pincer-workflow 0.5.0 → 0.7.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.
Files changed (59) hide show
  1. package/README.md +106 -19
  2. package/bin/pincer.js +17 -1
  3. package/package.json +3 -3
  4. package/template/.agents/skills/pincer-code/SKILL.md +88 -14
  5. package/template/.agents/skills/pincer-evaluate/SKILL.md +42 -13
  6. package/template/.agents/skills/pincer-narrow/SKILL.md +53 -12
  7. package/template/.agents/skills/pincer-plan/SKILL.md +12 -4
  8. package/template/.agents/skills/pincer-release/SKILL.md +18 -0
  9. package/template/.agents/skills/pincer-status/SKILL.md +29 -5
  10. package/template/.claude/commands/pincer-code.md +88 -14
  11. package/template/.claude/commands/pincer-evaluate.md +42 -13
  12. package/template/.claude/commands/pincer-narrow.md +53 -12
  13. package/template/.claude/commands/pincer-plan.md +12 -4
  14. package/template/.claude/commands/pincer-release.md +18 -0
  15. package/template/.claude/commands/pincer-status.md +29 -5
  16. package/template/.claude/hooks/hook-policy.cjs +13 -6
  17. package/template/.claude/references/prd-template.md +11 -4
  18. package/template/.codex/README.md +1 -1
  19. package/template/.github/prompts/pincer-code.prompt.md +88 -14
  20. package/template/.github/prompts/pincer-evaluate.prompt.md +42 -13
  21. package/template/.github/prompts/pincer-narrow.prompt.md +53 -12
  22. package/template/.github/prompts/pincer-plan.prompt.md +12 -4
  23. package/template/.github/prompts/pincer-release.prompt.md +18 -0
  24. package/template/.github/prompts/pincer-status.prompt.md +29 -5
  25. package/template/AGENTS.md +17 -1
  26. package/template/docs/dry-run-checklist.md +30 -3
  27. package/template/docs/release-checklist.md +3 -1
  28. package/template/docs/runtime-contracts.md +1428 -96
  29. package/template/scripts/pincer-evidence.cjs +9 -7
  30. package/template/scripts/pincer-runtime/adopt.cjs +132 -0
  31. package/template/scripts/pincer-runtime/agreement.cjs +240 -0
  32. package/template/scripts/pincer-runtime/authorization.cjs +167 -0
  33. package/template/scripts/pincer-runtime/changes.cjs +517 -0
  34. package/template/scripts/pincer-runtime/checks.cjs +48 -0
  35. package/template/scripts/pincer-runtime/coverage.cjs +361 -0
  36. package/template/scripts/pincer-runtime/dispositions.cjs +95 -0
  37. package/template/scripts/pincer-runtime/evidence.cjs +303 -18
  38. package/template/scripts/pincer-runtime/gates.cjs +73 -0
  39. package/template/scripts/pincer-runtime/identity.cjs +21 -4
  40. package/template/scripts/pincer-runtime/impact.cjs +177 -0
  41. package/template/scripts/pincer-runtime/io.cjs +41 -0
  42. package/template/scripts/pincer-runtime/lifecycle.cjs +34 -12
  43. package/template/scripts/pincer-runtime/locator.cjs +158 -0
  44. package/template/scripts/pincer-runtime/migrate.cjs +140 -63
  45. package/template/scripts/pincer-runtime/parse.cjs +20 -1
  46. package/template/scripts/pincer-runtime/phases.cjs +245 -0
  47. package/template/scripts/pincer-runtime/readiness.cjs +15 -2
  48. package/template/scripts/pincer-runtime/requirements.cjs +255 -0
  49. package/template/scripts/pincer-runtime/resume.cjs +273 -0
  50. package/template/scripts/pincer-runtime/routing.cjs +54 -0
  51. package/template/scripts/pincer-runtime/runner.cjs +24 -6
  52. package/template/scripts/pincer-runtime/scaffold.cjs +254 -0
  53. package/template/scripts/pincer-runtime/state.cjs +29 -7
  54. package/template/scripts/pincer-runtime/status.cjs +178 -22
  55. package/template/scripts/pincer-runtime/transaction.cjs +200 -0
  56. package/template/scripts/pincer-runtime/transitions.cjs +134 -0
  57. package/template/scripts/pincer-runtime.cjs +412 -76
  58. package/template/scripts/pincer-status.sh +1 -1
  59. package/template/scripts/pincer-ticket.sh +1 -1
@@ -49,6 +49,24 @@ durable runtime-owned release record is later work.
49
49
  exits 1 and names the check). A fresh clone reports `local verification history
50
50
  unavailable; saved candidate evidence validated only`: state that limit in the
51
51
  verdict rather than claiming local verification.
52
+ - Change records (the `Runtime` line reads `changes`): the selected change is
53
+ `completed`, its authorization is `current` for the current agreement, and its
54
+ evaluation locator `.prd/evidence/changes/<id>.json` names the evaluated
55
+ candidate; `node scripts/pincer-runtime.cjs ready` reports `SELECTION_REQUIRED`,
56
+ `LIFECYCLE_BLOCKED`, `DECISION_REQUIRED`, `AUTHORIZATION_REQUIRED` or
57
+ `AGREEMENT_CHANGED` as failures. Release selects, activates and completes nothing;
58
+ historical evidence of a cancelled or superseded change is inspectable but never
59
+ release-ready.
60
+ - Strict coverage (the status line reads `Coverage strict …`): read
61
+ `node scripts/pincer-runtime.cjs coverage` — structure complete, every in-scope
62
+ scenario `delivered` on the evaluated candidate, deferrals and removals backed by
63
+ their decision and user authorization, `delivery` reported as original versus
64
+ agreed scope (never conflate them), adequacy `adequate`; the manifest is schema 3
65
+ and the `Provenance` line reads `runtime (schema 3)`. `ready` names
66
+ `COVERAGE_INCOMPLETE`, `SCOPE_UNAUTHORIZED`, `OBLIGATION_MISSING`,
67
+ `REVIEW_MISSING` and `ADEQUACY_REQUIRED` as failures. A change labeled
68
+ `Coverage unverified` did not adopt strict coverage: audit it under the rules
69
+ above and say so; never imply strict coverage was established.
52
70
  - Every file the manifest lists is tracked, and `git status --short` is empty before
