Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions docs/agents-spec-driven-development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Agent Spec-Driven Development

This is the end-to-end path for creating a spec-driven feature with council.

## Create a feature

1. Start from the target repository root and make sure `.specify` exists. For
personal-stack it is committed; for other projects, install the kit with:

```bash
curl -fsSL -H "Authorization: Bearer ${KB_BEARER_TOKEN}" \
"${KB_URL}/install.sh" | AGENT_KIT_PROJECT_ROOT="$PWD" \
bash -s -- --agent all --scope project
```

2. Create or refine the feature spec with the dot-form Spec Kit commands:
`/speckit.specify`, `/speckit.clarify`, `/speckit.plan`,
`/speckit.tasks`, and `/speckit.analyze`. User-scope installs also work; if
`.specify` is absent, `/speckit.specify` scaffolds a minimal project runtime
before creating the first spec.

3. Run council planning against the spec or a brief:

```bash
python3 platform/agents/council/council.py plan \
--brief specs/001-feature/spec.md \
--spec-dir specs/001-feature
```

`--brief` may also be `-` for stdin or a free-text string. Without
`--spec-dir`, council allocates the next `specs/NNN-slug` directory and
writes `spec.md`, `plan.md`, and generated `tasks.md` under the run
directory.

4. Review `consolidated_plan.md`, `tasks.json`, and
`specs/NNN-slug/tasks.md` in the run directory. `tasks.json` is canonical;
`tasks.md` is generated from it and must keep an exact JSON-block bijection.
If a human changes task data in `tasks.md`, reconcile by regenerating with
`council plan --run <run> --brief <run>/brief.md` or by making the matching
`tasks.json` change before fan-out.

5. Execute the approved DAG:

```bash
python3 platform/agents/council/council.py fanout --run .council/runs/<id>
```

Fan-out commits `specs/NNN-slug` onto the integration branch before workers
are spawned. Workers then run in isolated worktrees and the orchestrator
merges successful task commits onto `council/<run>/integration` for review.

## Gates and scope

The analyze gate hard-fails before execution when
`.specify/memory/constitution.md` is missing or placeholder-like, when
`tasks.md` is missing beside `tasks.json`, or when `tasks.md` no longer
round-trips to the canonical task JSON. Constitution text is injected into
planner, critic, reviser, and consolidator prompts only; worker and verifier
prompts do not receive it.

The MCP ConfigMap is explicitly unchanged by SDD. Do not edit
`platform/cluster/flux/apps/agents/mcp/agents-mcp-servers-configmap.yaml` for
Spec Kit command, scaffold, or council task changes.

The commit-capture and stop-digest hooks are not path-suppressible. The edit
recall hook has an allowlist, but commit capture and stop digest can only be
disabled for a session through the global hook controls such as
`KB_AUTO_MCP_DISABLED=1`.

## Upstream pin and upgrades

The vendored scaffold is tracked in
`platform/agents/kit/spec-kit-source.lock`: upstream
`https://github.com/github/spec-kit`, tag `v0.9.5`, with the resolved commit
currently recorded as `UNRESOLVED-DNS-BLOCKED`. The agent-kit owner must replace
that placeholder with the real tag commit before merge.

Spec Kit self-upgrades are human-only and off-runtime. Do not run them from
installer, runner, hook, or council execution paths. The owner workflow is:

1. Install or upgrade `specify-cli` pinned to the candidate upstream tag.
2. Run `specify self upgrade --dry-run` outside runtime installs and compare
the scaffold changes.
3. Resolve the candidate tag commit and update `spec-kit-source.lock`.
4. Copy only the approved `.specify` scaffold paths into
`platform/agents/kit/templates/repo/.specify/`.
5. Keep `.specify/memory/constitution.md` as the committed personal-stack file;
do not replace it with the generic upstream template.
25 changes: 24 additions & 1 deletion platform/agents/council/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ python3 platform/agents/council/council.py --self-test
`plan` prints the run dir on stdout. Inspect `consolidated_plan.md` and
`tasks.json` there.

Spec-driven runs also materialize Spec Kit artifacts in the run directory:
`specs/NNN-slug/spec.md`, `plan.md`, and `tasks.md`. The slug is derived from
the first line of the brief; pass `--slug some-name` to choose the slug while
keeping the next available `NNN` number. `--brief` accepts a file, `-` for
stdin, or free text when the argument is not an existing path.

