Status: draft v0.1 · 2026-07-27
A production Flutter project accumulated a complete autonomous feature-development setup inside
its .claude/ directory: orchestrator skills, a crew of agents, quality-gate hooks, operator
documentation, and a deterministic CLI. The system was designed and hardened iteratively (specs
and plans preserved in that repo under docs/superpowers/). It works: a Jira ticket goes in, a
Bitbucket PR comes out, one approval pause in between.
Artel extracts the project-agnostic core of that system into a Claude Code plugin so any project can install it from a GitHub marketplace repo.
- One-command install —
/plugin marketplace add <owner>/artel→/plugin install artel@artel, works in any repo regardless of language or toolchain. - Keep the workflow's shape — the pipeline stages, the spec trail
(
specs/.current/<TICKET>/), the single approval pause, the quality gates, resumability. - Genericize, don't fork — project-specific facts (ticket grammar, tracker, VCS host, verify commands, language conventions) become configuration, not edits to skill bodies.
- Porting the informational Flutter/Dart reference skills (the
flutter-*/dart-*how-to and conventions guides) — those seed the likbez companion plugin (separate repo). Workflow skills that merely carry a Flutter/wallet name (flutter-inner-loop,wallet-review,run-app,drive-app) are in artel's scope, in genericized form. - Windsurf mirroring (
move-to-windsurf/restore-from-windsurf) — tooling quirk of the source repo. - Building a hosted service or GUI. Artel is files in a plugin: skills, agents, hooks, docs.
The plugin follows the standard Claude Code plugin layout. Skills become namespaced commands
(/artel:feature-development), agents become invocable subagent types, hooks register via
hooks/hooks.json using ${CLAUDE_PLUGIN_ROOT} paths.
| Layer | Contents | Source of truth ported from |
|---|---|---|
| Entry points | feature-development (full pipeline), dev (lean loop) |
orchestrator skills |
| Stage skills | analysis, researcher, planner, tasklist, implementer, run-reviewer, qa, docs-update, validate, pr-description, pr-create, sync-phases, generate-idea, generate-vision, generate-tasklist, figma-analysis, inner-loop (← flutter-inner-loop), deep-review (← wallet-review), run-app, drive-app, change-digest, address-pr-comment |
orchestrator skills |
| Ops & utility skills | init-branch, merge-conflicts, add-automation, remove-automation, save-context, restore-context, issue-draft (← jira-issue-ru), agents-md-generator |
utility skills |
| Agents | analyst, figma-analyst, researcher, planner, task-planner, tasklist-writer, vision-writer, implementer, reviewer, qa, validator, tech-writer | .claude/agents/*.md |
| Hooks | session baseline, fast per-edit verify, stop gate (+ verify), sensitive guard | .claude/hooks/*.py |
| Contracts | autonomous-run contract, orchestrator-common, ticket-parsing rules, deviation protocol, path conventions | .claude/docs/, .claude/agents/docs/ |
| Operator docs | workflow guide, skills reference | .claude/docs/ |
Most workflow skills are orchestrators, not workers: they resolve ticket context, invoke the
matching agent via the Agent tool, and report results, never inlining the agent's work. A
self-declared subset of pure-procedure utility skills (e.g. sync-phases, generate-idea,
merge-conflicts) has no matching agent to invoke and runs its documented procedure inline
instead, keeping its procedural shape — each says so in its own body ("worker, not an
orchestrator"). For every skill that does dispatch an agent, this contract is the backbone of the
system and ports unchanged.
specs/.current/<TICKET>/ with ticket-wide artifacts at the top and phase-N/ subfolders for
phase-scoped ones; specs/.current/.active_ticket points at the in-flight ticket. This
convention is already project-agnostic and ports unchanged.
Everything project-specific becomes per-project configuration that the installed plugin reads
from the host repo (not from the plugin): a single .artel/config.json at the host repo
root. Full key reference and defaults: config.md.
| Hardcoded in source | Becomes config |
|---|---|
Ticket grammar AW-NNNN / AW-NNNN-P |
ticket.pattern (project key + phase-suffix rule) |
Jira via mcp__aiguard__jira_* |
tracker.adapter: jira-mcp | github-issues | none (+ tool prefix) |
Bitbucket via mcp__aiguard__bitbucket_* |
vcs.adapter: bitbucket-mcp | github-cli |
make verify, Dart MCP analyze/format |
verify.commands (list of shell commands), verify.fast (per-edit) |
| Russian PR descriptions / Jira comments | language.docs, language.pr |
| Figma design analysis | optional module, enabled only when design.figma: true |
specs/.current/ location |
specs.dir (default keeps specs/.current/) |
| Spec trail readable only on the machine that produced it | knowledge.adapter: kartoteka | none (+ base URL) |
Skills and agents reference config values instead of literals. When no config exists, the entry-point skills run a short one-time init interview and write the file.
The deterministic CLI.Decided 2026-08-07: Python rewrite ofverify+plan-checkunderscripts/;codegendropped (setup.commandscovers install/codegen) — see decision log.Config file name and shape.Decided 2026-08-01:.artel/config.jsonin the host repo, run state alongside it under.artel/run/— see decision log and config.md.Tracker adapters at v1.Decided 2026-08-01: v1 shipsnone+github-issues+jira-mcp— see decision log.Run-state and journal paths.Decided 2026-08-01:.artel/run/in the host repo — see decision log and autonomous-run.md.Figma analysis.Decided 2026-08-01: ships in v1, runtime-optional (skips silently when no Figma MCP is connected) — see decision log.
Work that is known, deliberate and not yet done. This section exists because docs/superpowers/
is gitignored — a follow-up recorded only in a spec's "Open questions" section leaves no
trace in the repository, and several had already gone invisible by the time they were collected
here (2026-09-06). Anything parked for later belongs here as well as in its spec. Resolved items
move to the decision log.
- Phase 6: the non-Dart end-to-end dry run. The last unchecked box in
porting-plan.md and the genericization proof — every run so far has been on
a Flutter host, which is the stack artel was ported from. The
tracker.adapter: "none"gate-0 defect fixed on 2026-09-05 is exactly the class of bug this catches, and it survived from 2026-08-08 because the run never happened. - Store mode is designed but unbuilt.
docs/superpowers/specs/2026-08-31-kartoteka-primary-specs-design.mdhas no plan file and no implementation, and it is the only spec in that state. As of 2026-09-06 nothing on kartoteka's side blocks it: every §1 and §2 item in kartoteka-requirements.md has shipped. Either plan it or record it as parked — it should not sit in neither state. - kartoteka 0.28.0's queue parameters are unused.
task_readytakesparent_idandtask_listtakesorder="created", both added for artel's stated needs. task-queue.md §3 still claims unscoped and releases a wrong-phase row back toready, and still recovers plan order by sorting client-side. Adopting them changes the claim protocol (a phase-scoped run must first learn its iteration'stask_id), so it wants its own design pass rather than an in-place edit. issue-draftcalibration and operator smoke test (from the 0.10.0 redesign, 2026-09-04). The template and its kartoteka consultation have never been exercised against real tickets; which kartoteka project and which tickets to sample is itself unresolved.deep-review's forecast constants are placeholders (from the 0.8.0 design, 2026-09-02). The0.5weight for unlisted reviewers is a guess, not a measurement, and whetherreview-forecastershould run onopusorsonnetwas deliberately started atopusto be lowered once the evidence rows read well. Both need real runs behind them.- No GitHub releases have been published. Fourteen tags through
v0.11.0, zero releases;bump-versionprints the reminder at the end of every release and it has never been acted on. Fine if deliberate — but then it should be a decision here rather than a standing omission. - The license holder is provisional. MIT under a personal name pending AdGuard's review of the plugin (decision of 2026-07-27, below). No record that the review has been requested.
- 2026-07-27 — Name:
artel. Chosen overslipway,greenlight,baton,ticket-to-pr. Short (matters for the/artel:<skill>prefix), the metaphor maps exactly onto the agent crew, distinctive and searchable. Repo and marketplace share the name — a single-plugin marketplace repo installable directly from GitHub. - 2026-07-27 — Skeleton-first. Repo scaffolded with manifest + docs before any porting, so further work happens inside the plugin repo itself.
- 2026-07-27 — License holder. MIT under "Dmitry Seloustev" deliberately for now; switches to the AdGuard legal entity only after the company reviews and approves the plugin. Don't flag or change it before then.
- 2026-08-01 — Full workflow scope (user-approved). Artel ports every workflow skill
and agent from the source inventory (source-inventory-workflow.md):
32 skills, 12 agents (incl.
figma-analyst), all 5 hooks, the contracts and operator docs. Previously skipped/deferred items are now in scope:init-branch,save-context/restore-context,add-automation/remove-automation,merge-conflicts,address-pr-comment,change-digest,agents-md-generator,figma-analysis,run-app,drive-app. Renames:flutter-inner-loop→inner-loop,wallet-review→deep-review,jira-issue-ru→issue-draft(output language from config). Toolchain-bound skills (run-app,drive-app,add/remove-automation) port as adapter-shaped skills whose concrete commands come from.artel/config.jsonand degrade gracefully (e.g. RUNTIME_OK recorded as skipped) when unconfigured. - 2026-08-01 — Companion plugin:
likbez(user-approved). The informational/reference skills (source-inventory-informational.md, 31 skills) become a second plugin named likbez (era-matched pair to "artel": the crew works, likbez educates it), seeded from the source project and extended later from the internet.rules/ast-index.mdgoes there too; orchestrator index-refresh steps in artel become an optional config hook. Separate repo; out of scope for this porting plan beyond this note. - 2026-08-01 — Config lives at
.artel/config.json(open question 2). A dot-directory in the host repo rather thanartel.config.jsonat the root or a section of the hostCLAUDE.md: it keeps the host root clean, keeps machine-written configuration out of a human-authored doc, and gives the plugin one natural home for host-writable run state (.artel/run/, open question 4) next to the config it belongs to..artel/config.jsonis committed;.artel/run/is not. Nothing host-writable is ever placed in the plugin install/cache directory. Schema and defaults: config.md. - 2026-08-01 — v1 tracker adapters:
none+github-issues+jira-mcp(open question 3).none(ticket text from a local idea file) andgithub-issues(via theghCLI) need no private MCP server, so artel is usable in any repo on day one;jira-mcpports over from the source system almost unchanged and keeps parity for Jira shops, with the server addressed through a configurabletracker.mcpToolPrefixinstead of a hardcoded tool name. - 2026-08-01 — Run state and journals live under
.artel/run/(open question 4). The source system'srun-state.json,run-journal.mdandopen-questions.mdmove from the spec trail into their own host-writable, gitignored tree next to.artel/config.json, keeping the spec trail underspecs.dirpurely human-readable documentation.<specs.dir>/.active_ticketstays where ticket-parsing.md already settled it — it is a phase pointer, not run bookkeeping. Schema and rules: autonomous-run.md. - 2026-08-01 — Agent contract references via
${CLAUDE_PLUGIN_ROOT}. Agent bodies address plugin-shipped contracts as${CLAUDE_PLUGIN_ROOT}/docs/<file>.md: agents execute with the host repo as working directory, and the plugin-root variable is Claude Code's documented way to address bundled plugin files. Docs among themselves keep relative links. - 2026-08-01 — Ported agents ship without a
tools:frontmatter restriction. With config-driven tracker/VCS/design adapters the required MCP tool names are unknowable at plugin-authoring time; the source project already hit this (its figma-analyst dropped its curated tool list because the list silently blocked MCP tools and ToolSearch). Agents inherit the full toolset; their prompts constrain behavior. - 2026-08-01 — Ported agents keep their source
model:frontmatter. Per-agent model choices (opus for design-heavy agents, sonnet for mechanical ones) are portable cost/capability tuning, not project specifics; each ported agent keeps its source value. Companion to thetools:-drop decision already logged. - 2026-08-01 — Ported skills drop
allowed-tools:frontmatter. Same rationale as the ported agents' droppedtools:: with config-driven tracker/VCS/design adapters, the required MCP tool names are unknowable at plugin-authoring time, and a curatedallowed-tools:list silently blocks MCP tools and ToolSearch. Skills keepmodel:andargument-hint:— those are portable and don't name adapter-specific tools. - 2026-08-01 — Introduced
specs.releasesconfig key (default"specs/releases").agents/qa.mdandagents/validator.mdkept aspecs/releases/<RELEASE_ID>literal, each with a caveat noting config.md had no dedicated key for it yet. Per the genericization strategy ("everything project-specific becomes configuration"), addedspecs.releasesto config.md'sspecssection — repo-relative, sibling tospecs.dirrather than nested inside it, since a release spans multiple tickets.agents/qa.md,agents/validator.md, and the portedskills/qa/skills/validatebodies now reference<specs.releases>instead of the literal. Where a project keeps its release-scope QA/validation artifacts is project-specific state, not workflow-structural, so it belongs in configuration alongsidespecs.dir, not hardcoded. - 2026-08-01 — Bitbucket
projectKey/repositorySlugderived at runtime, not a config key.pr-descriptionandpr-create(Phase 3, PR/digest skills) need a Bitbucket ServerprojectKey/repositorySlugpair to addressbitbucket-mcpPR-listing and PR-creation tools; the source skills hardcodedflutter/adguard-wallet, verified once by hand from.git/config. Unlikespecs.releases, this did not become a newdocs/config.mdkey: both values are mechanically derivable fromgit remote get-url origin(Bitbucket Server clone-URL shape.../<projectKey>/<repositorySlug>.git), mirroring the source's own verification method. The git remote is the single source of truth for where the repo actually lives — a config-file copy could drift from it after a repo move/rename/re-clone with nothing to catch the mismatch, whereas deriving it at call time never can.skills/pr-description,skills/pr-createderiveprojectKey/repositorySlugthis way instead of hardcoding them or adding a new config key. - 2026-08-01 — Restored the implementer's on-demand runtime-check hint (Task-3 follow-up,
resolved with the run-app port). The source
implementerskill carried a brief, optional debugging hint distinct from theRUNTIME_OKcompletion gate: mid-task, when a change's effect isn't obvious from tests alone, the agent could launch the app to look. The Phase-3 implementer port (Task 3) dropped it — Dart/Flutter-specific launcher mechanics, no generic launcher to point at yet. Now thatrun-appexists with a config-drivenruntime.run, restored as one sentence inskills/implementer/SKILL.md's Phase-1 dispatch prompt: "you may launch via the/artel:run-appskill flow to observe it; this is not the RUNTIME_OK gate." Not duplicated intoagents/implementer.md— source only ever carried the hint in the skill's dispatch prompt, and the agent already receives it whenever the skill invokes it; adding a second copy in the agent body would only risk drift between the two without adding capability. - 2026-08-01 — Context store lives at
.artel/context/(Task 7:save-context/restore-context). The source system mirrored session context — root docs plus the spec trail — into a user-level, cross-project store keyed by checkout basename (~/.claude/plugins/data/<tool>/<project>/), so it survived branch switches, working-tree wipes, and even a full reclone. Artel drops the user-level store: like.artel/run/, the context store is host-repo-local, host-writable, and gitignored —.artel/context/, sibling to.artel/run/under the.artel/footprint (see config.md). What gets mirrored narrows to match: only the parts of the source store that correspond to concepts artel's ported docs already define — rootCLAUDE.md/CHANGELOG.mdand the ticket-scoped spec trail (<specs.dir>/<TICKET_ID>/,<specs.dir>/.active_ticket— ticket-parsing.md's own vocabulary). Dropped: the source's curated mutable-docs/-subset mirror and its top-level loosespecs/-file mirror, both keyed to fixed source-project filenames (code_style_guide.md,conventions.md,mfa-locker.md, …) with no generic equivalent in artel's config-driven model. Trade-off accepted knowingly: unlike the global store,.artel/context/does not survive a full reclone or a wipe of the working tree — the same cost already accepted for.artel/run/, and consistent with "host-writable state only under.artel/" (porting-plan.md global constraints).docs/config.md's "Purpose and location" section now lists the new subtree alongside.artel/run/. - 2026-08-01 — Figma analysis ships in v1, runtime-optional (open question 5). Design
analysis is not deferred to a later release: it ships now, config-gated via
design.figma(docs/config.md, defaultfalse), and even when enabled it degrades gracefully rather than blocking — a silent skip when no Figma MCP is connected, per autonomous-run.md §13. Implemented by thefigma-analysisskill dispatching thefigma-analystagent. - 2026-08-01 — Gate 3.5 (plan-check) ports as spec, skips until Phase 5. The
feature-developmentport keeps the full bounce contract (bounce-line bookkeeping,MAX_PLAN_CHECK_BOUNCES = 2, exit-code semantics) but the deterministic checker itself —scripts/plan_check.py, the open-question-1 Python rewrite — ships in Phase 5. Until it exists the gate journalsPLAN_GROUNDED: skipped (plan-check ships in Phase 5)and proceeds: an unshipped tool degrades exactly like an unconfigured gate, and the absence of a recorded green means the gate re-runs for real once the tool lands. - 2026-08-01 — Runtime-surface detection becomes
runtime.surface. The source orchestrators skipped theRUNTIME_OKlaunch when no*.dartunderlib//packages/*/lib/changed — a Dart-specific test with no generic equivalent. Added an optionalruntime.surfaceconfig key (array of globs, config.md): set → the gate runs only when the run's diff matches, elseskipped (no runtime surface); absent withruntime.runconfigured → the gate always runs. The source behavior is expressible as["lib/**/*.dart", "packages/*/lib/**/*.dart"]. - 2026-08-01 —
setup.commandsconfig key; init-branch regains its post-branch step (Phase-3 Task-7 follow-up). The sourceinit-branchran dependency install/codegen after branch creation; the Phase-3 port dropped it for lack of a generic key. Addedsetup.commands(array,verify.commandsexecution rules, default[]) and restored the step ininit-branch— fatal on failure, silently skipped when empty. Thesetupskill's interview offers it as an optional extra. - 2026-08-01 — The init interview is
/artel:setup, its own skill. A self-declared procedural worker both entry points invoke when.artel/config.jsonis missing and users invoke manually to create or revise the config (revise pre-selects current values). Namedsetup, notinit: Claude Code's built-in/init(CLAUDE.md generator) already exists and artel's owninit-branchchains it, so anartel:initwould collide cognitively; kinship withsetup.commandsis a bonus. Writes one complete explicit config file (the config.md "filled example" shape) as its last step — an aborted interview writes nothing — and maintains the.gitignoreentries for.artel/run/and.artel/context/. - 2026-08-07 — Open question 1: Python rewrite of
verify+plan-check;codegendropped.scripts/plan_check.pywas effectively pre-decided — feature-development's Gate 3.5 already calls it.scripts/verify.pywraps the configuredverify.fast/verify.commandsin the source CLI's one-line JSON envelope (exit 0 clean / 1 findings / 2 environment error, kindsinvalid_argument/timeout/spawn_failed/command_not_found/internal_error), giving hooks one deterministic contract over arbitrary commands: exit 126/127, a spawn failure, or a timeout classify as environment errors, any other non-zero as findings. The sourcecodegenverb is not ported — its auto-detection rules (freezed/arb/API annotations) are inherently Dart-specific andsetup.commandscovers install/codegen generically.plan_check.pykeeps the source anchor grammar, genericizes the backticked-path rule (any repo-relative token with a/and a file extension, Dart root whitelist dropped), and resolves symbols viaast-indexwhen it is on PATH and usable (an unindexed or non-JSON response falls back togit grep -l -wper symbol), elsegit grep -l -w— no hard tool dependency. Phase-3 skill bodies keep running config commands directly;verify.pyis the hooks' engine, not a forced migration. - 2026-08-07 — Finding keys are digit-stripped output lines. The source stop gate diffed
structured
file:line:rulekeys; generic commands emit arbitrary text. A key = a non-empty output line of a red stage, ANSI-stripped, digit-stripped, whitespace-collapsed, deduped, prefixeds<stage-index>:, capped at 200 per stage — stable against shifting line numbers and timing noise ("Done in 3.2s") at the accepted cost of deduping same-rule-same-file findings. Keys are computed once inverify.py; hooks read them from the envelope. - 2026-08-07 —
verify.surfaceconfig key +{files}placeholder. Replaces the source hooks' hardcodedis_code_dartfilter: optional fnmatch globs (!-prefix excludes; only- excludes implies*; absent → every changed file counts), deliberately separate fromruntime.surface(runtime and lintable surfaces are different sets).verify.fast/verify.commandsentries may carry{files}, replaced with the space-joined shell-quoted changed paths; a blank--filesvalue is aninvalid_argument, never a silent widening to unscoped. - 2026-08-07 — Sensitive-paths policy: shipped defaults + wholesale host override. The
plugin ships
hooks/sensitive-paths.jsonwith three generic categories:secrets(full-gates),gate-config(full-gates — an armed run must not rewrite its own gates or the host's hook wiring),ci-cd(plan-gate). A host.artel/sensitive-paths.jsonreplaces the default wholesale — no merge semantics, the effective policy is always exactly one readable file; thesetupskill offers to scaffold it from the defaults. Broader nets (migrations, lockfiles, infra) were rejected: too many innocent matches across ecosystems. - 2026-08-07 — Hook state at
.artel/run/.hooks/; hooks inert until configured. Session baselines and verify-stop counters live in a dot-prefixed dir inside the already-gitignored run tree (can never collide with a ticket dir); the per-ticket stop-gate counter stays at.artel/run/<TICKET>/.stop-gate-blocksfor source parity. The verify-layer hooks return 0 immediately when.artel/config.jsondoes not exist, so an installed-but-unconfigured plugin leaves zero footprint in the host repo. - 2026-08-24 — The automation skills derive their file list with
-uall -z, and substitute{files}themselves. Both skills learn what the host's scaffold command touched by diffinggit status --porcelain, which turned out to be the wrong reading of that command for the job: it collapses a newly created directory into a single?? dir/entry, andtest_driver/is exactly what the source project's scaffold creates. That one entry then breaks three downstream steps —rm -frefuses a directory, so the verify-failure rollback leaves the tree dirty; the per-file pathsgit show --statprints never match it, so the commit's "exactly those paths" check cannot pass; and a{files}linter is handed a directory.-uallexpands it,-zdrops the quoting--porcelainapplies to names with spaces. Separately, both skills ranverify.fastas a raw config string while holding the very file scope the{files}token wants: with the source project's own config (which carries the token twice) the literal reached the shell and failed a correct scaffold. Skill bodies run config commands directly (the 2026-08-07 decision above), so they own the substitutionscripts/verify.pywould otherwise do for them. Also closed here:add-automationclaimed a failed apply left "nothing to roll back" because preflight had proved the tree clean, which says nothing about a command that fails part-way through writing; andremove-automationhad no default-branch guard despite committing and pushing — the one realistic way to meet the scaffold on the default branch is a branch merged withAUTOMATION_REMOVEDred, and the fix for that is a branch and a PR, not a push to main. - 2026-08-22 — The kartoteka mirror is a hook, not a step in each producing skill.
artel's spec trail is unreachable to any later agent asking why a decision was made; the
fix is to dual-write it into a kartoteka artifact store (that project's spec §11). The
mirror could have been an
artifact_putcall in each of the nine producing skills and agents, which is how the consuming project's spec described it and which would have madestageintentional and populatedauthor_agent. It is aPostToolUsehook instead, because an agent-executed side effect happens only when the agent remembers, and this one is invisible when skipped — nothing breaks, the trail is quietly incomplete. artel already puts must-not-be-skipped work in hooks. Two consequences accepted: a hook is a subprocess and cannot call MCP tools, so it posts to the HTTP facade of the same service; andPostToolUseinput does not carry the subagent name, soauthor_agentis always null (the field is nullable and self-reported on that side). Design: the consuming project's2026-08-22-artel-dual-write-design.md. - 2026-08-25 — The session router is a hook-injected skill, inert without a config.
skills/using-artelcopiessuperpowers:using-superpowers' mechanism: aSessionStarthook (startup|clear|compact) injects a routing table over every skill, because a skill description in a list is not a rule the model reliably follows, and an autonomous run compacts several times. It is injected only when.artel/config.jsonexists — the 2026-08-07 zero-footprint rule — since a repo artel does not run against has nothing to route, and the entry points already invoke/artel:setupthemselves. Capped at 10 KiB by test, carries<SUBAGENT-STOP>, lists no agents (every agent is behind a skill that resolves ticket context first), and states that an entry point is a complete process not to be wrapped in generic brainstorming or plan-writing skills. - 2026-08-25 — Conversational kartoteka access is two worker skills, read and write.
knowledge(search,related,index_status, a non-active ticket's artifact history) andtasks(list/add/done/block/release) rather than onekartoteka <verb>skill or procedure inlined in the router: the contracts are already organised as a read side and a write side, a fat router would charge every session for the queue rules, and a router executingtask_createfrom prose is a meta-skill doing a worker's job. Both refuse — no override — whenknowledge.adapteris notkartoteka, for the same reason the pipeline's row 3 exists: an undeclared adapter names no project. - 2026-08-25 —
tasks addgoes throughtasklist.mdand the existing mirror; no conversational claim;releaseis user-confirmed. Titles are the idempotency key, promotion finds siblings by theI<N> ·prefix, andtask_readyclaims intask_idorder, so a hand-built row sits outside all three;addappends a sectioned checkbox and runsscripts/tasklist_tasks.py+ create-onlytask_createexactly as the pipeline does.--rawcreatesbacklogonly and says artel will not claim it (areadybare row would be claimed by an implementer with no checkbox to flip). There is noclaimverb — claiming is the implementer's — andreleaseshows holder and age and asks first, becausedocs/task-queue.md§5 makes clearing a claim the user's call. - 2026-08-25 — The router names one other plugin:
ast-index. Cross-plugin routing stays out of scope in general (likbez is not routed), but the AST index is different in kind: artel already depends on its CLI —scripts/plan_check.pyresolves plan anchors through it — the index is project-agnostic, and the requests it answers ("find usages of X", "what is the project structure") are the ones a session in an artel repo makes before every stage. Sousing-artelcarries a code-navigation group pointing at/ast-index:ast-indexand/ast-index:initialize, gated on a fourth host-status line the hook computes withshutil.which('ast-index')— the CLI on PATH is the practical prerequisite the ast-index skill itself states, and whether the plugin's skill is installed is not knowable from a hook. With the CLI absent the group is inert, exactly like the kartoteka group withknowledge.adapter: none. The pipeline's own agents are unchanged: the source project's mandatory-ast-index rule went to likbez (2026-08-01) and stays there. - 2026-08-25 — Code navigation gets its own contract; the pipeline's agents do use the index
after all. The entry above closed with "the pipeline's own agents are unchanged." That was
right about the rule — the source project's mandatory-ast-index policy is likbez's and stays
there — and wrong about the mechanics. Six agents already reached for "the host's optional
code-symbol index," and every one of them pointed at
orchestrator-common.md§1, which documents only the post-implementation refresh hook. There was no contract anywhere for querying an index: no availability probe, no index-before-grep rule, no staleness handling, no command mapping. The pointer was dangling for the case it was cited for most. code-navigation.md is that contract, shaped like knowledge-consultation.md — numbered sections agents cite as§N. It is also the second sanctioned exception to the genericization rule, and a narrower one than the router: §2 namesast-indexand its commands because an index-first rule without commands is unactionable, every other section is tool-neutral, and a host with a different index answers §1 with its own probe and §2 with its own table. Agents keep saying "the host's optional code-symbol index" and cite the file; the concrete names live in exactly one place, which is what the test guards. Newly wired:reviewer(resolve a diff's symbols before judging it —changed,usages,implementations),tech-writer,vision-writer(grounding, and finding what to reuse),agents-md-generator(map/conventions/depsare built for repo-shape discovery) andmerge-conflictsPhase 3 (locating moved symbols). Deliberately not wired:qa,validator,task-plannerread artifacts, not code — andmerge-conflictsPhase 5 keeps its Grep, because a conflict marker is a string literal, which §3 makes the worked example of when not to use the index. - 2026-08-25 — Superpowers' subagent-driven development is not adopted as a mode; three of
its mechanics are. Assessed
superpowers:subagent-driven-development(v6.3.0) against the pipeline. Its premise — a controller session that dispatches a fresh worker per task and reviews each one — is artel's founding shape already (dev/feature-developmentloopSkill: implementer, which spawns the agent and keeps its id for resumes), and its compaction ledger is weaker thanrun-state.json+ the journal + the Stop hook. Its "rulings, not stalls" rule is the opposite of the deviation protocol's major → ask, and stays out. What it had and artel lacked: (1) the implementer returned "files changed (with the actual diff)" — every task's diff sat in the orchestrator's context for the rest of the run, the single biggest lever on how often a long run compacts; now the diff, evidence and reasoning go to.artel/run/<TICKET_ID>/reports/NNN-<slug>.mdand the completion is a short contract (autonomous-run.md §1, "Bulk stays in files"); (2) nothing forbade a worker from spawning its own reviewer, which superpowers observed duplicating the controller's review at full cost every time — the implementer and reviewer agents now carry a no-subagent rule; (3) no review between tasks inside a phase, so a misread acceptance criterion could be built on by every later task before the phase review saw it — that became the opt-in gate below. Not borrowed: batching same-shape tasks (conflicts with one claim per task on the queue), per-dispatch model tiering (a cost knob, not a workflow change; the agents keep their frontmattermodel:), and the brief-file extraction (the agent reads its task from the tasklist it already owns). - 2026-08-25 — Per-task review is
review.perTask, off by default, one fix round, no re-review, and it feeds the phase review rather than replacing it. The gate reuses every existing shape: findings land under## Code Review Fixesin the phase-aware tasklist (the section the phase review, the implementer's fix-list rule and theREVIEW_OKvalidator already understand), the fix round is a normal fix-list implementer dispatch counted towardcounters.correction_rounds, and whatever the round leaves unchecked is the phase review's from there — so there is no second loop, no second cap artifact and no per-task re-review (MAX_TASK_REVIEW_ROUNDS = 1). Off by default because it is one reviewer seat per task andtask-planneris told to make tasks small. Two mechanics it needed that did not exist: the implementer does not commit, so a task has noBASE..HEAD—scripts/review_package.pysnapshots the working tree through a throwaway index (read-tree --empty,add -A,write-treeunderGIT_INDEX_FILE), so the real index and the checkpoint's explicit staging are never touched, and writes the diff package to a file the reviewer reads; and therevieweragent gained a task mode that grades one task's diff against that task's own acceptance criteria, writesNNN-<slug>-review.mdbeside the implementer's report, and deliberately does not writereview.md, bump**Review round:**, run the lenses or writefindings.json— those are the phase review's, and a per-task gate that touched them would corrupt its counters. - 2026-08-26 — OpenCode becomes the second host, via a generator — canonical files stay
Claude-flavored.
scripts/build_opencode.pyderives an OpenCode install (artel--prefixed skills/agents/commands, a host glossary prepended to every generated body,${CLAUDE_PLUGIN_ROOT}baked to the install root) from the canonicalskills/andagents/; a TypeScript bridge (opencode/plugin/artel.ts) adapts OpenCode's plugin events onto the existing Python hook stdin/stdout contracts; a bridge-agnosticscripts/install-opencode.shcopies the plugin to~/.config/opencode/artel/and the generated artifacts into OpenCode's flat discovery directories. Claude Code is preserved by construction: the generator never writes outside--out, and the hook layer is untouched. Known softenings, documented in docs/opencode.md: the Stop gate becomes an idle re-prompt (OpenCode has no blocking Stop hook),SendMessageresume-by-id becomes a fresh Task dispatch, and per-agent model tiers are dropped (subagents inherit the caller's model). - 2026-08-28 —
remove-automationverifies the removal from git alone: an add-commit cross-check and a directory sweep. The skill learned what the host'sruntime.scaffold.removechanged fromgit statusand took the command's exit0as proof the scaffold was gone — but git lists files, never directories, and a host command that deletes the entrypoint without its directory (or whosermdirfails on an ignored file an editor or Finder dropped there) leaves the directory on disk under a cleangit status, with a removal commit that still contains "exactly those paths". Two generic checks close this without the plugin learning any host artifact:add-automationalways commits under one fixed subject, so the paths it added are recoverable from git and each must be gone (a survivor byte-identical to the scaffold's version is deleted by the skill itself — a plainrm, nevergit rm, so step 5'sgit addof the path still resolves — anything modified since is stop-and-report); and a directory whose every tracked file is among this run's deletions is the scaffold's own, removed when empty or holding only git-ignored entries and left with a stop on untracked work. Rejected: making the host command responsible (the wallet's script already doesrmdir, and the failure is invisible from the host's exit code), and a second run ofruntime.scaffold.removeas the completeness check (a hostpub getper run, and an idempotent no-op proves nothing).drive-app'sdrive-observation.mdstays: it is spec-trail evidence, likerun-app'sobservation.md. - 2026-09-02 —
deep-reviewis one reviewer pass plus a kartoteka-grounded forecast, in one file. The dual-review flow — two standalonereviewerdispatches, a mergedreview-summary.mdwith a QA plan, then plan mode — bought independence, not a second checklist, and produced three files nothing else consumed. Replaced by onereviewerdispatch and a newreview-forecasteragent that groups the diff into change units, finds precedents for each in kartoteka'spr/review_threaddocuments, classifies how the reviewer reacted, computes a pass percentage and drafts fixes under a threshold — all into<specs.dir>/<TICKET_ID>/deep-review.md, which replacesreview-summary.mdin the mirror set. Rejected: a precedent pass insidereviewer's standalone mode (the agent serves the per-task and phase gates too, and a fourth mode to protect them is more surface than a new agent) and doing the forecast in the skill body (orchestrators, not workers). The reviewer's own report becomes run-state evidence under.artel/run/<TICKET_ID>/reports/, which also closes the store-mode draft's open question aboutreview-claude.md/review-second.md. Design:docs/superpowers/specs/2026-09-02-deep-review-forecast-design.md(local —docs/superpowers/is untracked). - 2026-09-02 — The forecast has its own lookup budget and always writes its mode.
review-forecast.md §3 allows
search_knowledge×16 andrelated×4 per run — the first deliberate deviation from knowledge-consultation.md §3's four searches, because a forecast needs about one search per change unit and the unit count is bounded by the diff, not by curiosity. And §1's mode line is written even when the adapter isnone, deviating from consultation §4's no-record rule: the forecast table is a fixed part of a document the person asked for, and a table of dashes with no reason is the ambiguity §4 exists to prevent. The number is a Laplace-smoothed rate over judged precedents (§5), shown with its evidence, a dash at zero precedents — a grounded prior, not a model; kartoteka holds no "author changed the code" signal, so a fix request is judged from the thread text. Threshold 70 over the proposed 60 (review.forecast.threshold): with two to five precedents the estimate moves ten or fifteen points on one reclassified thread. The reviewer roster isreview.forecast.reviewersbecause it changes; unlisted participants weigh 0.5, a placeholder to revisit against real outcomes. - 2026-09-02 — Applying a forecast's fixes goes through
## Code Review Fixes, not plan mode. After writing the file the skill asks which set to apply and appends the chosen task blocks to the ticket-widetasklist.md, thenSkill: implementeronce per task andverify.commandsonce — the pipeline's own review-fix path (task-queue.md §6: file-scan work, never mirrored). No re-review loop; re-running the skill refreshes the forecast. - 2026-09-04 —
knowledge.projectnames the kartoteka namespace, with no default. kartoteka 0.31.0 namespaces its store and index by project so one daemon can serve several repositories out of one database, and it refuses any write —POST /api/artifacts,task_create,task_ready— andrelated()that name none. artel carries the name asknowledge.projectbesideadapterandbaseUrl, the block kartoteka's own integration note suggested, and puts it on every call: required where kartoteka requires it and as a scope on the reads (search_knowledge,index_status,task_list,artifact_list), which is what retires the six places where the docs justified the gate by kartoteka serving one project only. No default, deliberately: kartoteka removed its own because a guessed project appends to another project's deliberation trail, and a slug derived from the directory name would be exactly such a guess. A missing or malformed value is a configuration error resolved before the gating tables rather than as a fifth row — it is not a capability question, the tables are pinned at four rows, anddeep-reviewalready handles a bad adapter value the same way — with one record line spelled identically in all three contracts. The unscopedindex_status()that every consultation already opens with doubles as the registration check, because a scoped read for an unregistered project answers with silent zeros. Rejected: a fallback toticket.projectKeylower-cased (a Jira key is not a namespace, and two repos sharing one is the case the namespace exists for), and carrying the project inscripts/tasklist_tasks.py's output (the script contacts nothing and the orchestrator reads the config for the gate anyway). Released as 0.9.0 — a breaking config change, minor pre-1.0 — in step with kartoteka's coordinated release. - 2026-09-04 —
issue-draftdrafts against a template, consults kartoteka, and asks once before it writes. A reading of the corporate Slack↔Jira bridge (slackjira-service) found its issue text comes from one structured-output call whose prompt is, item for item, the generation rules the skill has carried since thejira-issue-ruport; there was no prompt to adopt, and its Slack-side context handling and AI routing step were not adopted — input is text or a local file, and no issue type is inferred. What carries over is the principle that a draft is always produced and its gaps are listed rather than guessed. The skill now renders one universal, self-describing template (each section's<!-- required|optional … -->comment is its rule; a host overrides the whole file at.artel/templates/issue-draft.md, presence being the switch, as with.artel/sensitive-paths.json), consults kartoteka under the read-side contract to close gaps before anyone is asked — quoted and attributed,⚠ NON-CURRENTclosing nothing, with two declared deviations (a tool error stops the consultation, not the draft; nothing is recorded under<specs.dir>) — and then asks the four highest-ranked remaining gaps in oneAskUserQuestionround, listing what it did not ask or was not told under Missing Details. Slack-export input and the multiple-suggestions mode go;language.prstays the output language and the dialect stays adapter-driven. Rejected: a drafting agent (the question round would cross the agent boundary twice, and utility skills stay procedural workers), per-type templates (they need exactly the type inference the rules forbid), and an iterative interview (one interruption per invocation). Released as 0.10.0 — a capability added and two behaviours removed, minor pre-1.0. - 2026-09-05 —
knowledge.tokenEnvnames the kartoteka credential; the config never holds it. kartoteka 0.32.0 (E3 phase 1) puts a bearer token on the whole port once[auth] enabled = true— every hosted daemon — and asked artel for three things: a config key naming the variable, theAuthorizationheader on the mirror hook, and--headeron the MCP registration. The key carries a variable's name because.artel/config.jsonis committed team configuration and a token is one machine's credential. A named variable that is unset sends the request unauthenticated rather than loggingmisconfigured: one committed config then serves a laptop against a loopback daemon with auth off and a host against a hosted one, and the daemon — not the hook — decides whether a token was needed. So a401is classified by what the hook had: a token sent and refused is areject(permanent until the operator acts, like every other contract refusal), none sent ismisconfigurednaming the empty key or the unset variable. The default is empty, inert like every other optional key, over"KARTOTEKA_TOKEN": an explicit log line beats a silent convention, and the setup interview suggests the conventional name anyway. The MCP session's token stays host wiring, not artel config — there is still no MCP URL in the config — because both clients expand an environment variable inside headers (${VAR}in.mcp.json,{env:VAR}inopencode.json), so one export feeds the hook and the session. Rejected: a file path to the token (a path in committed config points at a secret on every machine), and folding the token status into the adapter line of the host status (a separate line, presence only, never the value). Two guards came out of review: a value with control characters ismisconfiguredby name before any header exists —http.clientwould otherwise refuse the header with an error that echoes it, the one path on which the token could have reached the log — and a token is never sent over plaintexthttp://off loopback, since kartoteka refuses such a bind without TLS and the only thing at that address is a proxy's upstream port. The hook's proxy bypass stays, its promise restated as "the document goes tobaseUrland nowhere else", and the 2-second timeout stays until a hosted daemon shows it short. Released as 0.11.0 — an additive key with an inert default, minor pre-1.0; no coordinated release this time, since kartoteka's defaults are byte-identical to 0.31.0. - 2026-09-05 — Pipeline gate 0 invokes
generate-ideaunder every tracker adapter. Resolves the reader-test finding of 2026-08-08 (porting-plan.md, Phase 6): gate 0 previously rangenerate-ideaonly whentracker.adapterwas not"none", and under"none"merely relied on a$1description file or a pre-existingidea.md. Nothing in the pipeline ever wroteidea.mdon that path, and gate 2 (generate-vision) hard-requires it — so an untracked run died at the vision gate withError: idea file not found, whether or not a description file was passed. Gate 0 now delegates togenerate-ideaunconditionally, passing$0 $1. No branch is needed in the orchestrator because the skill already branches ontracker.adapteritself: its"none"path reads the description file, or runs the same input gateanalysisdoes when there is none, and its pipeline-invocation preflight skips silently whenidea.mdexists, so resume semantics are unchanged. Rejected: teaching gate 2 to tolerate a missing idea (the idea is a real input to the vision, not ceremony), and leaving the seeding to the operator (the workaround testing-flutter.md carried — a pipeline that cannot start itself from its own documented arguments is a defect, not a runbook step). This restores the"none"adapter as a first-class path, which is what makes artel usable in a repo with no tracker on day one.