53
71
  and after the audit.
54
72
  - Run the repository's candidate-wide release gate directly (`npm test`, or the
@@ -12,7 +12,19 @@ start of a session. Read-only: change nothing.
12
12
 
13
13
  ## Steps
14
14
 
15
- 1. Run `scripts/pincer-status.sh`. It reads the artifacts on disk (`.prd/`, `tickets/`,
15
+ 1. On a project with change records, start with
16
+ `node scripts/pincer-runtime.cjs resume --brief`. It is one read that answers the
17
+ question this command exists for: the selected change and its lifecycle, the
18
+ authorization verdict, the coverage label, ticket and attempt counts, every blocking
19
+ reason category with its count, and the exact next command — the full report's own,
20
+ copied, never recomputed. Reading the full `resume`, `status` and `coverage` reports
21
+ in sequence to find one next action costs several kilobytes to answer in a line, and
22
+ on a large change the full report grows with the work while the answer does not.
23
+ Read further only when the brief gives you a reason to: it ends with the `Detail`
24
+ line naming the command that prints every row it grouped.
25
+ 2. When you need the detail — a blocker you must act on, a ticket list, an attempt
26
+ history — run `scripts/pincer-status.sh`. It reads the artifacts on disk (`.prd/`,
27
+ `tickets/`,
16
28
  `NOTES.md`) and prints the PRD state and profile, every ticket with its state and
17
29
  clock-based elapsed time, what is blocked, wall-clock build time while a ticket is in
18
30
  progress or against an explicit user budget, the `Runtime` line (legacy receipts or
@@ -20,12 +32,24 @@ start of a session. Read-only: change nothing.
20
32
  candidate, any warnings (each readiness problem once), and the next command to run.
21
33
  `scripts/pincer-status.sh --json` prints one status object with reason codes and the
22
34
  next action for tooling; `node scripts/pincer-runtime.cjs ready [T-NN]` is the
23
- read-only gate.
24
- 2. Report in three lines: where the workflow is, what is in progress or blocked, and the
35
+ read-only gate. On a project with change records (`Runtime changes …`)
36
+ `node scripts/pincer-runtime.cjs resume` is the full report: it reports the selected change, its
37
+ lifecycle, agreement and authorization, decisions, references, tickets, attempts,
38
+ candidate, the authored handoff note and the next command, and never writes;
39
+ `change list` shows every retained change, `status --change <id>` and
40
+ `resume --change <id>` inspect another one without selecting it. `resume` is the
41
+ report; `change resume <id>` is the lifecycle operation. The `Coverage` line says
42
+ `strict` or `unverified`; on a strict change `node scripts/pincer-runtime.cjs coverage`
43
+ (and `impact` after an edit) name the exact scenario, ticket, check or decision
44
+ that is next — quote them rather than inferring coverage from the ticket list.
45
+ On a legacy project there is no change record to summarize, so `pincer-status.sh`
46
+ is the first read.
47
+ 3. Report in three lines: where the workflow is, what is in progress or blocked, and the
25
48
  next command. Quote the `Next` line as-is. When the `Runtime` line says `legacy`,
26
49
  add the register or migrate command it names as the step that precedes the next
27
- ticket (fresh project → `register`, legacy receipts → `migrate --preview`).
28
- 3. If a ticket is `in_progress`, read it and `git status`, then offer to resume it with
50
+ ticket (fresh project → `register`, legacy receipts → `migrate --preview`, a v0.5.0
51
+ binding → `migrate --preview`, no selection → `change select <id>`).
52
+ 4. If a ticket is `in_progress`, read it and `git status`, then offer to resume it with
29
53
  `$pincer-code T-{NN}`. If the script printed a warning, surface it — a done ticket
30
54
  without a receipt was marked by hand and needs `scripts/pincer-ticket.sh verify T-{NN}`.
31
55
  Never restore a ticket file from git to clear a warning; a failed attempt is a record.
@@ -19,8 +19,14 @@ receipt that matches the current check, or with unticked acceptance criteria. Ne
19
19
  On a migrated project (a change binding under `.prd/changes/`; the `Runtime` line of
20
20
  `scripts/pincer-status.sh` names it) `verify` records an attempt under `.pincer/runtime/`
21
21
  and writes no receipt into the ticket, and `done` consumes the current passing attempt
22
- against the current source without re-running the check. `.pincer/` and `.prd/changes/`
23
- are written only by the runtime; never edit or delete them by hand.
22
+ against the current source without re-running the check. `.pincer/`, `.prd/changes/` and `.prd/evidence/changes/`
23
+ are written only by the runtime; never edit or delete them by hand. On a project with
24
+ change records (`Runtime changes …`) every `start`, `verify` and `done` first passes
25
+ the change gate: the ticket's change must be selected in this worktree, `active`
26
+ (`verify` also runs on a `completed` change), on a compatible branch, and authorized
27
+ for the current agreement — otherwise the command refuses with `SELECTION_REQUIRED`,
28
+ `WRONG_CHANGE`, `LIFECYCLE_BLOCKED`, `BASE_MISMATCH`, `DECISION_REQUIRED`,
29
+ `AUTHORIZATION_REQUIRED` or `AGREEMENT_CHANGED` before anything runs or is written.
24
30
 
25
31
  ## Before the loop
26
32
 
@@ -28,15 +34,37 @@ Run `scripts/pincer-status.sh`. It lists every ticket's state, what is blocked,
28
34
  build time from the clock, and the next action. If a ticket is `in_progress`, you are
29
35
  resuming: read it, check `git status` / `git diff` for uncommitted work, and continue
30
36
  from wherever the receipt says you are. Do not ask the user to reconfirm unchanged,
31
- previously authorized work. Read the `Runtime` line before the first ticket: a change
32
- binding present → continue; `legacy` and no ticket of this PRD carries legacy
33
- receipts → register now (`node scripts/pincer-runtime.cjs register --prd .prd/prd-vN.md --authorization "<the user's approval, quoted>"`,
34
- commit `.prd/changes/` and `.gitignore` as `Register PRD vN`); `legacy` with legacy receipts → run
35
- `node scripts/pincer-runtime.cjs migrate --preview --prd .prd/prd-vN.md`, show the plan
36
- (backups, receipts imported as history, `.gitignore` line) and ask once whether to
37
- apply. Apply only on a yes, then commit the rewritten tickets, `.gitignore` and the
38
- binding as `Migrate PRD vN to the runtime`. Never migrate silently, and never apply
39
- when the preview reports a conflict.
37
+ previously authorized work. Read the `Runtime` line before the first ticket. `changes` (change records under
38
+ `.prd/changes/`) → run `node scripts/pincer-runtime.cjs resume` and follow its `Next`
39
+ line: it names the selected change, its lifecycle state, the agreement and the
40
+ authorization verdict, the blockers in order and the exact next command, all from the
41
+ files on disk. `resume --brief` is the same report projected to counts and one next
42
+ action — the same verdict and the same `Next` line, with every blocker category kept
43
+ and its `Detail` line naming the command that prints the rows it grouped. Prefer it
44
+ when you are orienting, and read the full report when a blocker needs its detail. Select the change to work on (`node scripts/pincer-runtime.cjs change
45
+ select <id>`; selection is local metadata and touches no source), activate it
46
+ (`change activate <id>`; refused until the user's authorization is recorded with
47
+ `change authorize` and no consequential decision is open) and resume a paused change
48
+ with `change resume <id>`. On a change with strict coverage (`Coverage strict …` in status) also read
49
+ `node scripts/pincer-runtime.cjs coverage` — it names the scenario, ticket, check or
50
+ decision that is next and every structural gap (`COVERAGE_INCOMPLETE`,
51
+ `SCOPE_UNAUTHORIZED`, `OBLIGATION_MISSING`) — and, after any PRD, ticket or map edit,
52
+ `node scripts/pincer-runtime.cjs impact` (`--from G-NN` for another baseline): it
53
+ lists the affected scenarios, tickets and checks with reasons and the dependency
54
+ dependents separately, and reports an unscoped PRD change or unavailable history
55
+ rather than "no impact". `legacy` and no ticket of this PRD carries legacy
56
+ receipts → register now (`node scripts/pincer-runtime.cjs register --prd .prd/prd-vN.md`,
57
+ commit `.prd/changes/` and `.gitignore` as `Register PRD vN`), then record the user's
58
+ approval (`change authorize …`, as `/pincer-narrow` describes), select and activate.
59
+ `legacy` with legacy receipts, or a v0.5.0 binding (`Runtime change <id> · revision …`)
60
+ → run `node scripts/pincer-runtime.cjs migrate --preview --prd .prd/prd-vN.md`, show
61
+ the plan (backups, receipts imported as history, the converted binding, the
62
+ `.gitignore` line, the local selection) and ask once whether to apply. Apply only on
63
+ a yes, then commit the rewritten tickets, `.gitignore` and the record as `Migrate PRD
64
+ vN to the runtime`. The migrated change is planned with no authorization: record the
65
+ user's actual earlier instruction with `change authorize` (the v0.5.0 free text is
66
+ history only) and activate it. Never migrate silently, and never apply when the
67
+ preview reports a conflict.
40
68
 
41
69
  ## Loop (per ticket, in dependency order)
42
70
 
@@ -136,6 +164,48 @@ above applies only before migration. A session that died mid-`verify` leaves a `
136
164
  attempt: run `node scripts/pincer-runtime.cjs recover`, which finalizes it as
137
165
  `interrupted` once the owner process is gone, then `verify` again.
138
166
 
167
+ ## Changes: pausing, decisions and completion
168
+
169
+ - Stopping before the change is complete (end of session, switching to another
170
+ change): `node scripts/pincer-runtime.cjs change pause <id> --reason "<why>" --note "<handoff for the next session>"`
171
+ and commit the record. Pausing keeps every ticket state, attempt and authorization;
172
+ it refuses while a check is running (`recover` first if its owner died). A fresh
173
+ session runs `resume`, then `change resume <id>` under the existing authorization —
174
+ do not ask the user to re-approve unchanged scope. Another change's work in the
175
+ meantime makes this change's passing attempts `SOURCE_CHANGED`; verify again, do
176
+ not ask for approval again.
177
+ - A newly discovered consequential choice: `node scripts/pincer-runtime.cjs change decide <id> --summary "<the question>"`
178
+ blocks execution (`DECISION_REQUIRED`) until the user answers; record the answer with
179
+ `change decide <id> --resolve D-NN --reference "<where>" --excerpt "<the user's words>"`,
180
+ then `change authorize <id> --agreement <digest> --reference … --excerpt … --decision D-NN`.
181
+ A revision within the user's delegation (for example an added regression check for
182
+ approved behavior) records `change authorize <id> --agreement <digest> --delegated --basis A-NN --explanation "<why it stays within the delegation>"`
183
+ without asking again, and the changed check still needs fresh verification.
184
+ - Editing the PRD under its filename, or a ticket's acceptance text, dependencies,
185
+ size, timeout, association or check — and, with strict coverage, a scenario's text,
186
+ a map link, a declared command or timeout, or a scope entry — changes the agreement:
187
+ execution refuses with `AGREEMENT_CHANGED` (status shows the structural difference;
188
+ `impact` explains it) until its disposition is recorded as above. Ticking criteria, starting or closing tickets and recording
189
+ attempts never change it. An `AGREEMENT_CHANGED` caused by an edit this session
190
+ did not make — a revised PRD, an added or changed ticket found on resume — is a
191
+ consequential decision: raise it with `change decide <id> --summary "<what changed>"`,
192
+ report the structural difference (and the `impact` report) and stop. A general instruction to continue,
193
+ resume or not re-ask never authorizes new scope; record a `user` authorization for
194
+ the revised agreement only for an instruction that names the revised content. With strict coverage a scenario that will not be delivered is
195
+ never dropped from the map or the PRD: it is deferred or removed through a decision
196
+ the user resolves naming it, a `scope` entry (a removal keeps a tombstone naming the
197
+ prior agreement) and an authorization naming that decision; `coverage` reports
198
+ `OBLIGATION_MISSING` or `SCOPE_UNAUTHORIZED` until then, and `change complete`
199
+ refuses. A revised check declaration (a stricter command, an added check) within the
200
+ user's delegation is a `--delegated` authorization and needs fresh verification.
201
+ - When every ticket is done and ready: `node scripts/pincer-runtime.cjs change complete <id>`
202
+ (it refuses unfinished tickets, unticked criteria, stale or failed verification and
203
+ open decisions; with strict coverage also an incomplete map, an unauthorized
204
+ disposition or a missing obligation, before the ticket gate) and commit the record
205
+ as `Complete PRD vN`. Completion never asks for candidate evidence. Completed means ready
206
+ for evaluation, not evaluated or released; a later finding is `change reopen <id> --reason …`
207
+ plus a fix ticket.
208
+
139
209
  ## Budget rules
140
210
 
141
211
  - If the user set `PINCER_BUILD_BUDGET_MIN` or stated another budget, use the elapsed
@@ -147,7 +217,8 @@ attempt: run `node scripts/pincer-runtime.cjs recover`, which finalizes it as
147
217
 
148
218
  ## When all tickets are done
149
219
 
150
- Update the PRD to `status: built` and commit that change on its own (`PRD vN: built`).
220
+ On a project with change records, complete the change first (`change complete <id>`,
221
+ committed as `Complete PRD vN`). Then update the PRD to `status: built` and commit that change on its own (`PRD vN: built`).
151
222
  The built transition is part of the candidate that `/pincer-evaluate` reviews; it is
152
223
  never moved into a later evidence-only commit. Then finish with:
153
224
  "All tickets built. Run `/pincer-evaluate` for a final quality pass."
@@ -159,6 +230,9 @@ material choice not already authorized, and prepare the concrete proposal before
159
230
  asking. A decision the user delegated (for example "pick the architecture") does not
160
231
  need another approval when you exercise it, but a newly discovered consequential
161
232
  choice is surfaced before implementation. Record the authorization basis and the
162
- scope it covers in the PRD or the handover. An agent-written record or a status
233
+ scope it covers in the PRD or the handover, and on a project with change records as
234
+ a `change authorize` record (the user's words as the excerpt, or a `--delegated`
235
+ disposition with its basis). An agent-written record or a status
163
236
  field is not authenticated human approval. When resuming without the context that
164
- granted authorization, do not invent it — ask.
237
+ granted authorization, do not invent it — read the `resume` report; an authorization
238
+ it reports as `current` needs no repeat approval, and any other verdict is asked.
@@ -19,8 +19,10 @@ run the pipeline, then present results.
19
19
  that uncertainty before claiming a complete review. Record full commit IDs for
20
20
  `base` and `candidate` (`git rev-parse HEAD`), then review `git diff <base>..<candidate>`.
21
21
  The candidate is the clean, committed tree that already includes the implementation,
22
- the ticket closures and the PRD `status: built` commit: `git status --short` must be
23
- empty before review. If anything is uncommitted or the PRD is not yet built, return
22
+ the ticket closures, on a project with change records the `change complete` commit
23
+ (the `Runtime` line reads `… · completed ·`; `check` and `evidence export` refuse an
24
+ active, paused or unauthorized change), and the PRD `status: built` commit:
25
+ `git status --short` must be empty before review. If anything is uncommitted or the PRD is not yet built, return
24
26
  to `/pincer-code`; do not review a dirty tree.
25
27
  2. Dispatch a `code-quality-reviewer` agent with: the diff, the PRD's Success Criteria and
26
28
  Scope sections, and the list of tickets. If the diff is large, split by area and
@@ -28,7 +30,13 @@ run the pipeline, then present results.
28
30
  in a separate pass, applying `.claude/agents/code-quality-reviewer.md` as the rubric.)
29
31
  Keep the reviewer's report — or its explicit no-findings statement — for step 9,
30
32
  where it is saved as an artifact; a review that left no record cannot be audited.
31
- 3. Yourself, in parallel, check spec compliance. For every requirement `R-NN` in the
33
+ 3. Yourself, in parallel, check spec compliance. On a change with strict coverage
34
+ (`Coverage strict …` in status) start from `node scripts/pincer-runtime.cjs coverage`:
35
+ its structure must be complete, and its scenario rows are the obligations — the
36
+ export derives every disposition from the map and the outcomes, so you do not
37
+ author `requirements`; your judgment is recorded as `adequacy` (whether the
38
+ declared checks and reviews really establish their scenarios) and in
39
+ `coverage_review`. Otherwise, for every requirement `R-NN` in the
32
40
  PRD record one disposition: `delivered` (evidence on this candidate), `blocked`
33
41
  (required behavior failed or was left unverified — this blocks PASS; do not relabel
34
42
  it a known limitation to pass), or `deferred` (only with explicit user authorization;
@@ -63,8 +71,20 @@ run the pipeline, then present results.
63
71
  produces a new candidate: re-record `candidate`, re-run the checks against it, and
64
72
  write fresh evidence in step 9 — never reuse a manifest from a previous candidate.
65
73
  9. Persist evidence for the candidate under `.prd/evidence/prd-vN/<candidate>/`.
66
- Migrated project (the `Runtime` status line names a change): run each executable
67
- check through the runtime on the clean candidate view —
74
+ Strict coverage: run every declared command check as
75
+ `node scripts/pincer-runtime.cjs check C-NN --candidate <sha>` (no command, no
76
+ timeout: the map's declaration is the only source, and a supplied command is
77
+ `CHECK_UNDECLARED`); record each declared review or visual obligation in the
78
+ draft with its `result` and an artifact saved under the candidate's evidence
79
+ directory (a required one that is not passed with an artifact is
80
+ `REVIEW_MISSING`); write `adequacy: { verdict: "adequate" | "inadequate", note }`;
81
+ list every declared check once and no `requirements` (they are derived). The
82
+ export writes the inventory and map snapshots under `coverage/`, derives the
83
+ scenario and requirement rows and `delivery` (original versus agreed scope) and
84
+ validates them against the committed candidate; an `inadequate` judgment or a
85
+ failed required check is recorded honestly and blocks readiness. Migrated
86
+ project without strict coverage (the `Runtime` status line names a change): run
87
+ each executable check through the runtime on the clean candidate view —
68
88
  `node scripts/pincer-runtime.cjs check C-NN --candidate <sha> -- <command>` (one
69
89
  command per check, the command line as run; `npm test` stays one aggregate check) —
70
90
  then write the authored fields to a draft outside the evidence directory, for
@@ -77,8 +97,12 @@ run the pipeline, then present results.
77
97
  `result`, `provenance: runtime` and `attempt` from the attempts, labels review and
78
98
  visual checks `provenance: authored`, computes the digests and writes an evidence
79
99
  schema 2 manifest; it refuses a dirty tree, a HEAD that is not the candidate, a stub
80
- without an attempt, and a `passed` or `failed` command result written by hand. A
81
- tool that cannot run is recorded as an authored command check with
100
+ without an attempt, and a `passed` or `failed` command result written by hand. On a
101
+ project with change records the export also appends the evaluation to the change's
102
+ locator `.prd/evidence/changes/<id>.json` (the identity of this change's
103
+ evaluation; root `NOTES.md` stays the human summary and may be overwritten by a
104
+ later change's evaluation without losing this one) — commit the locator with the
105
+ evidence. A tool that cannot run is recorded as an authored command check with
82
106
  `result: unverified` and a note, as before. Legacy project (no change binding):
83
107
  author the schema 1 manifest as follows.
84
108
  - `checks/C-NN.log` — the command and a redacted summary or safe log of each
@@ -120,11 +144,13 @@ run the pipeline, then present results.
120
144
  evidence: .prd/evidence/prd-vN/<candidate>/manifest.json
121
145
  ---
122
146
  ```
123
- Then commit NOTES.md, the manifest and its listed artifacts — and nothing else —
147
+ Then commit NOTES.md, the manifest, its listed artifacts and (change records) the
148
+ evaluation locator — and nothing else —
124
149
  as `evaluate: PRD vN candidate <short sha>`. Status accepts this later commit only
125
- when its diff from the candidate is limited to `NOTES.md` and the evidence files
126
- the manifest lists; changes to source, tests, configuration, tickets, the PRD or
127
- other evaluations require reevaluation. Legacy notes without these references
150
+ when its diff from the candidate is limited to `NOTES.md`, the candidate's evidence
151
+ directories and evaluation locators; changes to source, tests, configuration,
152
+ tickets, the PRD or the change record (a lifecycle transition after the
153
+ candidate) require reevaluation. Legacy notes without these references
128
154
  do not establish readiness. Then describe what was built, what was cut
129
155
  and why, known issues, and what you'd do next with more time. Then a **Handover**
130
156
  section, written for the stranger who inherits this repo in six months: how to get
@@ -142,6 +168,9 @@ material choice not already authorized, and prepare the concrete proposal before
142
168
  asking. A decision the user delegated (for example "pick the architecture") does not
143
169
  need another approval when you exercise it, but a newly discovered consequential
144
170
  choice is surfaced before implementation. Record the authorization basis and the
145
- scope it covers in the PRD or the handover. An agent-written record or a status
171
+ scope it covers in the PRD or the handover, and on a project with change records as
172
+ a `change authorize` record (the user's words as the excerpt, or a `--delegated`
173
+ disposition with its basis). An agent-written record or a status
146
174
  field is not authenticated human approval. When resuming without the context that
147
- granted authorization, do not invent it — ask.
175
+ granted authorization, do not invent it — read the `resume` report; an authorization
176
+ it reports as `current` needs no repeat approval, and any other verdict is asked.
@@ -32,10 +32,29 @@ discovered consequential choice is surfaced before implementation.
32
32
  - Build the requirement map: for every `R-NN` in the PRD and each of its
33
33
  scenarios, name the ticket that owns the implementation and the executable
34
34
  check that exercises it, or an explicit review method when no executable check
35
- exists. Record the IDs in each ticket's Context as `Implements: R-NN, R-MM`.
36
- Enabling work that implements no requirement states its purpose in the ticket
37
- Objective. Resolve missing coverage and conflicting criteria with the user
38
- before implementation; do not start with an unmapped required scenario.
35
+ exists. Record the IDs in each ticket's Context as `Implements: R-NN, R-MM`
36
+ (a navigation aid). Enabling work that implements no requirement states its purpose
37
+ in the ticket Objective. Resolve missing coverage and conflicting criteria with
38
+ the user before implementation; do not start with an unmapped required scenario. On a change with change records, author the map once as
39
+ `.prd/coverage/<change id>.json` (coverage map schema 1, "Coverage map" in
40
+ `docs/runtime-contracts.md`). Start from
41
+ `node scripts/pincer-runtime.cjs coverage scaffold --change <change id>`: it prints
42
+ a read-only draft listing every live scenario exactly once with its requirement,
43
+ every ticket with its objective, its own `Implements:` claim and its Verification
44
+ text, and an `Unresolved` list of what is still unauthored. It writes nothing,
45
+ adopts nothing and decides nothing — it removes the transcription, not the
46
+ judgment, so a ticket's claim is material to check rather than a link, and no check
47
+ command, ticket role or scope disposition is ever invented. The draft is not a map:
48
+ it carries no `schema` key and `coverage adopt` refuses it. You author the real map
49
+ yourself, working through the draft's unresolved entries:
50
+ one `scenarios` row per `S-NN` naming its
51
+ implementing tickets and declared checks, every ticket of the change in
52
+ `tickets` as `implements` or `enables` (with a rationale), each check declared
53
+ once in `checks` with its kind, `required` flag and, for a command, the exact
54
+ command line and timeout, and a `scope` entry (`deferred` or `removed`) for a
55
+ scenario this change will not deliver, naming the decision that records the
56
+ user's choice. The map is authored work you edit by hand; the runtime never
57
+ rewrites it, and it validates it against the PRD's definitions.
39
58
  - Every ticket gets a runnable command in its Verification block — a fenced `bash`
40
59
  block that exits 0 only when the ticket is done. `scripts/pincer-ticket.sh verify`
41
60
  runs it verbatim and stamps the receipt that `done` requires, so it must be
@@ -85,12 +104,31 @@ existing authorization for the same scope and order.
85
104
  once it is resolved — only when step 4 surfaced a newly discovered consequential
86
105
  choice or a scope change the PRD does not cover. Then register the change when the
87
106
  `Runtime` status line says `legacy` and no ticket of this PRD carries legacy
88
- receipts: `node scripts/pincer-runtime.cjs register --prd .prd/prd-vN.md --authorization "<the user's approval, quoted>"`,
89
- then stage `.prd/changes/` and `.gitignore` (registration adds `.pincer/` to it) and
90
- commit them as `Register PRD vN`. The authorization
91
- text records the user's own words; running the command proves nothing by itself. A
92
- project whose tickets carry legacy receipts is migrated from `/pincer-code` after a
93
- preview, never here. Finish with:
107
+ receipts, or `changes` (the project already keeps change records):
108
+ `node scripts/pincer-runtime.cjs register --prd .prd/prd-vN.md` writes the change
109
+ record `.prd/changes/prd-vN.json` (retaining every earlier change); stage
110
+ `.prd/changes/` and `.gitignore` (registration adds `.pincer/` to it) and commit them
111
+ as `Register PRD vN`. Then record the user's actual approval against the agreement
112
+ the record binds — `node scripts/pincer-runtime.cjs change show prd-vN --json`
113
+ prints the agreement digest — with
114
+ `node scripts/pincer-runtime.cjs change authorize prd-vN --agreement <digest> --reference "<where the user said it>" --excerpt "<the user's approval, quoted>"`,
115
+ select it for this worktree (`node scripts/pincer-runtime.cjs change select prd-vN`)
116
+ and commit `.prd/changes/` as `Authorize PRD vN`. When the map was authored, adopt
117
+ strict coverage explicitly: `node scripts/pincer-runtime.cjs coverage adopt --preview --change prd-vN`
118
+ shows the inventory, the map digest and the agreement it records (it refuses an
119
+ incomplete map, naming the scenario or ticket); `--apply` writes the schema 3
120
+ record with a backup and grants nothing — record the user's approval of that
121
+ agreement with `change authorize` (the same instruction, if it named this
122
+ breakdown; a delegated disposition needs its basis) and commit `.prd/coverage/`
123
+ and `.prd/changes/` as `Adopt strict coverage for PRD vN`. A scenario the user
124
+ deferred or removed is a decision: `change decide --summary "<the choice>"`,
125
+ `--resolve D-NN` with the user's words naming the scenario, the `scope` entry in
126
+ the map, then `change authorize … --decision D-NN`. Never record a disposition
127
+ the user did not state; `coverage` reports `SCOPE_UNAUTHORIZED` until it is. The excerpt records the user's own
128
+ words; running the command proves nothing by itself, and a registration, a PRD
129
+ status or a passing check never becomes an authorization. A project whose tickets
130
+ carry legacy receipts, or that still holds a v0.5.0 binding (`Runtime change <id> ·
131
+ revision …`), is migrated from `/pincer-code` after a preview, never here. Finish with:
94
132
  "Tickets ready in `tickets/`. Run `/pincer-code` to start implementing."
95
133
 
96
134
  ## Authorization rule (shared by plan, narrow, code and evaluate)
@@ -100,6 +138,9 @@ material choice not already authorized, and prepare the concrete proposal before
100
138
  asking. A decision the user delegated (for example "pick the architecture") does not
101
139
  need another approval when you exercise it, but a newly discovered consequential
102
140
  choice is surfaced before implementation. Record the authorization basis and the
103
- scope it covers in the PRD or the handover. An agent-written record or a status
141
+ scope it covers in the PRD or the handover, and on a project with change records as
142
+ a `change authorize` record (the user's words as the excerpt, or a `--delegated`
143
+ disposition with its basis). An agent-written record or a status
104
144
  field is not authenticated human approval. When resuming without the context that
105
- granted authorization, do not invent it — ask.
145
+ granted authorization, do not invent it — read the `resume` report; an authorization
146
+ it reports as `current` needs no repeat approval, and any other verdict is asked.
@@ -91,8 +91,13 @@ concrete scope and architecture; do not repeat an approval already given for the
91
91
  In Requirements, assign stable `R-NN` IDs within the selected PRD: a revision
92
92
  keeps existing IDs and adds new ones, never renumbers. Every requirement has
93
93
  observable acceptance scenarios, the relevant failure paths, and the existing
94
- behavior it must preserve — `/pincer-narrow` maps each scenario to a ticket and
95
- a check, and `/pincer-evaluate` dispositions every ID.
94
+ behavior it must preserve, each written as a bold `- **S-NN:** …` item under its
95
+ requirement heading (the template's grammar; the runtime parses exactly these
96
+ definitions into the inventory that strict coverage tracks, and a requirement
97
+ without a scenario is invalid there) — `/pincer-narrow` maps each scenario to a
98
+ ticket and a check in the coverage map, and `/pincer-evaluate` dispositions every
99
+ ID. A supplied PRD keeps its own uppercase IDs (`REQ-1`, `AC-3`); only its
100
+ definition syntax is adapted, and the mapping table records what changed.
96
101
  2. Include optional sections when risk or the product context warrants them.
97
102
  3. Save to the next unused `.prd/prd-v{N}.md` (create `.prd/` if needed), with `N`
98
103
  matching the filename and frontmatter:
@@ -119,6 +124,9 @@ material choice not already authorized, and prepare the concrete proposal before
119
124
  asking. A decision the user delegated (for example "pick the architecture") does not
120
125
  need another approval when you exercise it, but a newly discovered consequential
121
126
  choice is surfaced before implementation. Record the authorization basis and the
122
- scope it covers in the PRD or the handover. An agent-written record or a status
127
+ scope it covers in the PRD or the handover, and on a project with change records as
128
+ a `change authorize` record (the user's words as the excerpt, or a `--delegated`
129
+ disposition with its basis). An agent-written record or a status
123
130
  field is not authenticated human approval. When resuming without the context that
124
- granted authorization, do not invent it — ask.
131
+ granted authorization, do not invent it — read the `resume` report; an authorization
132
+ it reports as `current` needs no repeat approval, and any other verdict is asked.
@@ -47,6 +47,24 @@ durable runtime-owned release record is later work.
47
47
  exits 1 and names the check). A fresh clone reports `local verification history
48
48
  unavailable; saved candidate evidence validated only`: state that limit in the