`tasks.json` is canonical. `tasks.md` is generated from it and includes one
JSON block per task between council markers so it can round-trip exactly. Human
edits to prose around the generated blocks are fine; changing task data requires
editing the marked JSON blocks and regenerating or re-running `council plan
--run <run> --brief <run>/brief.md` so `tasks.md` and `tasks.json` match.

The analyze gate is a HARD-FAIL checkpoint before fan-out. It requires a
non-placeholder `.specify/memory/constitution.md`, requires `tasks.md` beside
`tasks.json`, and enforces a bijection between the canonical task JSON and the
marked task blocks in `tasks.md`. `plan`, `fanout`, and `fleet` all run this
gate before execution.

```bash
# stages 5-6: execute the task DAG with cheap workers, verify, reconcile
python3 platform/agents/council/council.py fanout --run .council/runs/<id>
Expand All @@ -53,7 +71,9 @@ verifier (`claude:sonnet`) checks the diff against the objective. Worker commits
are merged onto a `council/<run>/integration` branch in dependency order;
conflicts are left out and reported. **Nothing is merged into your branch** —
`fanout` prints the integration branch name for review. See `report.md` in the
run dir.
run dir. Before worker fan-out starts, the run's `specs/NNN-slug` directory is
committed onto the integration branch so every worker sees the same committed
spec, plan, and task artifacts.

```bash
# ad-hoc, engine-agnostic worker pool over any tasks.json (no plan phase)
Expand Down Expand Up @@ -136,6 +156,9 @@ state.json # resume marker
- Sub-invocations run with `KB_AUTO_MCP_DISABLED=1` so council's internal prompts
never pollute the knowledge base.
- Codex planning/critique uses `model_reasoning_effort=high`, read-only sandbox.
- The project constitution is injected only into reasoning roles: planner,
critic, reviser, and consolidator. Worker and verifier prompts do not receive
the constitution block.
- Both `claude` and `codex` CLIs must be installed and authenticated.
- We don't bundle a full JSON-Schema validator; `validate_tasks` checks the
fields fan-out relies on and runs a topological sort (cycle / unknown-dep
Expand Down
37 changes: 37 additions & 0 deletions platform/agents/kit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,43 @@ repo-local `.claude` and `.codex` directories instead of user config homes.
See [PORTABILITY.md](PORTABILITY.md) for uninstall, backup/export, restore, and
compatibility-matrix details.

## Spec-driven development scaffold

The kit vendors the GitHub Spec Kit runtime under
`templates/repo/.specify`. The checked-in scaffold includes templates plus the
Bash helper scripts under `.specify/scripts/bash`; those scripts are executable
in the template tree and are installed with mode `0755`.

`render-agent-kit.py` treats `.specify` as a project seed, not as generated
client state:

- The personal-stack repo-root `.specify` tree is committed. Its scripts and
templates mirror the vendored scaffold; its memory directory carries the real
project constitution.
- The repo-local `.specify/memory/constitution.md` is committed and hand-edited.
It is the personal-stack constitution. Do not render over it; edit that file
directly when governance changes.
- The generic `.specify/templates/constitution-template.md` remains a scaffold
template for future projects.
- Project-scope installs seed `.specify` under `AGENT_KIT_PROJECT_ROOT` or
`$PWD`. The constitution is written only when absent; the vendored scripts and
templates are refreshed from the installer bundle.
- `.specify` is intentionally not tracked for uninstall. `install.sh
--uninstall` removes agent client files, but never removes a project's Spec
Kit scaffold or specs.
- Runner workspaces are also seeded from the same scaffold. The agent runner
image copies `templates/repo/.specify` to `/opt/agent-kit/sdd`, and the
runner entrypoint seeds workspace repositories from `AGENT_KIT_SDD_SOURCE`.

Spec Kit commands are dot-form commands: `/speckit.specify`,
`/speckit.clarify`, `/speckit.plan`, `/speckit.tasks`, `/speckit.analyze`,
`/speckit.implement`, `/speckit.checklist`, `/speckit.constitution`, and
`/speckit.taskstoissues`. They ship through `install.sh` for both `--scope user`
and `--scope project`. Claude receives slash-command Markdown under
`commands/speckit.*.md`; Codex receives matching `speckit-*` skills. The
user-scope `/speckit.specify` flow bootstraps a minimal `.specify` tree in the
current project when it is absent and does not overwrite existing Spec Kit files.

Run the read-only doctor when debugging runner memory, MCP, or installer
drift:

Expand Down