ADR 0024: Setup is a command, not a skill
Consolidated into ADR 0036 (unify init + setup under
discern setup). The command-not-a-skill decision and the[meta].bootstrappedmarker live on there. Kept for history.
Status: accepted
Amended by ADR 0036: the
setupcommand was folded into a singlediscern setup(withsetup), and the pre-setup "nudge, not gate" below became a hard redirect for the work verbs. The command-not-a-skill decision and the[meta].bootstrappedmarker recorded here still stand.
Context
§The bundled setup skill seeded a fresh install from the project brief — drafting the docs tree, the orientation docs, the design principles and guidance, and proposing the [capabilities] fills. It shipped as a materialized skill: discern refresh copied templates/skills/setup/ (instructions plus a skel/ doc tree) into every project's .claude/skills/, where its description loaded into the agent's context for the life of the project.
Delivering a one-shot as a permanent skill has two costs:
- Context pollution. Bootstrap runs exactly once, but its skill description sat in every agent session forever — a recurring context cost for a task that is done after the first day.
- No completion signal. Nothing recorded that a project had been bootstrapped, so neither the agent nor the harness could tell "freshly installed, seed me" from "seeded months ago." The skill lingered identically in both states.
The unlock: to the agent, a SKILL.md it reads and a CLI command's stdout it reads are the same thing — instructions. discern never authored anything during setup; the agent already in the loop did. So the instructions can be delivered by a command instead of a file, with no loss of capability, and the first call doubles as a smoke-test that the binary is on PATH (the agent will lean on it constantly thereafter).
Decision
§Replace the setup skill with a discern setup command pair, recorded by a [meta].bootstrapped marker.
discern setuplays the doc skeletons — only when the project has none (an existingdocs/is never touched) — substituting the project name, then prints the setup instructions (templates/setup/instructions.md) for the agent to act on. The deterministic scaffolding is discern's job; all authoring stays with the agent.discern setup donevalidates that no skeleton markers remain (the<!-- setup fills this -->sentinels and the EXAMPLE principle), then records[meta].bootstrapped = true. It is also the manual-setup escape hatch: run it after wiring the config by hand to silence the reminder.--forcerecords despite leftovers.- The marker drives two behaviours. Until it is set, the engine work verbs print a one-line "not set up yet" reminder to stderr. Once set, the reminder retires and
discern setuphides from--help(it stays callable with--forcefor a deliberate re-seed). - Nudge, not gate. Other commands are never blocked pre-setup. Nothing is unsafe before setup — an unconfigured gate passes trivially (the "green gate you grow into") — and a hard block would punish the manual-config path and the just-run-my-tests path. The reminder is suppressed in
--jsonand on the setup/config verbs (setup, upgrade, doctor, preset, config), so it never spams a recipe'sconfigreads. - Skeletons stay binary-embedded under
templates/setup/skel/, laid by discern rather than copied by the agent — strictly more robust than the old "agent copies the materialized setup-skill skeleton" step it replaces. - Schema 6→7 migration. Prunes the stale materialized setup-skill copy (now a foreign directory
materializeSkillswould otherwise leave in place forever) and back-fills[meta].bootstrapped = truefor an already-configured install (capabilities wired or adocs/tree present), so an upgrade never nags a project that is effectively done. A bare install gets no marker — absent ≡ not set up everywhere.
Consequences
§templates/skills/setup/is gone. Its instructions live attemplates/setup/instructions.mdand its skeletons attemplates/setup/skel/. The bundled skill set is nowdocument-subsystem,handoff-worktree,write-adr.SCHEMA_VERSIONis 7; the migration chain gains a contiguous 6→7 step. The[meta].bootstrappedmarker is tracked indiscern.toml, so a fresh clone of a bootstrapped project correctly reads as done (you set up once per project, not once per clone).- The
setupoutro points atdiscern setup(recommended) or manualdiscern.tomlediting. The two sibling skills that referenced the old skill (write-adr,document-subsystem) now name the command, as do the gate, docs, and doctor pointers. - Discoverability moved from skill-trigger heuristics to three always-present signals: the init outro, the per-command nudge, and the printed instructions — more reliable for a fresh agent session (one that did not run
setup) than a skill that may or may not trigger. - Historical ADRs keep their point-in-time references to the
discern setupskill; this ADR is the record of the change.
Alternatives considered
§- Keep the skill, just add a completion marker. Rejected: the recurring context cost of a one-shot skill is the core problem; a marker alone does not remove it.
- Hard-block other commands until bootstrapped. Rejected: hostile, and it contradicts the "green gate you grow into" stance — it would wall the manual-config user and anyone who just wants to run their tests. A reminder achieves the discoverability goal without the cost;
doctorespecially must never be blocked, since it is what you run to debug a broken install. - A conditional "not set up" banner compiled into
AGENTS.md. Rejected:AGENTS.mdis tracked, so a marker-conditional banner would churn the committed file on every setup. The stderr nudge carries no git cost. - Always lay skeletons (even over existing docs), or never lay them. Rejected both: always-lay imposes the skeleton structure on a project that already has its own docs; never-lay reintroduces the fragile "agent copies files" step. Laying only when
docs/is absent is the seamless middle, and it honours the rule that discern never disturbs what the user already has.
This builds on the materialized-skills model from ADR 0020: setup simply stops being one of those skills and becomes a first-class command, which also frees it from the [features].skills toggle (setup should work even with skills off).