49
49
  verdict rather than claiming local verification.
50
+ - Change records (the `Runtime` line reads `changes`): the selected change is
51
+ `completed`, its authorization is `current` for the current agreement, and its
52
+ evaluation locator `.prd/evidence/changes/<id>.json` names the evaluated
53
+ candidate; `node scripts/pincer-runtime.cjs ready` reports `SELECTION_REQUIRED`,
54
+ `LIFECYCLE_BLOCKED`, `DECISION_REQUIRED`, `AUTHORIZATION_REQUIRED` or
55
+ `AGREEMENT_CHANGED` as failures. Release selects, activates and completes nothing;
56
+ historical evidence of a cancelled or superseded change is inspectable but never
57
+ release-ready.
58
+ - Strict coverage (the status line reads `Coverage strict …`): read
59
+ `node scripts/pincer-runtime.cjs coverage` — structure complete, every in-scope
60
+ scenario `delivered` on the evaluated candidate, deferrals and removals backed by
61
+ their decision and user authorization, `delivery` reported as original versus
62
+ agreed scope (never conflate them), adequacy `adequate`; the manifest is schema 3
63
+ and the `Provenance` line reads `runtime (schema 3)`. `ready` names
64
+ `COVERAGE_INCOMPLETE`, `SCOPE_UNAUTHORIZED`, `OBLIGATION_MISSING`,
65
+ `REVIEW_MISSING` and `ADEQUACY_REQUIRED` as failures. A change labeled
66
+ `Coverage unverified` did not adopt strict coverage: audit it under the rules
67
+ above and say so; never imply strict coverage was established.
50
68
  - Every file the manifest lists is tracked, and `git status --short` is empty before
