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
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ host CI --runs----> surface-status check --require conformant
```

- **Commands** are three skills the developer types. `/surface-plan` explores, interviews, has the plan drafted and writes it with its gates, has the blueprint drawn and cross-checked, then commits, pushes and opens a draft pull request. It then stays in the conversation and takes amendments there, as it does after a block during planning, since a reply that approves nothing needs no new launch. `/surface-execute` approves the drafted revision by being launched, then dispatches slices, gate runs, reviews and fixes. It puts a plan change proposal and the ceiling to the developer in the conversation: a refusal or a resumption goes on in the session, while an accepted change or an amendment goes to `/surface-plan`, since this command cannot load it and never writes the plan. `/surface-status` reports, runs the conformity check, and abandons a plan on confirmation.
- **Agents** are four roles of the chain, and one agent built into Claude Code. The `Plan` agent drafts the plan, in the form it chooses: a good plan is what it already does well, so the chain ships no definition for it and holds the plan only to the minimum it reads, the numbered acceptance criteria, the slices behind their markers, each enough for an agent that starts fresh, and the `gates` block ([ADR 0035](docs/adr/0035-plan-drafted-by-the-built-in-plan-agent.md)). It has no write tool: it returns the plan, and `/surface-plan` writes `plan.md` as returned. The extractor draws `blueprint.md` from `plan.md`, as long as the feature needs, since the developer reads all of it. A blueprint has a fixed frame and a free body: it opens with the idea, the acceptance criteria and the scope, and closes with the sensitive zones, while the extractor cuts the body by what the developer decides separately, and keeps the cut of the previous revision, so that a revision is read against the one before. No heading carries a number: a section is cited by its title, which an amendment does not move, where a number would shift and send a report to another section. Five aspects are gone through whatever the cut, the data schema, the boundaries, the sequences, the state machines and the algorithms, and one closing line names those the plan leaves alone, since silence must not read as "unchanged". A diagram is drawn where what it shows has a shape that prose flattens, with the existing elements the change attaches to. The checker looks for what the plan does and the blueprint does not show, never for a cut, a short section or a diagram that is not there. The executor carries out a slice or a fix. The reviewer judges the branch against the approved blueprint, writes `conformity.md` when it finds nothing, with the changed files of the critical zones, judges a break an executor suspects, and corrects that list of files when the script refuses it.
- **Agents** are four roles of the chain, and one agent built into Claude Code. The `Plan` agent drafts the plan, in the form it chooses: a good plan is what it already does well, so the chain ships no definition for it and holds the plan only to the minimum it reads, the numbered acceptance criteria, the slices behind their markers, each enough for an agent that starts fresh, and the `gates` block ([ADR 0035](docs/adr/0035-plan-drafted-by-the-built-in-plan-agent.md)). It has no write tool: it returns the plan, and `/surface-plan` writes `plan.md` as returned, but for two repairs it makes without a word to the developer, who never reads the plan: a return that arrived escaped, whose markers the script would not read, and a path of the machine. The extractor draws `blueprint.md` from `plan.md`, as long as the feature needs, since the developer reads all of it. A blueprint has a fixed frame and a free body: it opens with the idea, the acceptance criteria and the scope, and closes with the sensitive zones, while the extractor cuts the body by what the developer decides separately, and keeps the cut of the previous revision, so that a revision is read against the one before. No heading carries a number: a section is cited by its title, which an amendment does not move, where a number would shift and send a report to another section. Five aspects are gone through whatever the cut, the data schema, the boundaries, the sequences, the state machines and the algorithms, and one closing line names those the plan leaves alone, since silence must not read as "unchanged". A diagram is drawn where what it shows has a shape that prose flattens, with the existing elements the change attaches to. The checker looks for what the plan does and the blueprint does not show, never for a cut, a short section or a diagram that is not there. The executor carries out a slice or a fix. The reviewer judges the branch against the approved blueprint, writes `conformity.md` when it finds nothing, with the changed files of the critical zones, judges a break an executor suspects, and corrects that list of files when the script refuses it.
- **The state script** derives the state, refuses illegal steps, runs the gates, and answers the skills and the host's CI.
- **A plan folder**, `docs/plans/<date>-<slug>/` by default, holds the specs, the exploration, the interview, the plan, the blueprint, the reports (`checks/`, `reviews/`, `plan-changes/`, `gates/`), `conformity.md` and `journal.jsonl`. It is the whole state of a plan.
- **The installer** copies the chain into the host, updates it, and checks it for drift.
Expand Down
2 changes: 1 addition & 1 deletion skills/surface-plan/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ The conversation is the developer's, the plan folder is the repository's.
3. Folder. `<plans_dir>/<YYYY-MM-DD>-<slug>/`: `plans_dir` from `.claude/surface.json`, `docs/plans` when the file or the key is missing; today's date; a short slug of the need, another one if the folder exists. Write `specs.md`: the developer's text as given, or the content of the file their arguments name, verbatim, but for the paths of their machine: a path under a home directory becomes a neutral form such as `/path/to/...`, here and in every answer you write down, since the plan folder is committed and the repository's checks read it. Tell the developer what you replaced. When the specs are in another language than the documents', add their translation below the text, under a heading that says so. Then `surface-status record <plan> plan-opened --json`.
4. Exploration. Before any question, read what the feature touches, as far as the interview needs: the domain objects it extends, the closest precedent (the last thing added the same way), the decisions of step 1 that bear on the area, and what the code already settles of the need, so that no question is asked that the code answers. The path through the code, layer by layer, is left to the agent that drafts the plan. Write `exploration.md` from `${CLAUDE_SKILL_DIR}/templates/exploration.md`, with the findings of step 1, the gates of the plan among them (see "The gates").
5. Interview. List what the specs leave open, then ask (see "Asking a question"), in `interview.md` from `${CLAUDE_SKILL_DIR}/templates/interview.md`. Stop when what remains is a matter of implementation you can decide: it goes to the plan as an assumption. Then `surface-status record <plan> interview-closed --json`.
6. Plan. Launch a fresh `Plan` agent (see "The agents") and wait for its return: the plan, whole, since it has no write tool. Write that return to `plan.md` as it is: its design and its form are the agent's, and you rework neither. Every document you write in the plan folder must pass the gates of step 1, which read it too: a decision still to come or an option set aside is described, never cited by a record number that does not exist. Then check the minimum the chain reads, below, and nothing else. A return that is no plan, or a plan that fails this minimum: launch a fresh agent once more; then tell the developer and stop.
6. Plan. Launch a fresh `Plan` agent (see "The agents") and wait for its return: the plan, whole, since it has no write tool. Write that return to `plan.md` as it is: its design and its form are the agent's, and you rework neither. Two repairs are not a rework, and are not reported to the developer, who never reads the plan: when the markers arrived escaped, `&lt;!--` for `<!--`, the whole return did, and every entity is written as its character, once, since the script reads the markers as literal text; and a path of the machine becomes its path from the root of the repository, or a neutral form such as `/path/to/...` when it lies outside it. Every document you write in the plan folder must pass the gates of step 1, which read it too: a decision still to come or an option set aside is described, never cited by a record number that does not exist. Then check the minimum the chain reads, below, and nothing else. A return that is no plan, or a plan that fails this minimum: launch a fresh agent once more; then tell the developer and stop.
- The acceptance criteria, numbered, those of the specs and the interview.
- Each slice behind its `<!-- slice:N -->` marker, alone on its line, and enough for an agent that starts fresh. A slice number the journal has seen is never reused.
- The `gates` block, which holds the gates you found and no other, in their order: this one you correct yourself (see "The gates").
Expand Down
2 changes: 1 addition & 1 deletion skills/surface-plan/templates/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Revision <N>. Execution instructions for the agents, slice by slice, drafted from `specs.md`, `exploration.md` and `interview.md` of the plan folder, and from the code. Written in the language `exploration.md` names in its repository rules: translate the headings, keep the markers and the `gates` tag as they are. The plan stays alive: an executor that deviates amends it in the commit of its code.

The agent that drafts the plan designs it and lays it out as it sees fit: the decisions and the options set aside, the tests, the risks, the assumptions taken instead of a question go where they read best, each as long as the feature needs, none filled for its own sake. The chain reads three things only, which the plan holds under the three headings below, whatever stands around them. No code beyond a signature or a schema fragment. The plan is committed and the repository's checks read it: no path of a machine, and a decision still to come or an option set aside is described, never cited by a record number that does not exist. The agent has no write tool: it returns the plan whole as its answer, and nothing around it, and the command that launched it writes `plan.md`.
The agent that drafts the plan designs it and lays it out as it sees fit: the decisions and the options set aside, the tests, the risks, the assumptions taken instead of a question go where they read best, each as long as the feature needs, none filled for its own sake. The chain reads three things only, which the plan holds under the three headings below, whatever stands around them. No code beyond a signature or a schema fragment. The plan is committed and the repository's checks read it: every path is written from the root of the repository, never as a path of a machine, and a decision still to come or an option set aside is described, never cited by a record number that does not exist. The agent has no write tool: it returns the plan whole as its answer, and nothing around it, and the command that launched it writes `plan.md`.

When `plan.md` already exists, the agent drafts its next revision, which carries every amendment and every accepted plan change of `interview.md`. A slice that a `slice-done` line of `journal.jsonl` records is built: it stays, under its number. What is left to build goes into the slices that have not run, or into new ones.

Expand Down
7 changes: 7 additions & 0 deletions tests/prompts/test_surface_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,13 @@ def test_the_command_writes_the_plan_as_returned_and_checks_only_the_minimum() -
assert positions == sorted(positions)
assert "since it has no write tool" in plan
assert "its design and its form are the agent's, and you rework neither" in plan
# What the script or the repository's checks could not read is repaired, and never told.
assert "Two repairs are not a rework, and are not reported to the developer" in plan
assert "when the markers arrived escaped, `&lt;!--` for `<!--`, the whole return did" in plan
assert "every entity is written as its character, once" in plan
assert "a path of the machine becomes its path from the root of the repository" in plan
assert "or a neutral form such as `/path/to/...` when it lies outside it" in plan
assert "every path is written from the root of the repository" in _template("plan.md")
for held in (
"The acceptance criteria, numbered, those of the specs and the interview",
"Each slice behind its `<!-- slice:N -->` marker, alone on its line",
Expand Down
Loading