Documents / Decisions / adr-3a2d5ee7bc84
Agent-instruction scaffolding as a self-contained module
Why agent-instruction scaffolding is a self-contained module that bypasses the daemon.
Context¶
docir is only useful to an AI coding agent if the agent knows it exists and how
to drive it (the read/write loop, the "never edit markdown by hand" rule). That
knowledge lived in a hand-written docs/AGENT_GUIDE.md that was not packaged
and had no install path — a user had to copy it into their assistant's
instruction file by hand. We want docir to install those instructions itself,
the way uipilot init does.
Two shapes an assistant reads instructions from cover the field: a Claude Code
skill (auto-loaded by its frontmatter description) and the cross-assistant
AGENTS.md convention. Everything else (Cursor, Copilot, Windsurf) either
reads AGENTS.md or is out of scope for now.
This operation is unlike every other docir command: it touches no documents, index, embeddings, or database — it copies a packaged template into the target tree. That raises two placement questions: which layer owns it, and whether it goes through the daemon/dispatcher write path.
Decision¶
Add a self-contained bounded-context module modules/agents (api.py +
CONTRACT.md + domain/application/infra), exposed as two CLI commands:
docir agent install [DIR] [--agent claude|agents ...] [--global]
docir agent update [DIR] [--agent claude|agents ...] [--global]
- Targets (
domain/targets.py):claude→.claude/skills/docir/SKILL.md(default; installable--globalunder~/) andagents→AGENTS.mdat the repo root (project-only; no global location). Unknown--agentnames are ignored;--globalof a non-global target is anAgentSetupError. - Single source of truth: one packaged template
(
infra/templates/skill.md) — the formerAGENT_GUIDE.mdplus skill frontmatter — is what the skill installs verbatim and whatAGENTS.mdembeds (frontmatter stripped) inside<!-- docir:start/end -->markers. - Idempotent + versioned: generated files carry a parseable
<!-- docir:vX -->stamp. A skill file is docir's entirely and is rewritten wholesale; anAGENTS.mdblock is replaced-not-duplicated and a foreignAGENTS.mdis never rewritten byupdate(only appended to when--agent agentsis asked for).updateauto-detects installed files and reportsvOLD → vNEW.
It does not go through the Dispatcher/daemon. The module owns no index/DB
state and does not participate in the shared unit-of-work (adr-d3e3616400bf), so the CLI
builds the service directly via agents.api.build_agent_service(version) and
runs it in-process — the same pattern as version and daemon serve. Routing a
filesystem copy through the socket, engine, and migrations would be actively
wrong. The module is therefore clean: it introduces no platform → agents
baseline edge and depends only on platform.errors.
Consequences¶
- Easier:
docir agent installis a one-command onboarding; the guide is now shipped in the wheel and versioned with docir, soupdaterefreshes it after an upgrade.AGENTS.mdcovers every non-Claude assistant. - Chosen cost: this is a full module (its own ADR,
CONTRACT.md, tach entries) for what is essentially file templating — deliberately, over a lighterplatformcapability, to keep a clean bounded context with a documented public contract. - Deviation from the "single command vocabulary" thesis:
agentcommands bypass the Dispatcher. This is scoped to operations with no index state and is noted in the moduleCONTRACT.md; it does not widen to document/tag/maintenance. - Scoped out: Cursor/Copilot/Windsurf-native files, and any environment
sniffing — targets are chosen explicitly by
--agent. Adding a native format later is a new entry inAGENT_TARGETSplus a render branch, nothing more. - Follow-up:
docs/AGENT_GUIDE.mdbecomes a thin pointer to the packaged template so the two never drift. - Amended by
adr-6ed847e02fe5. One clause above no longer holds:AGENTS.mddoes not embed the guide body. It carries the skill'sdescriptionand a link to the skill file, and selecting that target installs the skill it names. Everything else here — the module placement, the daemon bypass, the single packaged template, the version stamp — stands.