51
69
  and after the audit.
52
70
  - Run the repository's candidate-wide release gate directly (`npm test`, or the
@@ -10,7 +10,19 @@ start of a session. Read-only: change nothing.
10
10
 
11
11
  ## Steps
12
12
 
13
- 1. Run `scripts/pincer-status.sh`. It reads the artifacts on disk (`.prd/`, `tickets/`,
13
+ 1. On a project with change records, start with
14
+ `node scripts/pincer-runtime.cjs resume --brief`. It is one read that answers the
15
+ question this command exists for: the selected change and its lifecycle, the
16
+ authorization verdict, the coverage label, ticket and attempt counts, every blocking
17
+ reason category with its count, and the exact next command — the full report's own,
18
+ copied, never recomputed. Reading the full `resume`, `status` and `coverage` reports
19
+ in sequence to find one next action costs several kilobytes to answer in a line, and
20
+ on a large change the full report grows with the work while the answer does not.
21
+ Read further only when the brief gives you a reason to: it ends with the `Detail`
22
+ line naming the command that prints every row it grouped.
23
+ 2. When you need the detail — a blocker you must act on, a ticket list, an attempt
24
+ history — run `scripts/pincer-status.sh`. It reads the artifacts on disk (`.prd/`,
25
+ `tickets/`,
14
26
  `NOTES.md`) and prints the PRD state and profile, every ticket with its state and
15
27
  clock-based elapsed time, what is blocked, wall-clock build time while a ticket is in
16
28
  progress or against an explicit user budget, the `Runtime` line (legacy receipts or
@@ -18,12 +30,24 @@ start of a session. Read-only: change nothing.
18
30
  candidate, any warnings (each readiness problem once), and the next command to run.
19
31
  `scripts/pincer-status.sh --json` prints one status object with reason codes and the
20
32
  next action for tooling; `node scripts/pincer-runtime.cjs ready [T-NN]` is the
21
- read-only gate.
22
- 2. Report in three lines: where the workflow is, what is in progress or blocked, and the
33
+ read-only gate. On a project with change records (`Runtime changes …`)
34
+ `node scripts/pincer-runtime.cjs resume` is the full report: it reports the selected change, its
35
+ lifecycle, agreement and authorization, decisions, references, tickets, attempts,
36
+ candidate, the authored handoff note and the next command, and never writes;
37
+ `change list` shows every retained change, `status --change <id>` and
38
+ `resume --change <id>` inspect another one without selecting it. `resume` is the
39
+ report; `change resume <id>` is the lifecycle operation. The `Coverage` line says
40
+ `strict` or `unverified`; on a strict change `node scripts/pincer-runtime.cjs coverage`
41
+ (and `impact` after an edit) name the exact scenario, ticket, check or decision
42
+ that is next — quote them rather than inferring coverage from the ticket list.
43
+ On a legacy project there is no change record to summarize, so `pincer-status.sh`
44
+ is the first read.
45
+ 3. Report in three lines: where the workflow is, what is in progress or blocked, and the
23
46
  next command. Quote the `Next` line as-is. When the `Runtime` line says `legacy`,
24
47
  add the register or migrate command it names as the step that precedes the next
25
- ticket (fresh project → `register`, legacy receipts → `migrate --preview`).
26
- 3. If a ticket is `in_progress`, read it and `git status`, then offer to resume it with
48
+ ticket (fresh project → `register`, legacy receipts → `migrate --preview`, a v0.5.0
49
+ binding → `migrate --preview`, no selection → `change select <id>`).
50
+ 4. If a ticket is `in_progress`, read it and `git status`, then offer to resume it with
27
51
  `/pincer-code T-{NN}`. If the script printed a warning, surface it — a done ticket
28
52
  without a receipt was marked by hand and needs `scripts/pincer-ticket.sh verify T-{NN}`.
29
53
  Never restore a ticket file from git to clear a warning; a failed attempt is a record.
@@ -160,12 +160,15 @@ function dangerousReason(source, depth = 0) {
160
160
  }
161
161
 
162
162
  const TICKET_PATH = /(^|[\\/])tickets[\\/]T-[0-9]+[^\\/]*\.md$/;
163
- // Runtime-owned state: local attempts under .pincer/ and change bindings under
164
- // .prd/changes/ are written only by pincer-runtime.cjs.
163
+ // Runtime-owned state: local attempts under .pincer/, change records under
164
+ // .prd/changes/ (with their agreement snapshots), evaluation locators under
165
+ // .prd/evidence/changes/ and the inventory/map snapshots a strict evaluation writes
166
+ // under .prd/evidence/prd-vN/<candidate>/coverage/ are written only by
167
+ // pincer-runtime.cjs. The coverage map .prd/coverage/<id>.json is authored by hand.
165
168
  // The runtime owns .pincer/runtime/ and .pincer/backups/ (and the directory as a
166
169
  // whole); .pincer/drafts/ is the agent's own scratch space for evidence drafts.
167
170
  const RUNTIME_PATH = /(^|[\\/])\.pincer(?:[\\/](?:runtime|backups)(?:[\\/]|$)|[\\/]?$)/;
168
- const BINDING_PATH = /(^|[\\/])\.prd[\\/]changes([\\/]|$)/;
171
+ const BINDING_PATH = /(^|[\\/])\.prd[\\/](?:changes|evidence[\\/]changes|evidence[\\/]prd-v[0-9]+[\\/][0-9a-f]{40}[\\/]coverage)([\\/]|$)/;
169
172
  const PROTECTED = ['status', 'started', 'last_check', 'verified', 'finished'];
170
173
 
171
174
  function ticketPath(value) {
@@ -212,7 +215,7 @@ function applyEdit(content, oldText, newText, replaceAll = false) {
212
215
  function guardEdits(tool, toolInput) {
213
216
  const file = toolInput.file_path;
214
217
  if (typeof file !== 'string') block(`${tool} payload must contain a string file_path.`);
215
- if (runtimePath(file)) block('runtime state (.pincer/) and change bindings (.prd/changes/) are written only by pincer-runtime.cjs.');
218
+ if (runtimePath(file)) block('runtime state (.pincer/), change records (.prd/changes/), evaluation locators and coverage snapshots (.prd/evidence/…/coverage/) are written only by pincer-runtime.cjs; the coverage map .prd/coverage/<id>.json is yours to edit.');
216
219
  if (!ticketPath(file)) return;
217
220
  const before = existingContent(file);
218
221
  if (tool === 'Write') {
@@ -240,11 +243,15 @@ function guardEdits(tool, toolInput) {
240
243
  function isExactPincerCall(source) {
241
244
  const commands = shellCommands(source).filter(command => command.words.length);
242
245
  if (commands.length !== 1 || commands[0].separator) return false;
246
+ // An output redirection is a write, whatever runs in front of it: fall through to
247
+ // ticketShellMutation so its target is tested like any other path. Without this,
248
+ // adding a verb to the list below silently opened a new carrier for it.
249
+ if (commands[0].operators.some(op => op === '>' || op === '>>')) return false;
243
250
  const { executable, args } = commandParts(commands[0]);
244
251
  let words = [executable, ...args];
245
252
  if (['bash', 'sh', 'node'].includes(words[0])) words = words.slice(1);
246
253
  if (/pincer-runtime\.cjs$/.test(words[0] || '')) {
247
- return ['start', 'verify', 'done', 'bind', 'register', 'migrate', 'recover', 'check', 'evidence'].includes(words[1]);
254
+ return ['start', 'verify', 'done', 'bind', 'register', 'migrate', 'recover', 'check', 'evidence', 'change', 'resume'].includes(words[1]);
248
255
  }
249
256
  if (!/pincer-ticket\.sh$/.test(words[0] || '')) return false;
250
257
  const action = words[1];
@@ -335,7 +342,7 @@ function ticketShellMutation(source, depth = 0) {
335
342
  if (viaXargs && ['checkout', 'restore', 'clean'].includes(sub.name) && stdinPathspec) return true;
336
343
  }
337
344
  const hasTicket = words.some(ticketPath) || /(^|[\s'"`])tickets[\\/]T-[0-9]+[^\s'"`]*/.test(source) ||
338
- words.some(runtimePath) || /(^|[\s'"`=])\.pincer(?:[\\/](?:runtime|backups)(?:[\\/]|[\s'"`]|$)|[\\/]?(?:[\s'"`]|$))/.test(source) || /(^|[\s'"`=])\.prd[\\/]changes([\\/]|[\s'"`]|$)/.test(source);
345
+ words.some(runtimePath) || /(^|[\s'"`=])\.pincer(?:[\\/](?:runtime|backups)(?:[\\/]|[\s'"`]|$)|[\\/]?(?:[\s'"`]|$))/.test(source) || /(^|[\s'"`=])\.prd[\\/](?:changes|evidence[\\/]changes|evidence[\\/]prd-v[0-9]+[\\/][0-9a-f]{40}[\\/]coverage)([\\/]|[\s'"`]|$)/.test(source);
339
346
  if (!hasTicket) continue;
340
347
  if (command.operators.some(op => op === '>' || op === '>>')) return true;
341
348
  if (['rm', 'mv', 'cp', 'install', 'truncate', 'touch', 'tee', 'ed', 'ex'].includes(executable)) return true;
@@ -41,10 +41,17 @@ and never renumbered: a revision keeps existing IDs and adds new ones. Tickets
41
41
  name the IDs they implement and evaluation dispositions every ID.
42
42
 
43
43
  #### R-01 — short title
44
- - Scenario: an observable acceptance scenario (given / when / then, or a command
45
- and its expected output). Add one line per scenario.
46
- - Failure path: what invalid input or the relevant failure produces.
47
- - Preserve: existing behavior this must not change (brownfield).
44
+ - **S-01:** an observable acceptance scenario (given / when / then, or a command
45
+ and its expected output). One bold `S-NN` item per scenario, defined under its
46
+ requirement heading; every requirement has at least one.
47
+ - **S-02:** Failure path: what invalid input or the relevant failure produces.
48
+ - **S-03:** Preserve: existing behavior this must not change (brownfield).
49
+
50
+ Scenario IDs are stable like requirement IDs. The runtime reads exactly this
51
+ grammar (a `##`..`####` heading `R-NN — title`, bold `- **S-NN:** text` items
52
+ under it, continuation lines indented); mentions in prose, tables and fenced
53
+ examples define nothing. On a change with strict coverage the parsed inventory is
54
+ what the coverage map and the evaluation must cover completely.
48
55
 
49
56
  When the user supplied a PRD, keep its meaning and its existing requirement IDs.
50
57
  If its structure needs adapting to this template, add a `Requirement mapping`
@@ -38,7 +38,7 @@ sandbox_mode = "workspace-write" # writes confined to the repo; no network by
38
38
 
39
39
  The ticket scripts work here unchanged (`scripts/pincer-ticket.sh
40
40
  start|verify|done T-NN` and `scripts/pincer-status.sh`); they are thin wrappers
41
- around `scripts/pincer-runtime.cjs`, so Node.js 18+ is required.
41
+ around `scripts/pincer-runtime.cjs`, so Node.js 22+ is required.
42
42
  Without a Pincer Codex hook adapter, the rule in `AGENTS.md` carries the weight
43
43
  of stopping hand-edited ticket state; `$pincer-status` warns about any ticket
44
44
  marked done without a receipt.