@stdd/plugin 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +9 -0
- package/.codex-plugin/plugin.json +21 -0
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/extensions/stdd.mjs +77 -0
- package/hooks/claude-hooks.json +28 -0
- package/hooks/codex-hooks.json +28 -0
- package/package.json +38 -0
- package/runtime/adapters/README.md +158 -0
- package/runtime/cli/check.mjs +555 -0
- package/runtime/cli/ci.mjs +190 -0
- package/runtime/cli/claude-hooks.mjs +689 -0
- package/runtime/cli/config.mjs +27 -0
- package/runtime/cli/evidence.mjs +249 -0
- package/runtime/cli/generated-files.mjs +1693 -0
- package/runtime/cli/held-fs.mjs +415 -0
- package/runtime/cli/init.mjs +883 -0
- package/runtime/cli/ledger.mjs +1470 -0
- package/runtime/cli/lib.mjs +909 -0
- package/runtime/cli/path-bytes.mjs +83 -0
- package/runtime/cli/policy.mjs +112 -0
- package/runtime/cli/recorders.mjs +188 -0
- package/runtime/cli/review-fs.mjs +825 -0
- package/runtime/cli/review.mjs +1065 -0
- package/runtime/cli/runtime.mjs +32 -0
- package/runtime/cli/scope.mjs +185 -0
- package/runtime/cli/snapshot.mjs +897 -0
- package/runtime/cli/state-validation.mjs +168 -0
- package/runtime/cli/status.mjs +580 -0
- package/runtime/cli/stdd.mjs +536 -0
- package/runtime/cli/worker-fs.mjs +971 -0
- package/runtime/cli/worker-metadata.mjs +139 -0
- package/runtime/cli/worker.mjs +779 -0
- package/runtime/method/README.md +634 -0
- package/runtime/method/reference-commands.md +147 -0
- package/runtime/method/reference-generated-state.md +151 -0
- package/runtime/method/reference-integration.md +233 -0
- package/runtime/package.json +65 -0
- package/runtime/playbooks/brainstorming.md +46 -0
- package/runtime/playbooks/debugging.md +36 -0
- package/runtime/playbooks/delegate-slice.md +129 -0
- package/runtime/playbooks/finish-change.md +46 -0
- package/runtime/playbooks/implement.md +26 -0
- package/runtime/playbooks/investigation.md +33 -0
- package/runtime/playbooks/managed-playbooks.json +14 -0
- package/runtime/playbooks/planning.md +177 -0
- package/runtime/playbooks/pr-green.md +50 -0
- package/runtime/playbooks/start-change.md +37 -0
- package/runtime/playbooks/worktrees.md +45 -0
- package/runtime/prebuilds/stdd-fs/darwin-arm64/stdd-fs +0 -0
- package/runtime/prebuilds/stdd-fs/darwin-x64/stdd-fs +0 -0
- package/runtime/prebuilds/stdd-fs/linux-arm64/stdd-fs +0 -0
- package/runtime/prebuilds/stdd-fs/linux-x64/stdd-fs +0 -0
- package/runtime/prebuilds/stdd-fs/manifest.json +47 -0
- package/runtime/prebuilds/stdd-fs/win32-arm64/stdd-fs.exe +0 -0
- package/runtime/prebuilds/stdd-fs/win32-x64/stdd-fs.exe +0 -0
- package/runtime/sdk/adapters.mjs +279 -0
- package/runtime/sdk/file-observation.mjs +12 -0
- package/runtime/sdk/index.d.ts +140 -0
- package/runtime/sdk/index.mjs +31 -0
- package/runtime/sdk/native-fs.mjs +1235 -0
- package/runtime/sdk/path.mjs +71 -0
- package/runtime/sdk/text.mjs +42 -0
- package/runtime/sdk/workflow.mjs +294 -0
- package/runtime/templates/deferred-design.md +47 -0
- package/runtime/templates/github-stdd.yml +42 -0
- package/runtime/templates/gitlab-stdd.yml +72 -0
- package/runtime/templates/pr-description.md +35 -0
- package/scripts/adopting-root.mjs +42 -0
- package/scripts/stdd-hook.mjs +72 -0
- package/skills/stdd-brainstorming/SKILL.md +48 -0
- package/skills/stdd-debugging/SKILL.md +38 -0
- package/skills/stdd-delegate-slice/SKILL.md +118 -0
- package/skills/stdd-finish-change/SKILL.md +40 -0
- package/skills/stdd-implement/SKILL.md +28 -0
- package/skills/stdd-investigation/SKILL.md +35 -0
- package/skills/stdd-planning/SKILL.md +165 -0
- package/skills/stdd-pr-green/SKILL.md +52 -0
- package/skills/stdd-start-change/SKILL.md +39 -0
- package/skills/stdd-worktrees/SKILL.md +46 -0
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
# The STDD Method
|
|
2
|
+
|
|
3
|
+
This is the working contract. It is written for the agent or developer doing
|
|
4
|
+
the change, in the order the work happens.
|
|
5
|
+
|
|
6
|
+
## Sources of truth
|
|
7
|
+
|
|
8
|
+
Every repository adopting STDD names a **permanent docs tree** (for example
|
|
9
|
+
`docs/`) with an explicit hierarchy — typically product intent above domain
|
|
10
|
+
rules above implementation layers. When layers disagree, stop and reconcile
|
|
11
|
+
before implementing.
|
|
12
|
+
|
|
13
|
+
Three artifacts make claims about behavior, each in its own way:
|
|
14
|
+
|
|
15
|
+
- **Docs are the intended contract** — what the system is supposed to do.
|
|
16
|
+
- **Tests are the executable contract** — what the system provably does.
|
|
17
|
+
- **Code is the observed implementation** — what the system actually does.
|
|
18
|
+
|
|
19
|
+
A disagreement between them blocks implementation until they are reconciled.
|
|
20
|
+
None silently overrides the others: stale docs get corrected, wrong tests get
|
|
21
|
+
fixed, accidental behavior gets documented or removed — each resolution is an
|
|
22
|
+
explicit decision, not a default in favor of any one artifact.
|
|
23
|
+
|
|
24
|
+
## The loop
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
classify → read docs → docs edit (the spec) → failing test → implement → verify → PR evidence
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
1. **Classify the change.**
|
|
31
|
+
- *Behavior:* anything a user, operator, or downstream system can observe —
|
|
32
|
+
workflows, pricing, states, permissions, API contracts, copy with
|
|
33
|
+
business meaning.
|
|
34
|
+
- *Implementation-only:* refactors, lint fixes, build plumbing, mechanical
|
|
35
|
+
dependency updates that alter no behavior or architecture contract.
|
|
36
|
+
2. **Read the relevant docs first.** For behavior changes, read the matching
|
|
37
|
+
source-of-truth documents before proposing anything.
|
|
38
|
+
3. **Edit the docs — that edit is the spec.** Once the intended behavior is
|
|
39
|
+
agreed, update missing, stale, or ambiguous docs before tests and
|
|
40
|
+
production code. Make the docs edit the first reviewable unit — the first
|
|
41
|
+
commit where commits are used, otherwise the opening docs-only diff of the
|
|
42
|
+
PR — so the behavior contract can be reviewed on its own. A throwaway
|
|
43
|
+
exploratory spike may precede this commitment; discard it or explicitly
|
|
44
|
+
reclassify the change before review. If the docs already cover the
|
|
45
|
+
behavior, do not add duplicate prose — record that they were checked (see
|
|
46
|
+
PR evidence). Not every implementation detail deserves canonical prose.
|
|
47
|
+
4. **Write the failing test.** Red before green. Exception below.
|
|
48
|
+
5. **Implement** until the test passes, then refactor.
|
|
49
|
+
6. **Verify with the narrowest meaningful command.** Never claim "done",
|
|
50
|
+
"fixed", or "clean" without fresh verification evidence. Narrowest
|
|
51
|
+
meaningful governs the inner loop; once a PR exists, verification is
|
|
52
|
+
complete only when its required checks settle terminal-green on the
|
|
53
|
+
current head commit. `stdd ci --watch` is that wait, done right: it
|
|
54
|
+
pins the watch to the PR's current head, refuses to settle until the
|
|
55
|
+
check set is stable and fully terminal (a watcher attached right after
|
|
56
|
+
a push sees a partial set — the classic early-settle trap), restarts
|
|
57
|
+
itself when the head moves, and exits nonzero on a terminal failure.
|
|
58
|
+
Duplicate rollup entries for the same check name (re-runs, cancelled
|
|
59
|
+
concurrency twins) collapse to the freshest run, so a superseded
|
|
60
|
+
cancel never reads as a red. Never hand-roll the poller.
|
|
61
|
+
7. **State PR evidence.** Every PR carries exactly one of:
|
|
62
|
+
- `Docs updated first:` — list the changed docs;
|
|
63
|
+
- `Docs checked, no change needed:` — list the docs and the reason;
|
|
64
|
+
- `Docs not applicable:` — why the change is implementation-only.
|
|
65
|
+
|
|
66
|
+
The line must name its evidence — docs paths or a reason. A bare label
|
|
67
|
+
with nothing after the colon fails `stdd check-pr`, and only a line
|
|
68
|
+
starting at the beginning of a line counts (quoted templates and code
|
|
69
|
+
blocks do not).
|
|
70
|
+
|
|
71
|
+
When no valid line exists but a near-miss does — a markdown-formatted
|
|
72
|
+
label, a list or quote marker in front of it, or a wrong sentinel
|
|
73
|
+
wording — `stdd check-pr` points at that line and prints the corrected
|
|
74
|
+
form. The suggestion is advisory: the pass condition does not change.
|
|
75
|
+
|
|
76
|
+
With `--base <ref>` the claim is verified against the actual diff:
|
|
77
|
+
every doc path named after `Docs updated first:` must be a file changed
|
|
78
|
+
between the base ref and `HEAD` (and at least one path must be named);
|
|
79
|
+
paths named after `Docs checked, no change needed:` must exist in the
|
|
80
|
+
tree. Claiming a docs update the diff does not contain fails CI.
|
|
81
|
+
|
|
82
|
+
With `--pr <number|.>` the live PR is validated exactly as CI will see
|
|
83
|
+
it: the body is fetched from the forge, the base is the PR's own, and
|
|
84
|
+
the diff is taken against the PR's head commit — when the local checkout
|
|
85
|
+
is not on that commit, the head is fetched rather than silently diffing
|
|
86
|
+
the wrong tree. `.` resolves the current branch's PR.
|
|
87
|
+
|
|
88
|
+
`stdd evidence --base <ref>` drafts the line from ground truth instead
|
|
89
|
+
of recall. When canonical docs changed against the base, it prints the
|
|
90
|
+
finished `Docs updated first:` line to stdout — safe to embed in a PR
|
|
91
|
+
body via command substitution. When none changed, the remaining two
|
|
92
|
+
sentinels need an authored reason: the templates go to stderr and the
|
|
93
|
+
command exits nonzero, so substitution cannot silently embed a template.
|
|
94
|
+
The base comes from `--base` or the `baseRef` key in `.stdd/config.json`;
|
|
95
|
+
there is no built-in default.
|
|
96
|
+
|
|
97
|
+
## The frontend exception: design-first
|
|
98
|
+
|
|
99
|
+
Frontend **visual** work — layout, styling, markup structure, presentation
|
|
100
|
+
copy, component composition — is design-first, not test-first. A
|
|
101
|
+
failing-test-first loop forces the visual outcome to be specified before it
|
|
102
|
+
is explored; brittle rendering assertions then punish every design iteration.
|
|
103
|
+
|
|
104
|
+
The exception covers presentation, not meaning. Copy with business meaning —
|
|
105
|
+
prices, statuses, permissions, legal text, anything a user relies on as a
|
|
106
|
+
fact — is **behavior**: it goes through the docs edit and the normal loop.
|
|
107
|
+
Only its visual arrangement is design-first.
|
|
108
|
+
|
|
109
|
+
- Build the visual part freely; verify it visually (screenshots reviewed by a
|
|
110
|
+
human).
|
|
111
|
+
- Never write tests asserting static copy, class names, or pure rendering
|
|
112
|
+
output.
|
|
113
|
+
- After the visual part settles, add tests only for real behavior contracts:
|
|
114
|
+
hooks, formatters, state transitions, eligibility and conditional logic,
|
|
115
|
+
accessibility roles.
|
|
116
|
+
- Client-side **logic** follows the normal loop.
|
|
117
|
+
|
|
118
|
+
## Working artifacts are non-canonical by default
|
|
119
|
+
|
|
120
|
+
Plans, spec files, todo lists, handoff notes, and execution logs are working
|
|
121
|
+
artifacts. They help execution but can go stale as soon as the task or
|
|
122
|
+
checkout moves. When committed without an authority marker, they can outrank
|
|
123
|
+
fresher docs in code search and become a second source of truth.
|
|
124
|
+
|
|
125
|
+
The default STDD policy therefore keeps them uncommitted. This is a strong
|
|
126
|
+
default, not a universal ban: a team that needs an auditable design trail may
|
|
127
|
+
retain selected records when each record declares
|
|
128
|
+
`authority: non-canonical`, canonical retrieval rules exclude it by default,
|
|
129
|
+
and current behavior still has exactly one home in the permanent docs tree.
|
|
130
|
+
A repository that requires a strictly current-state-only tree sets
|
|
131
|
+
`projectLog.enabled` to `false`; `stdd check` then rejects tracked
|
|
132
|
+
`docs/project/**` files, and its generated method preamble and agent routing
|
|
133
|
+
override the generic project-log option below. Narrow `forbiddenArtifacts`
|
|
134
|
+
deliberately for any additional repository-specific archive paths and enforce
|
|
135
|
+
the chosen boundary with `contentRules`; never weaken it accidentally.
|
|
136
|
+
|
|
137
|
+
Where their content belongs instead:
|
|
138
|
+
|
|
139
|
+
| Content | Home |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| Durable rules (behavior, architecture, conventions) | The permanent docs tree, same PR |
|
|
142
|
+
| Design rationale, scope decisions, rejected alternatives | The PR description |
|
|
143
|
+
| Designs for deferred (not yet implemented) work | Dated project-log entries only when `projectLog.enabled` is `true`; otherwise outside the tracked tree |
|
|
144
|
+
| Task lists, sequencing | The durable plan (`.stdd/plan.md`, per checkout — see below), PR body |
|
|
145
|
+
|
|
146
|
+
The project log is **not canonical**: its entries are dated records of
|
|
147
|
+
decisions and future intentions, never a description of the present. Cite
|
|
148
|
+
canonical docs for how the system behaves; cite the project log only for why
|
|
149
|
+
something is deferred or was decided.
|
|
150
|
+
|
|
151
|
+
Because a plain `grep` cannot tell authority levels apart, the boundary is
|
|
152
|
+
made machine-readable on both sides. Every project-log entry starts with
|
|
153
|
+
frontmatter declaring itself non-canonical:
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
---
|
|
157
|
+
authority: non-canonical
|
|
158
|
+
status: deferred
|
|
159
|
+
---
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
When `projectLog.enabled` is `true`, the agent instructions `stdd init`
|
|
163
|
+
generates carry a retrieval rule: do not search the project log unless the
|
|
164
|
+
user explicitly asks for historical rationale or deferred work. When it is
|
|
165
|
+
`false`, generated instructions instead forbid creating or searching a project
|
|
166
|
+
log and direct history and rationale to git and PRs. The installed
|
|
167
|
+
`.stdd/method.md` begins with the same repository-policy override, so generic
|
|
168
|
+
method text cannot silently outrank the adopting repository's stricter rule.
|
|
169
|
+
|
|
170
|
+
`stdd check` enforces the configured artifact policy in CI; `stdd check-pr`
|
|
171
|
+
enforces the PR evidence line; `stdd doctor` reports a repository's overall adoption
|
|
172
|
+
health (setup, canonical docs, misleading artifacts, generated-file drift).
|
|
173
|
+
The rest of the method is review discipline — anything that later proves mechanically
|
|
174
|
+
checkable should move into `stdd check`.
|
|
175
|
+
|
|
176
|
+
A repository may declare a worktree-readiness contract in
|
|
177
|
+
`.stdd/config.json` — paths that must exist before verification output can
|
|
178
|
+
be trusted (installed dependencies, built packages, per-checkout env
|
|
179
|
+
files), each with a repo-authored fix hint. `stdd doctor` reports missing
|
|
180
|
+
ones; `stdd doctor --readiness` runs only that section, cheap enough for
|
|
181
|
+
every session start. The check is purely declarative — stdd verifies and
|
|
182
|
+
prescribes, it never installs, and it does not detect a stale-but-present
|
|
183
|
+
artifact (freshness belongs to the repo's own build tooling).
|
|
184
|
+
|
|
185
|
+
A repository may also declare **content rules** in `.stdd/config.json` —
|
|
186
|
+
mechanically checkable conventions that would otherwise live in folklore.
|
|
187
|
+
Each `contentRules` entry names the rule, a `files` glob, a `forbid`
|
|
188
|
+
and/or `require` regex, an optional repo-authored `message`, and
|
|
189
|
+
`newFilesOnly: true` to grade only files added against `baseRef`
|
|
190
|
+
(without a resolvable base, all matches are graded). `stdd check`
|
|
191
|
+
reports hits as violations; `stdd doctor` reports the section's health.
|
|
192
|
+
The kit ships the mechanism — the adopting repo authors the rule.
|
|
193
|
+
|
|
194
|
+
With a `branchPattern` regex in the same config, `stdd check` run on a
|
|
195
|
+
branch also validates the branch name — the pre-push hook thus rejects a
|
|
196
|
+
doomed name before the forge does. A detached checkout (CI) skips the
|
|
197
|
+
rule, and the pattern must match every branch a human pushes, including
|
|
198
|
+
long-lived ones (`^(main|dev|feat/|fix/)…`).
|
|
199
|
+
|
|
200
|
+
A repository also declares a **capability profile** in the same config —
|
|
201
|
+
a `capabilities` object stating what the agent environment can actually
|
|
202
|
+
do: `subagents` (fresh subagent sessions can be dispatched), `crossCli`
|
|
203
|
+
(selected agent CLIs may invoke a second reviewer CLI), `worktrees` (isolated
|
|
204
|
+
git worktrees are available). Defaults: `subagents` and `worktrees` on,
|
|
205
|
+
`crossCli` off. Playbooks are compiled against the profile at `stdd init`
|
|
206
|
+
time, never branched at runtime: a `<!-- cap:NAME --> … <!-- /cap -->`
|
|
207
|
+
block survives compilation only when its capability is on (a block
|
|
208
|
+
naming alternatives, `cap:a|b`, survives when any of them is on), and a
|
|
209
|
+
playbook whose frontmatter declares `requires: NAME` is skipped entirely
|
|
210
|
+
when it is off. Edit the profile and re-run `stdd init` — the generated skills
|
|
211
|
+
and the AGENTS snippet match the project again, and generated files a
|
|
212
|
+
previous init wrote that fall outside the new profile are removed
|
|
213
|
+
(only when still byte-identical to what init wrote). `stdd init
|
|
214
|
+
--capabilities <list>`, `stdd init --interview`, and `stdd configure` set the
|
|
215
|
+
profile without hand-editing JSON — see
|
|
216
|
+
`method/reference-integration.md`.
|
|
217
|
+
|
|
218
|
+
Agent adapters have two outputs with deliberately different context costs:
|
|
219
|
+
|
|
220
|
+
- a short, always-on instruction block carrying only repository invariants;
|
|
221
|
+
- native, lazily loaded skills carrying the task workflows.
|
|
222
|
+
|
|
223
|
+
Three routing skills make the main path explicit instead of asking an agent
|
|
224
|
+
to infer a workflow from a flat list: `stdd-start-change` classifies first,
|
|
225
|
+
opens a task only for repository-changing work, and routes read-only questions
|
|
226
|
+
without writing state; `stdd-implement` runs the docs/red/green/verify loop, and
|
|
227
|
+
`stdd-finish-change` closes review, evidence, PR checks, and any requested
|
|
228
|
+
runtime verification. Specialized playbooks remain independently invocable.
|
|
229
|
+
|
|
230
|
+
## The session ledger and `stdd status`
|
|
231
|
+
|
|
232
|
+
The loop's state must not live only in the agent's context window — context
|
|
233
|
+
is not durable storage. **Compaction is a trust boundary**: anything that
|
|
234
|
+
must survive a session lives in a file, never in conversation memory.
|
|
235
|
+
|
|
236
|
+
The ledger is that file: `.stdd/ledger.jsonl`, append-only JSONL, one event
|
|
237
|
+
per line. It is a working artifact — per checkout, never committed
|
|
238
|
+
(`stdd init` adds the ignore rule). A branch is not a task identity: base
|
|
239
|
+
branches and long-lived feature branches are reused. `stdd task start
|
|
240
|
+
<name>` therefore opens a random task ID and records the existing plan hash
|
|
241
|
+
as its baseline; subsequent events carry `taskId`. `stdd task finish`
|
|
242
|
+
closes the active task without deleting its evidence, and `stdd task reset`
|
|
243
|
+
closes it as abandoned and opens a fresh ID. Starting while another task is
|
|
244
|
+
active is an error; finish/reset are explicit so a new session cannot
|
|
245
|
+
silently discard another session's work. A crash never leaves the ledger in a
|
|
246
|
+
half-written state — see
|
|
247
|
+
`method/reference-generated-state.md`.
|
|
248
|
+
|
|
249
|
+
`stdd status --json` has one stable top-level shape in every lifecycle
|
|
250
|
+
state: `state`, `task`, `branch`, `loop`, `slice`, `plan`, `review`, `pr`,
|
|
251
|
+
and `next` are always present. Idle state uses explicit empty/null values,
|
|
252
|
+
so integrations never need a second response schema.
|
|
253
|
+
|
|
254
|
+
Readers consider only the current branch's active task. A plan that was
|
|
255
|
+
already present when the task started stays invisible until rewritten for
|
|
256
|
+
the new task. A closed task makes `stdd status` report `idle`, not the last
|
|
257
|
+
task's unfinished state. Branch-only events written by older stdd versions
|
|
258
|
+
remain readable as legacy state on a changed working branch, but are ignored
|
|
259
|
+
on a clean base branch so old work cannot be injected into a new session.
|
|
260
|
+
Recorders invoked without an explicit start keep the legacy behavior for
|
|
261
|
+
backward compatibility and tell the user to run `stdd task start`.
|
|
262
|
+
|
|
263
|
+
Recorders anchor to the repository, never the shell's working directory.
|
|
264
|
+
Run from any subdirectory, `stdd docs`/`red`/`verify`/`note` — and the
|
|
265
|
+
ledger reads inside `status`, `slice`, `scope`, `evidence`, and
|
|
266
|
+
`check-pr` — resolve one root: the git toplevel when it holds `.stdd/`
|
|
267
|
+
(or when no `.stdd/` exists yet), otherwise the nearest ancestor holding
|
|
268
|
+
`.stdd/`. The root `.stdd/config.json` resolves the same way, so a
|
|
269
|
+
`redPattern` applies from anywhere in the tree, and an accidental nested
|
|
270
|
+
`apps/*/.stdd/` cannot appear. The explicit directory argument of
|
|
271
|
+
`init`, `check`, and `doctor` is unchanged.
|
|
272
|
+
|
|
273
|
+
Recorders write it at the moment the fact happens:
|
|
274
|
+
|
|
275
|
+
- `stdd docs <updated-first|checked|not-applicable> [paths…] [--reason <why>]`
|
|
276
|
+
records the docs decision and its reason once, when it is made.
|
|
277
|
+
- `stdd red -- <cmd>` and `stdd verify -- <cmd>` run the command, record
|
|
278
|
+
`{cmd, exit, excerpt, snapshot}` verbatim, and pass the exit code through.
|
|
279
|
+
The snapshot binds the fact to the checkout state that produced it. What
|
|
280
|
+
follows `--` is the command and its arguments, never prose: a single
|
|
281
|
+
quoted description is rejected with the corrected form (wrap shell
|
|
282
|
+
constructs in `sh -c`) and records nothing. `red`
|
|
283
|
+
asserts genuine-red (a test-framework failure, not an environment error)
|
|
284
|
+
only when `.stdd/config.json` defines a `redPattern` regex matched against
|
|
285
|
+
the output; otherwise it records `genuine: "unknown"` and warns. A red run
|
|
286
|
+
that exits zero is recorded as not genuine — that is green, not red.
|
|
287
|
+
- `stdd note <text>` records free-form handoff context.
|
|
288
|
+
|
|
289
|
+
The ledger is **advisory input, never a gate by itself**. `stdd check` and
|
|
290
|
+
`check-pr` pass or fail exactly as without it; a missing ledger changes
|
|
291
|
+
nothing. Derivation replaces reconstruction where a ledger exists:
|
|
292
|
+
`stdd evidence` reads the recorded docs decision first — the diff remains
|
|
293
|
+
the cross-check, and on contradiction the diff wins and the conflict is
|
|
294
|
+
reported; the authored reason for `checked`/`not-applicable` comes from the
|
|
295
|
+
ledger instead of being retyped at PR time. `check-pr` adds one advisory
|
|
296
|
+
line when the body's evidence label disagrees with the recorded decision.
|
|
297
|
+
|
|
298
|
+
`stdd status` is the next-step oracle: callable at any moment, it answers
|
|
299
|
+
where in the loop this checkout is and what the next step is. Inputs in
|
|
300
|
+
order of trust: git (diff against the configured `baseRef`, branch, dirty
|
|
301
|
+
state), then the ledger, then the forge when available (`gh` reports the
|
|
302
|
+
branch's PR and its check rollup; offline or without `gh` these lines read
|
|
303
|
+
"unknown", never an error). Output is one screen ordered as the loop, with
|
|
304
|
+
a concrete `next:` suggestion; `--json` emits the same for agents.
|
|
305
|
+
`--local` omits the forge lookup unconditionally and is the only form
|
|
306
|
+
generated lifecycle hooks call. A red
|
|
307
|
+
event that exited zero or was classified `genuine: "no"` never closes red.
|
|
308
|
+
The latest docs decision is cross-checked too: `updated-first` must still
|
|
309
|
+
name docs in the current diff, while `checked` and `not-applicable` are
|
|
310
|
+
contradicted by a canonical-doc change; missing checked paths also stale the
|
|
311
|
+
decision.
|
|
312
|
+
Implementation is observed only when the checkout changes after the red
|
|
313
|
+
snapshot. A passing verify becomes stale after any later checkout change;
|
|
314
|
+
`status` asks for a fresh verify instead of displaying historical green as
|
|
315
|
+
current proof. Older ledger events without snapshots remain readable but
|
|
316
|
+
are explicitly reported as legacy evidence. Timing
|
|
317
|
+
leaves the prose: run `stdd status` at session start and before opening a
|
|
318
|
+
PR. Once the loop is verified and the plan is exhausted, the closing
|
|
319
|
+
review is the named next step ahead of the evidence line — when the
|
|
320
|
+
capability profile has a dispatch route on (`subagents` or `crossCli`),
|
|
321
|
+
`status` says to dispatch the fresh reviewer explicitly; with both off
|
|
322
|
+
the suggestion is omitted rather than degraded to self-review.
|
|
323
|
+
|
|
324
|
+
## The durable plan and `stdd defer`
|
|
325
|
+
|
|
326
|
+
A multi-step change needs a plan that survives compaction. Its working copy
|
|
327
|
+
is `.stdd/plan.md`: markdown with a checkbox list (`- [ ]` / `- [x]`), one
|
|
328
|
+
item per verifiable step, free prose around it. Like the ledger it is a
|
|
329
|
+
per-checkout working artifact — `stdd init` adds the ignore rule, and
|
|
330
|
+
`stdd check` fails when the plan or the ledger is a tracked file,
|
|
331
|
+
regardless of config.
|
|
332
|
+
|
|
333
|
+
An optional `Mode: inline|delegated` line (the first such line outside
|
|
334
|
+
code fences, case-insensitive; any other value reads as absent) records
|
|
335
|
+
the execution choice made at planning time, so it survives compaction
|
|
336
|
+
with the plan.
|
|
337
|
+
|
|
338
|
+
`stdd status` reads the plan and reports progress ("4/7 done") plus the
|
|
339
|
+
first open item, and the declared mode when the line is present (in
|
|
340
|
+
`--json`: `plan.mode`, null when absent). The mode is informational —
|
|
341
|
+
it never affects the gate or the stop hook. Once the current pass through the loop is verified and
|
|
342
|
+
open items remain, continuing the plan is the named next step — ahead of
|
|
343
|
+
drafting the evidence line and opening the PR.
|
|
344
|
+
|
|
345
|
+
A checkbox is a claim; for test-gated steps the ledger is the proof. An
|
|
346
|
+
item carrying a `[red: <substring>]` tag closes only when the current
|
|
347
|
+
branch's ledger holds a red event whose recorded command contains the
|
|
348
|
+
substring — a run recorded `genuine: "no"` (a green exit or an environment
|
|
349
|
+
error) never closes it. Until then the item counts as open even when
|
|
350
|
+
checked, and `stdd status` flags it as unproven.
|
|
351
|
+
|
|
352
|
+
A multi-step plan ends with an **independent review** of the cumulative
|
|
353
|
+
diff as its last item when the capability profile has a dispatch route
|
|
354
|
+
(`subagents` or `crossCli`). The item is written in at planning time so
|
|
355
|
+
the trigger travels with the plan rather than the session's memory. The
|
|
356
|
+
review is not a property of delegation — it closes inline work and
|
|
357
|
+
delegated work alike, and its reviewer is a fresh context (a read-only
|
|
358
|
+
subagent or the other CLI, per the capability profile) that sees the plan
|
|
359
|
+
and the diff, never the implementing session's history. With both dispatch
|
|
360
|
+
capabilities off, capability compilation omits the review item and closing
|
|
361
|
+
review guidance entirely; it never substitutes self-review.
|
|
362
|
+
|
|
363
|
+
The review item carries a `[review:]` tag, and the tag follows the same
|
|
364
|
+
claim-vs-proof rule as `[red:]`: the checkbox is a claim, the ledger is
|
|
365
|
+
the proof. Both tags are read from prose only — a backticked
|
|
366
|
+
`` `[review:]` `` names the tag as a literal and never gates the item. A tagged item closes only when the branch's newest `review`
|
|
367
|
+
event carries an `approved` verdict — recorded by `stdd review`, never
|
|
368
|
+
by ticking the box. Approval closes the item directly from the ledger without
|
|
369
|
+
rewriting the plan; its checkbox remains user-authored and may stay unchecked.
|
|
370
|
+
Until approval the item counts as open, and a checked item is flagged as
|
|
371
|
+
unproven.
|
|
372
|
+
|
|
373
|
+
`stdd defer <text>` records a scope cut for the active task: the text is
|
|
374
|
+
appended under the plan's `## Deferred` section, created as needed. It rejects
|
|
375
|
+
idle, legacy, and malformed task state before touching the plan, captures the
|
|
376
|
+
task and branch before reading it, and rechecks both before publishing, so a
|
|
377
|
+
concurrent task or branch switch records the cut nowhere. Appending to a plan
|
|
378
|
+
that predates `task start` changes its baseline hash and makes the plan,
|
|
379
|
+
including the deferred cut, visible to the active task. Deferred entries never
|
|
380
|
+
count toward progress; carry them into the PR description's out-of-scope when
|
|
381
|
+
the PR is assembled. The plan stays deletable at any moment — durable rules
|
|
382
|
+
flow to the docs edit, rationale and scope decisions to the PR description
|
|
383
|
+
(see "Working artifacts are non-canonical by default").
|
|
384
|
+
|
|
385
|
+
## Project policy and `stdd policy`
|
|
386
|
+
|
|
387
|
+
A repository accumulates standing decisions no kit rule can carry: which
|
|
388
|
+
migrations are pre-approved on which branch, which agent owns which area, what
|
|
389
|
+
a session should stop asking about. Their home is `.stdd/policy.md` — owned by
|
|
390
|
+
the repository, created by `stdd init` when absent, never overwritten
|
|
391
|
+
afterwards. Unlike the plan and the ledger it is tracked: a granted authority
|
|
392
|
+
must be visible in a diff and reviewable like any other rule.
|
|
393
|
+
|
|
394
|
+
The file holds two kinds of entries, and they differ in what they grant. A
|
|
395
|
+
**note** is free text under `## Notes`, appended by `stdd policy add <text>`.
|
|
396
|
+
It records project nuance and grants nothing — free text that reads like a
|
|
397
|
+
permission is still only a note. A **permission** is a structured line under
|
|
398
|
+
`## Permissions` naming one action and one condition, appended by
|
|
399
|
+
`stdd policy allow <action> --when <condition>`. Only permissions carry
|
|
400
|
+
authority.
|
|
401
|
+
|
|
402
|
+
A permission's action comes from a closed set: `merge`, `deploy`, `publish`,
|
|
403
|
+
`migrate`, `force-push`, and `external-mutation`. Any other action is rejected,
|
|
404
|
+
which is also why policy cannot waive a method gate — the docs edit, a genuine
|
|
405
|
+
red, verification, the closing review, and `stdd check` are not actions the
|
|
406
|
+
file can name. Policy widens what an agent may do without asking; it never
|
|
407
|
+
narrows what the loop must prove.
|
|
408
|
+
|
|
409
|
+
The set is enforced when the document is read, not only when `stdd policy`
|
|
410
|
+
writes it. The file is tracked and hand-editable, so an entry naming an
|
|
411
|
+
unknown action is reported as rejected and grants nothing; resting the closed
|
|
412
|
+
set on the CLI having been used would leave the guarantee to etiquette. Each
|
|
413
|
+
`stdd policy` append republishes the whole document bound to the identity and
|
|
414
|
+
bytes it read, so a concurrent edit fails the write instead of overwriting it.
|
|
415
|
+
|
|
416
|
+
The reader holds the writer's other rules too. An entry is one printable line:
|
|
417
|
+
a permission carrying control, bidirectional, or zero-width characters is not
|
|
418
|
+
honored, and neither is a bullet with no `— when:` clause. Those are dropped
|
|
419
|
+
rather than reported, because repeating unreadable bytes back into a
|
|
420
|
+
diagnostic is the thing the rule prevents; only a legible entry naming an
|
|
421
|
+
unknown action is echoed as rejected.
|
|
422
|
+
|
|
423
|
+
A section holds nothing but its own bullets. Any line that is neither blank
|
|
424
|
+
nor a well-formed bullet ends it — a heading, a fence, a rule, a paragraph.
|
|
425
|
+
Enumerating the constructs that close a section would be a losing game against
|
|
426
|
+
a hand-edited file, so a permission-shaped line anywhere else in the document
|
|
427
|
+
carries no authority by construction.
|
|
428
|
+
|
|
429
|
+
None of that binds a session that reads the markdown itself, so policy is
|
|
430
|
+
consulted through `stdd policy show`. That view is where the rules are applied:
|
|
431
|
+
it lists the grants the kit honors, the advisory notes, and any entry it
|
|
432
|
+
ignored with the reason. A guarantee enforced only in a library nobody calls is
|
|
433
|
+
not a guarantee, and the raw file is a record, not an authority.
|
|
434
|
+
|
|
435
|
+
Every permission carries a condition, and the condition is the point. Before
|
|
436
|
+
acting, the session verifies it mechanically and states what it verified: a
|
|
437
|
+
branch, an environment, a recorded review verdict, a terminal-green check set.
|
|
438
|
+
A condition the session cannot verify is not authorization — it asks, exactly
|
|
439
|
+
as it would with no policy at all.
|
|
440
|
+
|
|
441
|
+
Precedence runs live instruction, then policy, then kit default. A word in the
|
|
442
|
+
current session outranks the file; the file outranks what the playbooks would
|
|
443
|
+
otherwise ask. `stdd policy` writes only from the owning checkout and refuses
|
|
444
|
+
inside a managed gitless worker sandbox, so an agent cannot grant itself
|
|
445
|
+
authority. Playbooks consult the file before asking a question it may already
|
|
446
|
+
answer, and the always-on router names it so a session finds it without loading
|
|
447
|
+
a skill.
|
|
448
|
+
|
|
449
|
+
## The closing review and `stdd review`
|
|
450
|
+
|
|
451
|
+
`stdd review` runs the closing review and records its verdict as ledger
|
|
452
|
+
evidence. The route comes from the capability profile and the `review`
|
|
453
|
+
config (`{"review": {"via": "codex"}}`, default `subagent`); `--via`
|
|
454
|
+
overrides per call. `--via codex` and `--via claude` require the `crossCli`
|
|
455
|
+
capability, `--via subagent` requires `subagents` — an unavailable route is an
|
|
456
|
+
error, never a silent fall-back to self-review.
|
|
457
|
+
|
|
458
|
+
Every run starts the same way: the command snapshots the work under
|
|
459
|
+
review — a hash over the content of every path that differs from
|
|
460
|
+
`baseRef`, tracked or untracked and whether or not it is committed, plus
|
|
461
|
+
the plan's text. The snapshot follows content, never Git's bookkeeping:
|
|
462
|
+
staging or committing the reviewed work moves no bytes on disk, so it
|
|
463
|
+
cannot stale a verdict about those bytes. Editing them does.
|
|
464
|
+
The plan's checkbox marks and its `## Deferred` section are normalized
|
|
465
|
+
away — a ticked box is progress and a deferred entry is a recorded scope
|
|
466
|
+
cut, and neither is the specification the verdict was a comparison
|
|
467
|
+
against, so a session may close an item or defer a late finding without
|
|
468
|
+
discarding the approval. Editing the plan's words still stales it.
|
|
469
|
+
The session ledger,
|
|
470
|
+
the plan file, and only the exact private internal transaction names
|
|
471
|
+
described in
|
|
472
|
+
`method/reference-generated-state.md` are
|
|
473
|
+
exempt. Recording events
|
|
474
|
+
must never invalidate a review.
|
|
475
|
+
Every other tracked `.stdd/` deliverable (config, generated kit) stays
|
|
476
|
+
under review like any other file. An unresolvable base ref aborts the run —
|
|
477
|
+
a review of an unavailable diff proves nothing. The command then builds a
|
|
478
|
+
**brief** — the plan, the diff and a complete changed-file manifest, the
|
|
479
|
+
untracked files the diff cannot show, and the governing canonical docs the
|
|
480
|
+
reviewer reads for itself — plus
|
|
481
|
+
the review rubric: spec compliance against the
|
|
482
|
+
plan first, then code quality graded against named dimensions: needless
|
|
483
|
+
duplication where one home for the logic exists, magic numbers and
|
|
484
|
+
strings that deserve named constants, loose type contracts at
|
|
485
|
+
boundaries, swallowed or blanket-caught errors, tests that assert mocks
|
|
486
|
+
instead of behavior, unrequested extras (a finding, not a bonus),
|
|
487
|
+
inconsistency with surrounding patterns, and readability: working code
|
|
488
|
+
that is badly written is a legitimate blocking finding, not a style
|
|
489
|
+
nit — and a strict output contract: a single JSON object with required
|
|
490
|
+
`summary` and `findings` fields, each finding carrying
|
|
491
|
+
`severity: blocking | advisory`. Any wrong field type or output shape
|
|
492
|
+
rejects the whole result; the field-level rules are in
|
|
493
|
+
`method/reference-commands.md`, along with how the
|
|
494
|
+
brief is stored and settled and what each dispatch route does.
|
|
495
|
+
|
|
496
|
+
Repository text inside the brief is untrusted review data, never reviewer
|
|
497
|
+
instructions. The brief states this boundary explicitly; instructions found
|
|
498
|
+
inside plans, diffs, filenames, or source contents cannot replace the review
|
|
499
|
+
contract.
|
|
500
|
+
|
|
501
|
+
An automated reviewer is evidence, not a security boundary or a substitute
|
|
502
|
+
for accountable human review. Read-only tool enforcement limits mutation; it
|
|
503
|
+
does not make model judgment infallible or eliminate prompt-injection risk.
|
|
504
|
+
Teams choose which changes still require human approval.
|
|
505
|
+
|
|
506
|
+
The verdict is **derived, never self-declared**: no blocking findings
|
|
507
|
+
means `approved`, any blocking finding means `changes-requested`, and a
|
|
508
|
+
runner failure, timeout, malformed output, or stale snapshot means
|
|
509
|
+
`error` — an `error` is never an approval. The `review` event records
|
|
510
|
+
the verdict, the findings, the snapshot, and the runner's exit; exit
|
|
511
|
+
codes mirror the verdict (0 approved, 1 changes-requested, 2 error).
|
|
512
|
+
On `approved`, that one ledger fact closes the `[review:]` item; no
|
|
513
|
+
second plan write can leave the verdict and its projection split across a
|
|
514
|
+
crash or write failure. After `changes-requested`: fix the findings and run
|
|
515
|
+
`stdd review` again; the newest verdict controls the tag.
|
|
516
|
+
|
|
517
|
+
A repository may declare a **review budget**:
|
|
518
|
+
`{"review": {"maxRounds": 3}}`. Once the branch's ledger holds that
|
|
519
|
+
many `changes-requested` verdicts, `stdd review` refuses another
|
|
520
|
+
dispatch and says to defer the remaining findings; `--force --reason <text>`
|
|
521
|
+
spends one more round deliberately, and `error` verdicts (timeouts, malformed
|
|
522
|
+
output) never burn budget. The budget ends the **loop**, never the
|
|
523
|
+
judgment: the gate still refuses to bless an unproven claim, so the
|
|
524
|
+
honest exit past a spent budget is an unchecked review item plus the
|
|
525
|
+
open findings deferred into the PR. The default is unlimited; the knob
|
|
526
|
+
exists because unbounded re-review does not converge on a large diff —
|
|
527
|
+
a fresh reviewer finds one more, ever-smaller truth every round.
|
|
528
|
+
|
|
529
|
+
Overriding that budget is a decision, so it is recorded like one:
|
|
530
|
+
`--force` requires `--reason <text>` and refuses without it, `--reason`
|
|
531
|
+
is meaningless without `--force` and is refused there too, and the text
|
|
532
|
+
is stored on the `review-request` event as `forced`. A limit that can be
|
|
533
|
+
waived silently is not a limit — it is a suggestion nobody has to
|
|
534
|
+
account for. The recorded reasons are what later shows whether the loop
|
|
535
|
+
kept converging or turned into a treadmill, so they belong in the branch's
|
|
536
|
+
ledger next to the round they bought.
|
|
537
|
+
|
|
538
|
+
A stale approval (the snapshot differs from the current checkout)
|
|
539
|
+
reopens the review everywhere, not just in the gate: `stdd status`
|
|
540
|
+
counts the tagged item unproven again and names `stdd review` as the
|
|
541
|
+
next step — an approval of a diff nobody can see anymore proves
|
|
542
|
+
nothing about the diff that exists now. So an approved verdict freezes
|
|
543
|
+
the checkout: anything found afterwards is either deferred with
|
|
544
|
+
`stdd defer` or costs a fresh round. Editing on top of an approval does
|
|
545
|
+
not preserve it, it discards it.
|
|
546
|
+
|
|
547
|
+
`stdd status --gate` folds the review state into an exit code for hooks
|
|
548
|
+
and scripts. It exits non-zero when a `[review:]` item is checked but
|
|
549
|
+
unproven, when the newest review verdict is `changes-requested` or
|
|
550
|
+
`error`, when an `approved` verdict is stale, or when a review claim or
|
|
551
|
+
open request needs a route that the capability profile cannot dispatch.
|
|
552
|
+
A configured route is otherwise dormant: a profile with neither
|
|
553
|
+
`subagents` nor `crossCli` may keep the default route and passes the gate
|
|
554
|
+
when it makes no review claim. An unchecked review item on its own never
|
|
555
|
+
fails the gate — work in progress remains pushable; the gate judges
|
|
556
|
+
claims, not pace.
|
|
557
|
+
|
|
558
|
+
## Delegating a slice
|
|
559
|
+
|
|
560
|
+
When an orchestrating session hands a slice of the work to a worker
|
|
561
|
+
session, the roles are fixed: the **orchestrator** owns the docs edit, the
|
|
562
|
+
commits, and the PR; the **worker** owns red-green inside a declared scope.
|
|
563
|
+
The handoff artifact is the ledger, not prose — a worker's chat summary
|
|
564
|
+
does not survive compaction, its recorded events do.
|
|
565
|
+
|
|
566
|
+
The scope is declared before the worker starts, never after: `stdd slice new`
|
|
567
|
+
for an in-checkout worker, `stdd worker create <directory>` for one that must
|
|
568
|
+
have no Git authority. Both take `--frozen` (globs the slice must not touch)
|
|
569
|
+
and `--allowed` (globs it may touch), and at least one is required — an
|
|
570
|
+
undeclared slice cannot be graded.
|
|
571
|
+
|
|
572
|
+
Both forms record a `scope` event carrying globs and a **baseline**, and
|
|
573
|
+
`stdd scope` grades the result against that baseline. The worker records
|
|
574
|
+
red/verify/note events as it goes, and the orchestrator assembles the PR body
|
|
575
|
+
from the parent ledger. What a managed sandbox
|
|
576
|
+
copies, what `stdd worker collect` refuses, and how the postflight reads are in
|
|
577
|
+
`method/reference-commands.md`.
|
|
578
|
+
|
|
579
|
+
The worker asks its blocking questions before the first edit — not
|
|
580
|
+
mid-slice — and ends with exactly one status: `DONE`,
|
|
581
|
+
`DONE_WITH_CONCERNS`, `BLOCKED`, or `NEEDS_CONTEXT`. Escalating early is
|
|
582
|
+
never penalized: bad work is worse than no work. Briefs and reports
|
|
583
|
+
travel as files, never pasted prose — pasted context stays resident in
|
|
584
|
+
the orchestrator's window for the rest of the session.
|
|
585
|
+
|
|
586
|
+
The brief, the orchestrator's two review verdicts, and the handling of a
|
|
587
|
+
`BLOCKED` slice live in the delegate-slice playbook — the document a session
|
|
588
|
+
doing this work has already loaded. One rule lives in one document.
|
|
589
|
+
|
|
590
|
+
## Bug fixes and refactors
|
|
591
|
+
|
|
592
|
+
- **Bug fix:** reproduce the symptom in a test before editing. Fix the root
|
|
593
|
+
cause, not the symptom.
|
|
594
|
+
- **Refactor:** prove behavior preservation with existing tests, typecheck,
|
|
595
|
+
or focused characterization tests. No docs edit needed when behavior and
|
|
596
|
+
contracts are unchanged.
|
|
597
|
+
|
|
598
|
+
## Style for docs
|
|
599
|
+
|
|
600
|
+
Concise. Short, direct sentences. Do not omit words that carry meaning. One
|
|
601
|
+
rule lives in one document — link, don't duplicate. Canonical docs use the
|
|
602
|
+
repository's declared language and describe the **present**. Configure
|
|
603
|
+
`temporalPhrases` in that language to flag likely historical narrative; this
|
|
604
|
+
is a deliberately simple heuristic, not semantic proof. History usually
|
|
605
|
+
belongs in git and PR descriptions. Fenced code blocks and inline code spans
|
|
606
|
+
are exempt: a backticked phrase is a literal being named, not narrative — a
|
|
607
|
+
doc may state this very rule without tripping it.
|
|
608
|
+
|
|
609
|
+
## Reference
|
|
610
|
+
|
|
611
|
+
This document is what a session reads before a change, so it holds the
|
|
612
|
+
contract and nothing else. The mechanisms behind it are canonical too, and
|
|
613
|
+
live beside it:
|
|
614
|
+
|
|
615
|
+
- `method/reference-generated-state.md` — how
|
|
616
|
+
generated files are authenticated, retired, and recovered: manifest hashes,
|
|
617
|
+
the cleanup journal, the bundled `stdd-fs` helper, the printable-text
|
|
618
|
+
boundary, and ledger transaction state.
|
|
619
|
+
- `method/reference-integration.md` — what `stdd
|
|
620
|
+
init` and `stdd configure` write: capability profiles, per-host agent
|
|
621
|
+
outputs, adoption modes and the universal bundle, project-local recipes, CI
|
|
622
|
+
adapters, and lifecycle hooks.
|
|
623
|
+
- `method/reference-commands.md` — the internals of
|
|
624
|
+
`stdd review` and the worker commands: the review result contract, brief
|
|
625
|
+
storage and settlement, dispatch routes, managed sandboxes, and the scope
|
|
626
|
+
postflight.
|
|
627
|
+
|
|
628
|
+
## What stdd does not cover
|
|
629
|
+
|
|
630
|
+
stdd is a process contract, not an engineering standard. Architecture rules,
|
|
631
|
+
dependency-injection styles, error-handling policy, tenant/auth/data safety,
|
|
632
|
+
and database-migration policy stay in the adopting team's own contract
|
|
633
|
+
(typically `AGENTS.md`) and docs tree. stdd tells you *where* such rules
|
|
634
|
+
live and *when* they must be written — not what they should say.
|