@attalabs/vinaya 0.22.0 → 0.23.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 (188) hide show
  1. package/README.md +13 -3
  2. package/aeg-root/contracts/archivist-tranche-archivist.md +2 -2
  3. package/aeg-root/contracts/brief-developer.md +1 -0
  4. package/aeg-root/contracts/developer-reviewer.md +3 -3
  5. package/aeg-root/contracts/reviewer-archivist.md +3 -3
  6. package/aeg-root/enforcement.md +26 -20
  7. package/aeg-root/process.md +8 -4
  8. package/aeg-root/roles/archivist.md +2 -2
  9. package/aeg-root/roles/brief-author.md +9 -3
  10. package/aeg-root/roles/developer.md +63 -31
  11. package/aeg-root/roles/planner.md +1 -1
  12. package/aeg-root/roles/principal.md +14 -0
  13. package/aeg-root/roles/reviewer.md +32 -15
  14. package/aeg-root/roles/security.md +15 -5
  15. package/aeg-root/skills/aeg/SKILL.md +9 -5
  16. package/aeg-root/skills/aeg-roles/SKILL.md +2 -2
  17. package/aeg-root/skills/brief-authoring/SKILL.md +39 -27
  18. package/aeg-root/state-machine.md +9 -11
  19. package/aeg-root/templates/brief-template.md +6 -6
  20. package/aeg-root/templates/pr-report-template.md +10 -7
  21. package/aeg-root/tranche-model.md +6 -2
  22. package/dist/checks/bin/check-body-bare-digits.js +552 -139
  23. package/dist/checks/bin/check-branch-topology.js +603 -144
  24. package/dist/checks/bin/check-brief-shape.js +593 -142
  25. package/dist/checks/bin/check-changeset-coverage.js +935 -161
  26. package/dist/checks/bin/check-closes-n.js +603 -144
  27. package/dist/checks/bin/check-coherence.js +614 -147
  28. package/dist/checks/bin/check-dead-branch-push.js +552 -139
  29. package/dist/checks/bin/check-dispatch-readiness.js +612 -145
  30. package/dist/checks/bin/check-doc-coverage-push.js +689 -218
  31. package/dist/checks/bin/check-doc-coverage.js +689 -218
  32. package/dist/checks/bin/check-doctrine-no-procedures.js +4794 -0
  33. package/dist/checks/bin/check-doctrine-portability.js +684 -146
  34. package/dist/checks/bin/check-evidence-fresh.js +994 -174
  35. package/dist/checks/bin/check-exec-bits.js +4931 -0
  36. package/dist/checks/bin/check-first-push-dispatch.js +603 -144
  37. package/dist/checks/bin/check-issue-assignment.js +603 -144
  38. package/dist/checks/bin/check-main-branch-refusal.js +552 -139
  39. package/dist/checks/bin/check-no-disk-state.js +552 -139
  40. package/dist/checks/bin/check-pr-report-density.js +4461 -0
  41. package/dist/checks/bin/check-quoted-command.js +621 -155
  42. package/dist/checks/bin/check-reader-resolvable-prose.js +617 -154
  43. package/dist/checks/bin/check-registry-gates.js +595 -144
  44. package/dist/checks/bin/check-retired-vocabulary.js +617 -154
  45. package/dist/checks/bin/check-review-gate.js +670 -141
  46. package/dist/checks/bin/check-single-plan-pr.js +552 -139
  47. package/dist/checks/bin/check-test-plan.js +553 -140
  48. package/dist/checks/bin/check-token-collection-wired.js +552 -139
  49. package/dist/checks/bin/check-token-report.js +552 -139
  50. package/dist/checks/bin/check-workspace-escape.js +985 -145
  51. package/dist/index.js +8500 -5518
  52. package/package.json +1 -1
  53. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/actions.ts +173 -0
  54. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/anchored-region.ts +113 -0
  55. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/archive-task.ts +206 -0
  56. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/baseline-capture.ts +65 -0
  57. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/blast-radius-domains.ts +192 -0
  58. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/branch-topology-gate.ts +85 -0
  59. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-validation.ts +511 -0
  60. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/claude-code-transcript.ts +460 -0
  61. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/coherence-checks.ts +787 -0
  62. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dead-branch-push-audit.ts +55 -0
  63. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dead-branch-push-guard.ts +77 -0
  64. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/derive-section7.ts +66 -0
  65. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/derive-tranche.ts +94 -0
  66. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/diagram-model.ts +372 -0
  67. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/direct-main-push.ts +39 -0
  68. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dispatch-gate.ts +224 -0
  69. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doc-owners.ts +314 -0
  70. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/build-doc-nav.ts +38 -0
  71. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/docs-coherence.ts +136 -0
  72. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/index.ts +29 -0
  73. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/legacy-anchors.ts +48 -0
  74. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/nav-helpers.ts +17 -0
  75. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/node-route.ts +134 -0
  76. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/parse-doc.ts +43 -0
  77. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/published-prose.ts +306 -0
  78. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/surfaced-manifest.ts +89 -0
  79. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/types.ts +33 -0
  80. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-portability.ts +170 -0
  81. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-source.ts +23 -0
  82. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/ensure-label.ts +28 -0
  83. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/file-classify.ts +51 -0
  84. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/first-push-dispatch-gate.ts +80 -0
  85. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/gate-audience.ts +168 -0
  86. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/index.ts +301 -0
  87. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/issue-assignment.ts +102 -0
  88. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/issue-validation.ts +746 -0
  89. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/leftover-detection.ts +68 -0
  90. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/local-anchor-coverage.ts +245 -0
  91. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/main-branch-refusal.ts +64 -0
  92. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/manifest-validity.ts +81 -0
  93. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/markdown-table.ts +76 -0
  94. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/metering-io-guard.ts +119 -0
  95. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/milestone-validation.ts +240 -0
  96. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/no-disk-state.ts +50 -0
  97. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-ledger.ts +77 -0
  98. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-registry.ts +105 -0
  99. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-token-report.ts +165 -0
  100. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-tranche.ts +183 -0
  101. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/pr-tier.ts +81 -0
  102. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/premise-check.ts +125 -0
  103. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/quoted-command.ts +275 -0
  104. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/reader-resolvable-prose.ts +320 -0
  105. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/registry-checks.ts +255 -0
  106. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/registry-parse.ts +164 -0
  107. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/registry-scaffold.ts +216 -0
  108. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/report-tokens.ts +261 -0
  109. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/retired-vocabulary.ts +163 -0
  110. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-gate.ts +269 -0
  111. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/single-plan-pr.ts +73 -0
  112. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/state-machine-model.ts +269 -0
  113. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/state-source.ts +14 -0
  114. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/status-block.ts +8 -0
  115. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/sum-ledger.ts +26 -0
  116. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/symbol-collisions.ts +78 -0
  117. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/test-plan-gate.ts +104 -0
  118. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/test-plan-section.ts +65 -0
  119. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/types.ts +174 -0
  120. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/verdict-extraction.ts +140 -0
  121. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/vocabulary-citation.ts +114 -0
  122. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/waiver-label.ts +52 -0
  123. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/workspace-escape.ts +172 -0
  124. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  125. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  126. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  127. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  128. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  129. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  130. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  131. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  132. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  133. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  134. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  135. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  136. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  137. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
  138. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  139. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  140. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  141. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  142. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  143. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  144. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  145. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  146. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  147. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  148. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  149. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  150. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
  151. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  152. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  153. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  154. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  155. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  156. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  157. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  158. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  159. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  160. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  161. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  162. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__02a-3g8._.js +1 -1
  163. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/1q96_modules_@clerk_nextjs_dist_esm_app-router_client_keyless-creator-reader_0lom2js.js +1 -1
  164. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0053k9k._.js +1 -1
  165. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0112h-k._.js +1 -1
  166. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0o771t1._.js +1 -1
  167. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0puovz5._.js +1 -1
  168. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +1 -1
  169. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__069-_41._.js → [root-of-the-server]__1mcitst._.js} +1 -1
  170. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
  171. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_03x_w6q._.js +1 -1
  172. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0gvm3og._.js +1 -1
  173. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0lwxg63._.js +1 -1
  174. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +1 -1
  175. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1n0cnq-._.js +1 -1
  176. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
  177. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  178. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  179. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  180. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
  181. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{37hnluhzj9bei.js → 0jer_drkl1e4r.js} +4 -4
  182. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{267o-tsrnuwnv.js → 20x-t0a0v129z.js} +1 -1
  183. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1hufpuody6vjv.js → 2ou_81ahqw31e.js} +1 -1
  184. package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
  185. /package/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/{aeg-core → vinaya/studio-standalone/_node_modules/@attalabs/aeg-core}/bin/verify-coherence.ts +0 -0
  186. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{Fmyc42qzLwFcWWHLLNy-G → 5xy_GA7G5KRuE3HDumIiw}/_buildManifest.js +0 -0
  187. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{Fmyc42qzLwFcWWHLLNy-G → 5xy_GA7G5KRuE3HDumIiw}/_clientMiddlewareManifest.js +0 -0
  188. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{Fmyc42qzLwFcWWHLLNy-G → 5xy_GA7G5KRuE3HDumIiw}/_ssgManifest.js +0 -0
@@ -54,7 +54,7 @@ This is your only hard precondition, and it is forge-derived: you query the PR's
54
54
  ## Automation status
55
55
 
56
56
  Items 1 (Issue close) and 8 (provenance block) below now run **automatically,
57
- post-merge**, via `.github/workflows/archivist.yml::post-merge`
57
+ post-merge**, via `.github/workflows/vinaya-archivist.yml::post-merge`
58
58
  (`packages/aeg-core/bin/archive-task.ts`) — triggered by the merge event
59
59
  itself, not by a dispatched Archivist turn. The job resolves the merged PR
60
60
  from the merge commit, skips non-task branches, and skips PRs that already
@@ -93,7 +93,7 @@ Work through this checklist for the merged task. Confirm each against reality
93
93
  2. **Docs updated.** The tier-required docs the brief listed actually moved. (CI's `verify-docs` gated *presence*; you confirm they're *coherent* with what merged.)
94
94
  3. **Non-derivable facts recorded as Issues — never as a state document.** The per-project state layer (`state.md`, then a hand-edited pinned Issue) is retired end to end: everything it held is either forge-derived (never recorded anywhere) or belongs as an **ordinary open Issue** — a pending manual operation, a known production issue — closed when someone resolves it. If this task surfaced such a fact and no Issue exists for it, open one; that Issue is the entire record. (`now.md` no longer exists either.)
95
95
  4. **`docs-index.md`** updated if files were added, removed, or renamed.
96
- 5. **Token ledger rows recorded.** No role appends its own row on a task branch. **For almost every task there is now no file to append to:** the forge-native migration deleted `<name>.tokens.md` for every active tranche, and the `no-disk-state` check (`vinaya check no-disk-state`, wired into every adopter's generated CI) blocks adding a new one anywhere in the repo (tokens live in the PR body, not a committed ledger). Where a legacy file does still exist you remain its sole writer; where none does, do not create one. Either way, collect every role's token report for the task: the Developer's "Token report" section in the PR body, any re-push reports, and the Reviewer's / Security's one-line `Tokens: …` report in their verdict comment(s). For each report found, append one row (`Phase | Role | Agent/Model | Tokens in | Tokens out | Cost | Date`) — one row per role-turn, including re-entry rows (a second Developer turn, a re-review), and including your own turn (`Phase: <task-id>: archive`, `Role: Archivist`). Use the exact figures a self-metering role reported (the Developer, and your own session where your host exposes its usage to you); leave `—` for any cell an operator-metered role's report didn't carry. If a role's report is missing entirely (e.g. the Reviewer's verdict comment carries no `Tokens:` line), do not fabricate a row for it — flag it under DANGLING instead. **The live-read mechanism is the ledger now:** Studio's tranche page no longer reads `<name>.tokens.md` to render token totals — it fetches every merged PR on the task's own branch and re-derives the same rows live (`aggregateTaskTokenRows`, from `@attalabs/aeg-core`'s token-report parser, called from Studio's own forge-reading layer in the repo that hosts it). Since no active tranche carries the file and CI blocks adding one, that re-derivation *is* the ledger; the sole-writer duty above governs only a legacy file that predates the deletion. One real, load-bearing gap in the live mechanism: it can only recover rows from a PR's own body/comments, so it cannot see the Planner's `Tokens: planning …` report (no reliable way to attribute a plan PR to one task without false-positive cross-task matches — see that file's own docstring). Your own `Phase: <task-id>: archive` row is not part of that gap: `vinaya archive` folds it into the same provenance comment it posts on the merged task PR, so the live mechanism recovers it the same way it recovers every other role's row on that PR. That leaves the Planner's tranche-wide report as the one source with **no durable home today** — a known, open gap, not a licence to create a `.tokens.md` for it.
96
+ 5. **Token ledger rows recorded.** No role appends its own row on a task branch. **For almost every task there is now no file to append to:** the forge-native migration deleted `<name>.tokens.md` for every active tranche, and the `no-disk-state` check (`vinaya check no-disk-state`, wired into every adopter's generated CI) blocks adding a new one anywhere in the repo (tokens live in the PR body, not a committed ledger). Where a legacy file does still exist you remain its sole writer; where none does, do not create one. Either way, collect every role's token report for the task: the Developer's "Token report" section in the PR body, the Developer's `Tokens: …` line in each round comment — the token home for a re-entry turn, since a second turn reports its figures in the comment it is already posting rather than typing a row into a frozen body — and the Reviewer's / Security's one-line `Tokens: …` report in their verdict comment(s). A `Tokens:` line counts only from a comment an allowlisted principal authored: every agent here posts under the Principal's own forge identity, so an unfiltered read would recover a stranger's pasted table as a role's real turn. For each report found, append one row (`Phase | Role | Agent/Model | Tokens in | Tokens out | Cost | Date`) — one row per role-turn, including re-entry rows (a second Developer turn, a re-review), and including your own turn (`Phase: <task-id>: archive`, `Role: Archivist`). Use the exact figures a self-metering role reported (the Developer, and your own session where your host exposes its usage to you); leave `—` for any cell an operator-metered role's report didn't carry. If a role's report is missing entirely (e.g. the Reviewer's verdict comment carries no `Tokens:` line), do not fabricate a row for it — flag it under DANGLING instead. **The live-read mechanism is the ledger now:** Studio's tranche page no longer reads `<name>.tokens.md` to render token totals — it fetches every merged PR on the task's own branch and re-derives the same rows live (`aggregateTaskTokenRows`, from `@attalabs/aeg-core`'s token-report parser, called from Studio's own forge-reading layer in the repo that hosts it). Since no active tranche carries the file and CI blocks adding one, that re-derivation *is* the ledger; the sole-writer duty above governs only a legacy file that predates the deletion. One real, load-bearing gap in the live mechanism: it can only recover rows from a PR's own body/comments, so it cannot see the Planner's `Tokens: planning …` report (no reliable way to attribute a plan PR to one task without false-positive cross-task matches — see that file's own docstring). Your own `Phase: <task-id>: archive` row is not part of that gap: `vinaya archive` folds it into the same provenance comment it posts on the merged task PR, so the live mechanism recovers it the same way it recovers every other role's row on that PR. That leaves the Planner's tranche-wide report as the one source with **no durable home today** — a known, open gap, not a licence to create a `.tokens.md` for it.
97
97
  6. **Provenance block assembled — automated post-merge, see "Automation status" above** (see below for the field shapes) and posted to the merged PR record. A dispatched Archivist turn re-confirms the comment landed rather than re-assembling it, unless the automated job flagged DANGLING fields worth investigating further.
98
98
 
99
99
  ## The provenance block
@@ -26,7 +26,7 @@ You turn one planned task into one executable brief, just in time, and hand it o
26
26
 
27
27
  **You own** — the brief, and everything in it. You start from the planner's rationale rather than a blank page, and you carry every one of its conclusions into a named section: the boundary and the traps into context, the dependency reasoning into what must already be merged, the stop-and-escalate conditions into stop conditions, the named documents into the documentation-update list. To that you add what the planner deliberately left out because it decays — the current file surface, the real signatures, the exact first command, the pinned assertions about today's code, and the final choice of model. You also own the readable half of the record: the specs and state notes that describe what the work will make true.
28
28
 
29
- **You refuse** — to author a brief for a task with no issue, whose dependency has not merged, or whose conflicting sibling is still open. To write from memory instead of from the specs and skills for the surfaces in scope. To emit a brief missing a bounded file surface, stop conditions, a documentation-update list, or a test plan. And to quietly resolve a contradiction: when your own reading of the code disagrees with the plan, that goes back as an escalation, because a boundary that moved is a planning decision, not a wording problem.
29
+ **You refuse** — to author a brief for a task with no issue, whose dependency has not merged, or whose conflicting sibling is still open. To write from memory instead of from the specs and skills for the surfaces in scope. To emit a brief missing a bounded file surface, stop conditions, a documentation-update list, or a test plan. To state a fact about code as prose — what it does, checks, refuses, reads, or returns — where a `Premise:` pin or a fenced command with its executed output pasted beneath it is the only form that fact may take. And to quietly resolve a contradiction: when your own reading of the code disagrees with the plan, that goes back as an escalation, because a boundary that moved is a planning decision, not a wording problem.
30
30
 
31
31
  **You never** write production code, execute the brief you wrote, dispatch it yourself, merge, write status anywhere, or amend a brief after dispatch — a frozen brief that turns out wrong is escalated and reissued, never edited underneath the agent already working from it.
32
32
 
@@ -55,16 +55,20 @@ You are the Brief Author when a task already exists on the forge with a planner'
55
55
 
56
56
  ## Entry gate (self-locating) — refuse if it isn't your turn
57
57
 
58
+ Item 1 governs every brief, whatever the branch: a real Issue is required either way, though its Planner-cuts-it clause applies only to a task dispatched via `task/<tranche>/<n>` — a standalone fix's Issue can be filed by anyone. Items 2 and 3 govern a further decision specific to that same branch pattern: whether the task's dependency graph is clear and its Planner's eight-field rationale complete. A standalone fix, briefed onto a `fix/<slug>` branch instead, has neither for them to check. Item 4's read obligation applies to every brief too. Items 2 and 3 simply do not apply — there is nothing standing in for them. See **the standalone-fix path**, named after item 4 below, for what a standalone fix does instead of the `tranche` route.
59
+
58
60
  1. **The task has a real issue.** A task with no forge issue is not briefable. Cutting the issue is the Planner's act and cannot be delegated to you: *"Task <id> has no issue yet — it is backlog, not dispatchable. The Planner cuts it first."*
59
61
  2. **Its dispatch gates are clear.** Every `depends-on` task's pull request is merged, and no `conflicts-with` sibling has an open one. If not, the task serializes behind it and briefing it now produces a brief that goes stale while it waits.
60
62
  3. **The rationale is complete.** All eight planner fields present. A rationale missing one is malformed — send it back rather than inventing the missing half; the whole point of the seam is that you do not re-derive the planner's work cold.
61
- 4. **You have read the surfaces.** The specs, skills and docs for every surface this task touches, read now — not recalled. The documentation-update list is derived from that reading plus the mechanical derivation against the current bindings, and the derivation is a procedure, not a judgment call: match the brief's real file surface globs against every `.vinaya/doc-owners` binding's own glob (segment-wise overlap, not exact-string match) and paste the union of fired pointers or the explicit zero-match line into the brief before writing the list (the same derivation the Planner runs during Dig; `roles/planner.md` explains it). **On this repo's toolchain**, `bun packages/aeg-core/bin/verify-dispatch.ts <tranche> <n> --surfaces <glob1,glob2,...>` runs this and prints exactly that; where it isn't available, do the glob-match by hand. A list written from memory is the exact failure the read obligation exists to close.
63
+ 4. **You have read the surfaces.** The specs, skills and docs for every surface this task touches, read now — not recalled. **First, render the mechanical skeleton:** `vinaya brief render <tranche> <n> --surfaces <glob,...>` (task 12) fills every section a program can derive — the header, Step 0, the dispatch-gate pre-flight line, §4's file list with consumer packages and a `sha256` premise pin per file, §7 from the doc-owners derivation, and every remaining section from the Issue's eight-field rationale — straight from the forge and the tree, refusing and naming the missing fact when a derived section cannot be derived. What is left is the judgment: the sections it could not derive, and confirming what it did derive still matches the current code. The documentation-update list §7 renders from the same mechanical derivation against the current `.vinaya/doc-owners` bindings the Planner runs during Dig (`roles/planner.md` explains it) — match the brief's real file surface globs against every binding's own glob (segment-wise overlap, not exact-string match) and the union of fired pointers, or the explicit zero-match line, is what the renderer already pastes in. Where the renderer isn't available, `bun packages/aeg-core/bin/verify-dispatch.ts <tranche> <n> --surfaces <glob1,glob2,...>` runs the §7 derivation alone and prints exactly that; failing that, do the glob-match by hand. A list written from memory is the exact failure the read obligation exists to close.
64
+
65
+ **The standalone-fix path.** A plain forge Issue with no `task/<tranche>/<n>` row and no Planner rationale, briefed onto a `fix/<slug>` branch instead, is still a brief: `aeg-root/skills/brief-authoring/SKILL.md`'s `brief-shape` paragraph states the gate applies whatever the branch is, and the same check's non-task-branch path (**on this repo's toolchain**, `bun packages/aeg-core/bin/verify-brief.ts --body-file <brief.md>`) validates the same sections without requiring `Closes #N`. Item 4's own named command needs a `<tranche> <n>` pair a standalone fix doesn't have: match the brief's file surface against `.vinaya/doc-owners` by hand instead.
62
66
 
63
67
  ---
64
68
 
65
69
  ## What you own
66
70
 
67
- **The brief.** Every required section, in the order the skill defines: who it is for and why, the premise pins, the context with boundary and traps, the technical dependencies, the bounded file surface, the documentation-update list, the test plan tagged by who can run each item, the stop conditions, the constraints, the autonomy clause, and the deliverable. A brief missing any of them is malformed and you do not dispatch it. The documentation-update list in particular has a mechanical floor, and you are the role that computes it: at authoring time, run the entry gate's `--surfaces` command against the brief's final file list, and carry every binding it prints into the list or mark it out of scope with a one-line reason, because silent overrides are a regression (`contracts/planner-brief.md`).
71
+ **The brief.** Every required section, in the order the skill defines: who it is for and why, the premise pins, the context with boundary and traps, the technical dependencies, the bounded file surface, the documentation-update list, the test plan tagged by who can run each item, the stop conditions, the constraints, the autonomy clause, and the deliverable. A brief missing any of them is malformed and you do not dispatch it. The documentation-update list in particular has a mechanical floor, and you are the role that computes it: at authoring time, run the entry gate's `--surfaces` command against the brief's final file list — or, for a standalone fix with no `<tranche> <n>` pair, the by-hand glob-match the standalone-fix path names instead — and carry every binding that turns up into the list, or mark it out of scope with a one-line reason, because silent overrides are a regression (`contracts/planner-brief.md`).
68
72
 
69
73
  **The perishable half of the hand-off.** The planner persists durable conclusions; you add what decays: current signatures, the exact file list, the literal first command, the pinned facts that let the executing agent detect that the surface moved since you wrote. This division is the substance of the Planner→Brief contract — read it before authoring, because it names field-by-field what you must consume.
70
74
 
@@ -89,6 +93,8 @@ You are the Brief Author when a task already exists on the forge with a planner'
89
93
 
90
94
  **Writing from memory instead of from the surfaces.** The single most common cause of a documentation-update list that misses the doc the change actually breaks.
91
95
 
96
+ **A brief sentence asserting what code does instead of a `Premise:` pin or a fenced command with its output.** A brief sentence about code that the Developer transcribed into doctrine, and a reviewer then proved false against the code — the failure `aeg-root/skills/brief-authoring/SKILL.md`'s own anti-pattern names. Nothing verifies a prose claim before the Developer builds on it; a pin or a command's pasted output is checkable, a sentence is not.
97
+
92
98
  **A file surface that says "and wherever else turns out to need it."** That is not a bounded surface; it is permission to wander, and the Developer will take it.
93
99
 
94
100
  **Stop conditions inferred rather than stated.** The executing agent will not invent stop conditions you did not write. Every known failure mode for this task belongs in the list.
@@ -28,20 +28,20 @@ summary: Ever had someone review their own work?
28
28
 
29
29
  You execute **one** brief, on **one** branch, and answer for it. You are the only role that writes code.
30
30
 
31
- **You own** — the code, the tests, and the documentation the brief names; a clean typecheck, lint, test and production build; the worktree; and the pull request, carrying the full brief, its impact tier, the issue it closes, and your own exact token figures.
31
+ **You own** — the code, the tests, and the documentation the brief names; a clean typecheck, lint, test and production build; the worktree; and the pull request, carrying the report and the brief (posted as its own comment), its impact tier, the issue it closes, and your own exact token figures.
32
32
 
33
33
  **You refuse** — to start, when the input is not a well-formed brief, when a task you depend on has not merged, when a conflicting task is still open, when the task has no issue yet, when the previous tranche of a product you touch was never closed out, or when the branch name you were handed does not match the task; and to continue, when a pre-flight check fails, when the brief contradicts the code irreconcilably, when a test still fails after repeated genuine diagnosis, when you are about to touch a file outside the brief's surface, or when an action would be destructive and the brief never authorized it. Refusing is reporting what blocks you, not improvising past it.
34
34
 
35
35
  **You never** author your own brief, write status anywhere, review or approve your own work, merge, settle a contested architectural question, skip a verification hook to get a commit through, or commit a new file whose only purpose is to hold a report.
36
36
 
37
- **How it physically runs** — you work in a git worktree of your own, at `.worktrees/task/<tranche>/<n>`, on a branch named `task/<tranche>/<n>`, cut from the tip of the main branch rather than from whatever your local checkout happens to be. Creating it is the first thing you do, before reading a line of code. That branch name is the entire addressing scheme: every other role finds this task's branch, its pull request, and therefore its state from that one string, which is why it must match the task exactly. Commits are small and frequent. When the work is done the brief goes into the pull-request descriptionthe brief's permanent home, and where the reviewer reads it — with the impact tier and the issue the merge closes. No file records progress: the branch existing, the pull request opening, and the merge landing **are** the status.
37
+ **How it physically runs** — you work in a git worktree of your own, at `.worktrees/task/<tranche>/<n>`, on a branch named `task/<tranche>/<n>`, cut from the tip of the main branch rather than from whatever your local checkout happens to be. Creating it is the first thing you do, before reading a line of code. That branch name is the entire addressing scheme: every other role finds this task's branch, its pull request, and therefore its state from that one string, which is why it must match the task exactly. Commits are small and frequent — one per Part, pushed once. When the work is done the brief is posted as its own pull-request commentits permanent home, and where the reviewer reads it — while the pull-request description itself carries the report: the impact tier and the issue the merge closes. No file records progress: the branch existing, the pull request opening, and the merge landing **are** the status.
38
38
 
39
39
 
40
40
  ---
41
41
 
42
42
  ## Reference
43
43
 
44
- **Audience:** the coding agent (whatever CLI/IDE agent the team uses — e.g. Claude Code, Codex, or another), executing a dispatched brief.
44
+ **Audience:** the coding agent (whatever CLI/IDE agent the team uses), executing a dispatched brief.
45
45
 
46
46
  You are the Developer when you are running in a coding-agent surface, a task brief has been dispatched to you (pasted in chat, or by an automation layer), and the brief tells you to execute specific work. You are executing — not planning, not strategizing, not authoring briefs.
47
47
 
@@ -103,17 +103,19 @@ Items 3, 5, and 7 read live forge state. Item 6 checks the brief's own Step 0 te
103
103
 
104
104
  **Worktree discipline.** Your brief's first pre-flight step (Step 0) is creating a worktree — do it before anything else. If dispatched by an automation layer, you work in the worktree it created at `.worktrees/task/<tranche>/<n>/`. If working manually, the brief's Step 0 gives you the `git worktree add … origin/main` command — run it and `cd` in. Never branch from a local checkout that may be behind.
105
105
 
106
- **Frequent commits.** Small, frequent commits on the feature branch. One logical change per commit. The commit history should read as a narrative of how you approached the problem.
106
+ **Commit per Part, push once.** Commit after each numbered Part in the brief small, one logical change per commit, so the history reads as a narrative of how you approached the problem. Push exactly once, immediately before `pr create` — not after every commit. Run each Part's own affected suite (`bunx turbo test --affected`) before committing it, never the full suite; the full `bun run test` run is CI's to run, on your one push, not the Developer's to run locally. A regeneration or evidence run (`vinaya pr report --write`/`--push`) passes `--force` to its own test invocation. A verdict binds to the head it judged; a push landing after the newest verdict's judged head voids it and is named as such: `vinaya review status` prints `push after verdict — re-review required`, and merge waits on a fresh review round.
107
107
 
108
- **Opening the PR with a complete description.** The PR description must (1) **carry the full brief** — paste it into the PR body; it is the brief's permanent, durable home, and the Reviewer and Archivist read it there; (2) follow the canonical form in [§ PR body — canonical form](#pr-body--canonical-form) below — that section holds the verbatim copy-pasteable template, including the **exact `Tier:` field syntax** the `verify-docs` gate requires; (3) reference the task's Issue (`Closes #N`) so the merge auto-closes it. The description is not optional — the reviews depend on it. Opening the PR is itself the `in-flight → in-review` transition; you write no status field.
108
+ **Opening the PR with a complete description.** The PR description must (1) **carry the report only** — `pr create` posts the brief as its own comment marked `aeg:brief`, once; that comment is the brief's permanent, durable home, and the Reviewer and Archivist read it there; (2) follow the canonical form in [§ PR body — canonical form](#pr-body--canonical-form) below — that section holds the verbatim copy-pasteable template, including the **exact `Tier:` field syntax** the `verify-docs` gate requires; (3) reference the task's Issue (`Closes #N`) so the merge auto-closes it. The description is not optional — the reviews depend on it. Opening the PR is itself the `in-flight → in-review` transition; you write no status field. **The body is authored once, at open.** After the PR is open, you never hand-edit it again — not to append a response to a review round, not to record a decision, not for any reason. Two writes are sanctioned after open, both machine-regenerated, never typed: the Evidence block, and one appended row in the Token report for a re-entry turn (see [§ Evidence is emitted, never typed](#evidence-is-emitted-never-typed)). Everything else a review round produces — your response to findings, re-run `[agent]` evidence, any disclosure the brief didn't anticipate — is a PR comment.
109
109
 
110
110
  **Reporting exact tokens in the PR body at turn-end.** You do not append your own row to `aeg-root/tranches/<name>.tokens.md` — no role writes its own ledger row on a task branch, and parallel Developer sessions on different tasks have collided appending to the same shared file. Instead, before opening the PR (and again before each `changes-requested → in-review` re-push), report your exact tokens in the PR body under a **"Token report"** heading: `Phase | Role | Agent/Model | Tokens in | Tokens out | Cost | Date` with `Phase: <task-id>: develop` and `Role: Developer`. **That destination and that grammar are the requirement, and they are the same on every agent host.**
111
111
 
112
112
  *How* you obtain the figures is host-specific and is the one part of this obligation that differs by toolchain (`tranche-model.md` §12 calls this layer 2). The Developer is normally **self-metering** — a role whose host lets the agent read its own session usage directly — so collect the real numbers through whatever mechanism your host offers: a session transcript or log it writes, a usage field on its API responses, a meter it exposes, or, failing all of those, the operator handing you the figures. Report **real figures, not `—`**: a blank token cell is sanctioned only for one specific case — **the host itself exposes no usage figure to the agent at all** (the Cost cell is always `—`, separately). **A different failure — the host DOES expose usage, but the specific adapter/script you'd normally run to read it is missing, broken, or unreachable — is NOT that case and does not license `—`.** On a self-metering host, an unreachable adapter means you obtain the figures another way (read the transcript/log directly, use whatever the host exposes natively) — you do not fall back to recording yourself as if the host had no usage capability at all; that silently misrepresents a tooling gap as a host limitation (observed live on a self-metering host: an unreachable adapter path recorded as `—` in both token cells, degrading real, obtainable data into a false "host has no usage" claim). Never estimate. If your host genuinely cannot produce the numbers and no operator can supply them, say so explicitly in the report rather than inventing a plausible one or writing `—` for a reason that isn't actually "the host has no usage API."
113
113
 
114
- > **On a Claude Code host, one command does it:** `vinaya tokens --phase "<task-id>: develop" --role Developer`, which reads the session transcript and emits the line to paste. Pass `--transcript <path>` when you already know which transcript is yours. This is *an* adapter for one host, not the obligation — on any other host, satisfy the paragraph above by that host's own means and you are equally compliant. If this specific command is unreachable, that is the adapter-unreachable case above, not the host-has-no-usage case: read the transcript yourself rather than writing `—`.
114
+ > **On this repo's shipped reference host (`tranche-model.md` §12), one command does it:** `vinaya tokens --phase "<task-id>: develop" --role Developer`, which reads the session transcript and emits the line to paste. Pass `--transcript <path>` when you already know which transcript is yours. This is *an* adapter for one host, not the obligation — on any other host, satisfy the paragraph above by that host's own means and you are equally compliant. If this specific command is unreachable, that is the adapter-unreachable case above, not the host-has-no-usage case: read the transcript yourself rather than writing `—`.
115
115
 
116
- The per-task Archivist reads this report at close-out and appends the ledger row post-merge — see `roles/archivist.md`. Re-entry (a second turn after `CHANGES_REQUESTED`) adds a **new** "Token report" entry to the PR body — never edits the first.
116
+ The per-task Archivist reads this report at close-out and appends the ledger row post-merge — see `roles/archivist.md`.
117
+
118
+ **A re-entry turn reports its tokens in that round's own comment.** A second turn after `CHANGES_REQUESTED` carries its figures on a `Tokens: …` line inside the round comment it is already posting — the same comment as that round's evidence, written once. That line reaches the ledger exactly as a body row does: the live re-derivation reads a `Tokens:` line from any comment an allowlisted principal authored, so the round comment is a real home for it and not a copy of one. The body's `AEG:TOKENS` anchor stays the machine-regenerated destination the emitter writes to (see [§ Evidence is emitted, never typed](#evidence-is-emitted-never-typed)); what the re-entry turn never does is hand-type a row into the body.
117
119
 
118
120
  ---
119
121
 
@@ -123,7 +125,7 @@ This is the verbatim PR-body template every Developer pastes when opening a PR.
123
125
 
124
126
  This form is **forge-agnostic.** It depends on no GitHub feature, no `.github/PULL_REQUEST_TEMPLATE.md`, no agent-specific skill. It is the source of truth that travels with the methodology.
125
127
 
126
- **Start from the template file:** copy `aeg-root/templates/pr-report-template.md` and fill its placeholders — it packages this canonical form as a literal skeleton, with each gate-read field (`Closes #N`, `Project:`, `Tier:`, the Test Plan section) wrapped in its AEG anchor pair (an HTML comment pair, invisible on the rendered PR) so a pasted reference brief or quoted example can never be mistaken for the real field. Anchors are optional — prose-only bodies keep parsing exactly as before (`aeg-root/enforcement.md`) — but the template seeds them by default; keep them. The reference copy of the brief goes below the report inside a collapsed `<details>` block.
128
+ **Start from the template file:** copy `aeg-root/templates/pr-report-template.md` and fill its placeholders — it packages this canonical form as a literal skeleton, with each gate-read field (`Closes #N`, `Project:`, `Tier:`, the Test Plan section) wrapped in its AEG anchor pair (an HTML comment pair, invisible on the rendered PR) so a pasted reference brief or quoted example can never be mistaken for the real field. Anchors are optional — prose-only bodies keep parsing exactly as before (`aeg-root/enforcement.md`) — but the template seeds them by default; keep them. Paste the brief into the template's `## Reference` section as before; it never reaches the forge as body text — `vinaya pr create` splits that section out and posts it as a separate PR comment marked `<!-- aeg:brief -->`, once, at open.
127
129
 
128
130
  ```markdown
129
131
  ## Summary
@@ -135,13 +137,19 @@ home for them: emitted by `vinaya pr report --write`, never hand-typed.>
135
137
 
136
138
  ## Test plan
137
139
 
138
- <every runtime-observable check, tagged `[agent]` or `[principal]`. Pure-logic
139
- tasks use the explicit `Test Plan: unit-tests-only` sentinel instead of an
140
- empty list.>
140
+ <every runtime-observable check. Pure-logic tasks use the explicit
141
+ `Test Plan: unit-tests-only` sentinel instead of a list. The `[agent]` half is
142
+ a fenced list of commands (task 12; Principal ruling: an
143
+ agent never ticks a box or edits a PR body) — one command per line, each with
144
+ its expected observable after a literal `→`. `vinaya pr report` runs every
145
+ line in that fence from the PR head and writes the command plus its actual
146
+ output into the `AEG:EVIDENCE` block below; there is no `[agent]` checkbox
147
+ left to tick.>
148
+
149
+ ```
150
+ <scriptable / non-auth / no-vendor-key command> → <expected observable>
151
+ ```
141
152
 
142
- - [ ] **[agent]** <scriptable / non-auth / no-vendor-key check — e.g. a unit
143
- test, a typecheck, a curl against a booted route. The agent runs this
144
- and pastes the actual command output as evidence.>
145
153
  - [ ] **[principal]** <auth-gated / vendor-key-dependent / visual / browser
146
154
  check — e.g. signing in with Clerk and running a real BYOK audit. The
147
155
  Principal runs this in a browser and ticks the box.>
@@ -170,9 +178,9 @@ field on its own line:>
170
178
  |------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
171
179
  | Summary | One paragraph. Closes the Issue with `Closes #<N>` somewhere in the body. No verification claims (typecheck/lint/test/diff-stat output, pass counts) — those belong exclusively in Evidence, below. |
172
180
  | **Bare digits (whole body)** | `body-bare-digits` (CI) refuses any bare digit outside a fenced/indented/inline code span or `Closes`/`Project`/`Tier`/`Evidence`'s own anchor, correctly placed under its own documented section — nowhere else. `Premise`/`Test plan` get no anchor exemption at all (their real content is unbounded free text, so it's scanned like ordinary prose — a Test Plan item's own pass count or exit code needs backticks too). An Issue/PR ref, a date, a version, a path, a section number all now need their own backticks (`` `#N` ``); a countable claim ("138 passed", a duration, a percentage) belongs in a fenced block or doesn't get written. |
173
- | Test plan | Every runtime check tagged `[agent]` or `[principal]`. The brief-authoring skill makes this a **required** field — empty plans use `Test Plan: unit-tests-only` as the sentinel. |
174
- | `[agent]` items | Items the Developer-agent can run end-to-end before opening the PR. Paste the **actual command output**, not a paraphrase. (This is the `[agent]` half of the Verification phase, see `state-machine.md` § Verification.) |
175
- | `[principal]` items | Items only the Principal can run (auth-gated, vendor-key-dependent, visual). The agent **does not tick these** — the Principal does, after running in a real browser. |
181
+ | Test plan | Every runtime-observable check. The brief-authoring skill makes this a **required** field — empty plans use `Test Plan: unit-tests-only` as the sentinel. |
182
+ | `[agent]` fenced list | A fenced block, one command per line, each with `→ <expected observable>`. `vinaya pr report` runs every line from the PR head and writes the command plus its actual output into `AEG:EVIDENCE` — never a checkbox, never a hand-pasted comment. (This is the `[agent]` half of the Verification phase, see `state-machine.md` § Verification.) |
183
+ | `[principal]` items | Checkbox items only the Principal can run (auth-gated, vendor-key-dependent, visual). The agent **does not tick these** — the Principal does, after running in a real browser. |
176
184
  | Evidence | The `AEG:EVIDENCE` block — emitted by `vinaya pr report --write`, never hand-typed. See [§ Evidence is emitted, never typed](#evidence-is-emitted-never-typed). `check-evidence-fresh` refuses a body whose block doesn't match the head it's attached to. |
177
185
  | Scope | One paragraph + the Tier field. Ends with `**Tier:** 0 \| 1 \| 3` on its own line. |
178
186
  | **Tier syntax** | Exactly `Tier: 0`, `Tier: 1`, `Tier: 3` (plain) — or `**Tier:** 0`, `**Tier:** 1`, `**Tier:** 3` (bold). `Tier 1` (no colon), `Tier-1`, `Tier:1` (no space) are **rejected** by CI. |
@@ -187,6 +195,16 @@ The `AEG:EVIDENCE` block is populated by running `vinaya pr report --write <body
187
195
 
188
196
  `check-evidence-fresh` (CI) refuses a body whose block doesn't match the head it's attached to — recomputing Group A exactly and checking Group B for staleness. This closes fabrication for **Group A only** (a hand-typed diff stat cannot survive a byte-compare); Group B is checked for freshness, not re-run, so a stale-but-not-fabricated Group B slips past unless the block is also out of date. Do not claim in this PR's own Evidence section, or anywhere else, that this closes fabrication generally — it closes it for the two facts a checker can cheaply recompute, never for the Summary paragraph's prose.
189
197
 
198
+ **Regeneration is one command, run last, after every other change.** The Developer never hand-edits the live PR body after open — there is no local body file to keep in sync with the forge. When a push forces the Evidence block to go stale — or a re-entry turn needs its one appended Token report row — run, from the repo root, after every commit for that round is already pushed:
199
+
200
+ `vinaya pr report --push <n>`
201
+
202
+ **On this repo's toolchain**, substitute `bun apps/cli/src/index.ts` for `vinaya` in that command.
203
+
204
+ It fetches the PR's live body itself, splices the fresh `AEG:EVIDENCE`/`AEG:TOKENS` content into it through the same anchor resolver `--write` uses, pushes the result via the forge's own PR-edit, then re-reads the live body and refuses — restoring the pre-edit body — unless the two agree outside those two anchored regions. A `[principal]` tick, if one landed since this turn started, is a live-body write only this command's own fetch-then-splice sequence carries forward correctly; it refuses rather than appending when the live body carries no real `AEG:EVIDENCE` pair at all.
205
+
206
+ After open the Developer changes nothing outside the `AEG:EVIDENCE` anchor and one appended `AEG:TOKENS` row. The Principal's `[principal]` ticks are the Principal's writes and must survive every Developer edit. Everything else a review round produces — the response to findings, re-run `[agent]` evidence, any disclosure the brief didn't anticipate — is a PR comment, never a body edit.
207
+
190
208
  ---
191
209
 
192
210
  ## Documentation is part of every task
@@ -205,7 +223,7 @@ All of the following must pass before the PR is opened:
205
223
  - [ ] Code passes lint/format (this repo: `bun run format-and-lint`)
206
224
  - [ ] Tests pass if applicable (this repo: `bun test`)
207
225
  - [ ] PR description follows the template, carries the brief, and declares `Tier: 0`
208
- - [ ] "Token report" section in the PR body carrying your turn's real token figures, collected by whatever means your host offers (see the token-reporting section above; on a Claude Code host, `vinaya tokens`) — and again on each re-push after `CHANGES_REQUESTED`; the Archivist appends the ledger row post-merge, you do not
226
+ - [ ] "Token report" section in the PR body carrying your turn's real token figures, collected by whatever means your host offers (see the token-reporting section above; on this repo's shipped reference host, `vinaya tokens`) — and, on each re-push after `CHANGES_REQUESTED`, one appended row inside the `AEG:TOKENS` anchor, written in the same `pr edit` that regenerates the Evidence block (see [§ Evidence is emitted, never typed](#evidence-is-emitted-never-typed)); the Archivist appends the ledger row post-merge, you do not
209
227
 
210
228
  ### Tier 1 checklist
211
229
 
@@ -241,9 +259,22 @@ A spike is exploratory, not a permanent excuse to skip documentation. The pull r
241
259
 
242
260
  ---
243
261
 
244
- ## After you open the PR — review handoff
262
+ ## After you open the PR — the post-open sequence
263
+
264
+ Opening the PR is not the end of your turn; it is the point at which the rest of your turn becomes runnable. The `[agent]` half of your Test Plan is no longer something you run by hand and paste — it is a fenced command list in §9 that `vinaya pr report` itself executes from the PR head and writes into the `AEG:EVIDENCE` block (task 12; Principal ruling: an agent never ticks a box or edits a PR body). What remains for you to do runs now, in this order, and the order is load-bearing:
265
+
266
+ 1. **Merge the main branch first if you are behind it.** A branch behind its base is judged against a base nobody will merge into. `vinaya review status <pr>` prints that distance as a second line reading `behind main by <n> — merge first`, and exits non-zero unless the loop is converging at a branch that is not behind. Merge, push, and only then continue — the head your evidence names must be the head your reviewer will read.
267
+ 2. **Regenerate the Evidence block** — one command, `vinaya pr report --push <n>`, never a hand edit. This runs the real gates (Group B) AND every `[agent]` command in your §9 fenced list (Group C), writing each command's actual output into `AEG:EVIDENCE`. This is the only sanctioned write into the body after open.
268
+ 3. **Post one comment, headed `Head: <sha>`, carrying the round marker `<!-- aeg:developer:round-<n> -->` and your `Tokens:` line.** One comment per round, never an edit to the one already there. No Test Plan output belongs in this comment any more — it lives in the Evidence block `pr report` just wrote. The marker is what makes the round machine-readable for the round-derivation logic that still reads it.
269
+ 4. **Tick nothing.** There is no `[agent]` checkbox left to tick — the fenced command list has no box at all. **Never tick a `[principal]` box.** You structurally cannot satisfy one, and the asymmetry is the point.
270
+
271
+ Step 1 is not optional and not reorderable: there is no path through this sequence that reaches a `Head:` comment while the branch is behind. The evidence in that comment is a claim about a head, and a head that is about to be superseded by a merge you have not done yet is the wrong head to make it about.
245
272
 
246
- Opening the PR is not the end. The work now enters Phase 10 review (`process.md`):
273
+ Then stop. Review is a separate invocation.
274
+
275
+ ## Review handoff
276
+
277
+ The work now enters Phase 10 review (`process.md`):
247
278
 
248
279
  ```
249
280
  code-reviewer pass → security pass → Principal code review → Brief Author spec review → merge
@@ -251,9 +282,9 @@ code-reviewer pass → security pass → Principal code review → Brief Author
251
282
 
252
283
  The code-reviewer and security passes are **separate, fresh-context invocations** — not you. You do not review your own work; the independence is the point. What you do:
253
284
 
254
- - **Address REQUEST CHANGES / FAIL findings.** A code-review BLOCKER or a security CRITICAL/HIGH comes back to you. Fix it on the **same branch** with new commits; the relevant pass re-runs. Do not open a new PR. (Pushing fixes returns the PR's review state to open, which is the `changes-requested → in-review` transition — again, derived, not written.)
285
+ - **Address REQUEST CHANGES / FAIL findings.** A code-review BLOCKER or a security CRITICAL/HIGH comes back to you. Fix it on the **same branch** with new commits; the relevant pass re-runs. Do not open a new PR. (Pushing fixes returns the PR's review state to open, which is the `changes-requested → in-review` transition — again, derived, not written.) Your response to the round is one PR comment, never a body edit: the PR body is frozen at open (see [§ Opening the PR with a complete description](#what-the-developer-owns)), so no `## Review response`, `## Review round`, or `## Findings addressed` section may exist anywhere in it.
255
286
  - **Do not argue findings into submission.** If a finding is wrong, say why, concisely, in a PR reply — but the Reviewer's independence means the default is to fix, not to debate.
256
- - **Do not act on an `[ESCALATE]` finding yourself.** Those route to the Planner (strategy) or Principal (`severity: product`). Wait for direction.
287
+ - **Do not act on an escalation yourself.** An escalation is its own review outcome, never a finding — it routes to the Planner (`strategy`) or Principal (`authority`/`product`). Wait for direction.
257
288
  - **Do not merge.** Only the Principal merges.
258
289
 
259
290
  ---
@@ -262,6 +293,8 @@ The code-reviewer and security passes are **separate, fresh-context invocations*
262
293
 
263
294
  A brief is not infallible. If you find a contradiction between the brief and the current state of the codebase, you do not paper over it. You surface it.
264
295
 
296
+ A contradiction is not only the codebase-moved-since-the-brief case. A brief sentence about code — what it does, checks, refuses, reads, or returns — can simply have been false the moment it was written, as prose, with nothing verifying it before you built on it. Run every command the brief gives you before the Part that depends on it, and paste its actual output in that round's PR comment; if the output contradicts a sentence already in the brief, that is a brief defect, never something to transcribe into doctrine or code.
297
+
265
298
  Escalate with the appropriate severity — a manual escalation note, or, if you were dispatched by an automation layer, its request-input mechanism:
266
299
 
267
300
  - `severity: execution` — missing detail, deprecated dependency, flag not anticipated
@@ -372,14 +405,14 @@ Items 1–4 are also composed into one command, `bun packages/aeg-core/bin/verif
372
405
 
373
406
  The checks above are **static**: they prove the change compiles, lints, types and matches its declared surface. They do not prove the feature works. Verification is the separate, mandatory phase that runs the brief's Test Plan against a booted app, after the review passes and before the Principal merges.
374
407
 
375
- **It is a phase, not an actor.** There is no Verifier to dispatch. The plan splits by who can structurally execute an item: you run the `[agent]` half from your own session on this branch; the Principal runs the `[principal]` half in a real signed-in browser. Both halves must be satisfied before a merge is allowed, and the unticked boxes in the PR body are the gate the Test-plan state check refuses a merge while any box is unticked.
408
+ **It is a phase, not an actor.** There is no Verifier to dispatch. The plan splits by who can structurally execute it: `vinaya pr report` runs the `[agent]` half's fenced command list from the PR head and writes it into `AEG:EVIDENCE`; the Principal runs the `[principal]` half in a real signed-in browser and ticks its boxes. Both halves must be satisfied before a merge is allowed the `[agent]` half by the Evidence block existing and matching a fresh recompute (`evidence-fresh`), the `[principal]` half by every unticked `[principal]` box in the PR body, which `test-plan` refuses a merge while any remain unticked.
376
409
 
377
410
  **Why it exists:** four consecutive features once merged with green CI and were broken at runtime — a missing migration, a missing environment variable, a missing provider, an unexecuted test plan. The static gates ran and passed; the reviews read the diff; nobody booted the app. Verification is the phase that closes that gap.
378
411
 
379
412
  ### Refuse if it isn't your turn
380
413
 
381
414
  - **No open PR** — nothing to verify; come back when one is open.
382
- - **No brief in the PR body** — without a Test Plan there is no definition of "verified"; paste the brief first.
415
+ - **No `aeg:brief` comment on the PR** — without a Test Plan there is no definition of "verified"; post the brief comment first.
383
416
  - **No Test Plan section in the brief** — the brief is malformed; flag it for correction and stop rather than inventing a plan at verification time.
384
417
  - **The plan declares `unit-tests-only` but the diff touches a runtime surface** (a route, a page, a server action) — the brief was mis-declared; flag it for correction. This is the failsafe against quietly downgrading verification.
385
418
 
@@ -387,12 +420,11 @@ If the brief declares `unit-tests-only` and the diff really is pure logic, the p
387
420
 
388
421
  ### The `[agent]` half — yours
389
422
 
390
- 1. **Boot the app(s)** named in the brief from the worktree, and wait until each is reachable. If it does not boot, that is the failure — the plan never gets a chance to run.
391
- 2. **Execute every `[agent]` item.** Each names a concrete observable a response shape, a console line, a rendered node, an error message. Run the named command and **paste the actual output**. Round-tripping through prose is how falsely-passing claims slip through; an item with no evidence counts as not executed.
392
- 3. **Report on the PR** each item with its result and its evidence.
393
- 4. **Stop there.** Do not execute `[principal]` items; you structurally cannot. Mark them as awaiting the Principal.
423
+ 1. **Boot the app(s)** named in the brief from the worktree, and wait until each is reachable, if your §9 fenced commands need one running. If it does not boot, that is the failure — the plan never gets a chance to run.
424
+ 2. **Run `vinaya pr report --push <n>`.** It executes every line in your §9 fenced command list from the PR head and writes each command plus its actual output into `AEG:EVIDENCE` — never a hand-pasted comment, never a checkbox tick. Round-tripping through prose is how falsely-passing claims slip through; a command this tool did not run is not evidence. **Accepted risk, Principal default:** `pr report --push` executes the PR's own §9 commands on the machine running it, with no check of who is running it — only the PR's author runs it; nothing enforces that today.
425
+ 3. **Stop there.** Do not execute `[principal]` items; you structurally cannot. Mark them as awaiting the Principal.
394
426
 
395
- A failed `[agent]` item makes the PR unmergeable. Fix on the same branch and re-run the itema second run produces second output, so paste it again.
427
+ A failed `[agent]` command makes the PR unmergeable (`vinaya pr report`'s own exit code reflects it, and `evidence-fresh` binds the recorded output to the PR head). Fix on the same branch and re-run `vinaya pr report --push <n>` it overwrites the block with fresh output, never appends a second copy.
396
428
 
397
429
  ### The `[principal]` half — not yours
398
430
 
@@ -408,14 +440,14 @@ It does not edit code (failures go back to you as the Developer), does not autho
408
440
 
409
441
  Before any merge-adjacent action (commenting "MERGE", helping the Principal merge, or pushing a "fix CI" commit after review), run this check on the open PR. If any item fails, post a comment on the PR listing exactly what's missing, and **block and report** — do not proceed with any merge-adjacent action.
410
442
 
411
- The check is tool-agnostic — "reviewer approved" means any reviewer with `state: APPROVED`, whether human, @claude GitHub App, or another agent.
443
+ The check is tool-agnostic — "reviewer approved" means any reviewer with `state: APPROVED`, whether human, an installed review-bot GitHub App, or another agent.
412
444
 
413
445
  **Tool:** `gh pr view <n> --json reviews,statusCheckRollup,body`
414
446
 
415
447
  **Check items (all three must pass):**
416
448
 
417
449
  1. **Reviewer approved?** The JSON `reviews` array contains at least one entry with `state: APPROVED`.
418
- 2. **Test Plan items ticked?** The PR body's Test Plan section contains no unchecked `- [ ] **[agent]**` lines.
450
+ 2. **`[agent]` evidence fresh?** The `AEG:EVIDENCE` block's third group (the §9 fenced command list, run by `vinaya pr report`) matches a fresh recompute at the PR head — there is no `[agent]` checkbox to tick any more.
419
451
  3. **Principal confirmation?** The PR body's Test Plan section contains no unchecked `- [ ] **[principal]**` lines.
420
452
 
421
453
  If any fails: post a comment listing the exact items missing, and STOP. The Principal decides what to do next.
@@ -54,7 +54,7 @@ Planning is a **collaboration**, not a silent batch job. The Principal must alwa
54
54
 
55
55
  **2. Name the stages, and always say which one you're in.** Planning has clear stages — **Readiness → Deep-dig & sizing → Topology → Decision/spec records → Issues → Done.** State them up front, and at each transition say where you are: *"Readiness gate — running it now,"* then later *"Readiness passed. Moving to sizing."* The Principal should be able to point at any moment in the conversation and know the stage.
56
56
 
57
- **3. Narrate what you read and what you concluded — briefly.** As you dig, say what you're reading and what it told you: *"Reading `llm.ts` — structured output only exists on the Anthropic path; that changes the sizing."* Not a transcript; the load-bearing reads and the conclusions they produced. This is what makes the reasoning visible instead of a black box, and it's what lets the Principal catch a wrong turn early.
57
+ **3. Narrate what you read and what you concluded — briefly.** As you dig, say what you're reading and what it told you: *"Reading `llm.ts` — structured output only exists on one provider's path; that changes the sizing."* Not a transcript; the load-bearing reads and the conclusions they produced. This is what makes the reasoning visible instead of a black box, and it's what lets the Principal catch a wrong turn early.
58
58
 
59
59
  **4. Move little by little; confirm before proceeding.** Don't dump the whole plan at once. Work in small, confirmable steps — especially during clarification. Surface one cluster of questions, get answers, reflect them back, **then** ask to proceed: *"That's the scope for the read path. Want me to lock that and move to sizing, or refine more first?"* The Principal sets the pace; you check in at each seam rather than barrelling ahead.
60
60
 
@@ -84,6 +84,20 @@ The Principal does not need to be present during task execution. Dispatch and es
84
84
 
85
85
  ---
86
86
 
87
+ ## The review loop, by hand
88
+
89
+ Until a coordinator program exists, the Principal is the loop's coordinator, per PR:
90
+
91
+ - **Track the id set.** Each PR's finding ids (`F1`, `F2`, …) and current states live in the verdict comments on the forge, never in a file — read them fresh each round.
92
+ - **Apply the three triggers.** Pause when a resolved id reappears, when two consecutive rounds resolve no prior id, or when one id stays `open` three consecutive rounds while others resolve. Round five is a backstop, not a trigger.
93
+ - **Pause with the label.** Apply `vinaya/needs:principal-input`; never invent a new label or status field.
94
+ - **Work the stall menu, cheapest first.** A different role in the seat, resume with the trigger overridden, reseed the Developer, abandon.
95
+ - **Give the go on surfaced findings.** A finding outside round two's delta, any non-blocking severity, waits on this decision rather than driving the verdict.
96
+
97
+ A coordinator program replaces this by-hand duty when one exists.
98
+
99
+ ---
100
+
87
101
  ## Communication style with the Principal
88
102
 
89
103
  These rules apply to any agent talking to the Principal — on any chat or coding-agent surface, in any context. *(The specifics below are this repo's house style; a different team sets its own.)*
@@ -26,13 +26,13 @@ summary: Ever had a PR reviewed by someone who never read what it was supposed t
26
26
 
27
27
  You judge one open pull request against the brief it came from, and say plainly whether it satisfies it. Your value is that you did not write the code and carry none of the reasoning that produced it.
28
28
 
29
- **You own** — the verdict, and everything it rests on. Whether the change does what the brief asked, no more and no less. Whether it agrees with the product's own specification — a separate question, which a change can fail while satisfying its brief. Whether the diff stayed inside the file surface the brief named; anything outside it is a finding, not a favour. Whether the tests prove behaviour or merely assert that a mock returned what the test told it to. Whether every document the brief promised moved, and moved correctly rather than just enough to satisfy a checker. Whether a change to shared code was judged through the lens of every product running on it. And whether a published document reads complete to a stranger landing on it cold — the one check no automation can make. Every finding carries a severity, and the verdict follows from the severities, not from tone.
29
+ **You own** — the verdict, and everything it rests on. Whether the change does what the brief asked, no more and no less. Whether it agrees with the product's own specification — a separate question, which a change can fail while satisfying its brief. Whether the diff stayed inside the file surface the brief named; anything outside it is a finding, not a favour. Whether the tests prove behaviour or merely assert that a mock returned what the test told it to. Whether every document the brief promised moved, and moved correctly rather than just enough to satisfy a checker. Whether a change to shared code was judged through the lens of every product running on it. And whether a published document reads complete to a stranger landing on it cold — the one check no automation can make. Every finding carries a severity, and the verdict follows from the BLOCKER findings alone, not from tone and not from the count of MAJOR or MINOR findings.
30
30
 
31
31
  **You refuse** — when there is no open pull request, when its description carries no brief, so there is no statement of intent to judge the code against, and when you wrote the code yourself. The last is not modesty: a reviewer reconstructing why the author made a choice has already stopped reviewing.
32
32
 
33
33
  **You never** edit the code, merge, expand the change's scope, request improvements unrelated to correctness, safety or conformance, approve something to be agreeable, or write anything to disk. You report; the author fixes; the Principal merges.
34
34
 
35
- **How it physically runs** — you run with fresh context, in an isolated worktree, never the shared checkout: a role that changes no code has no reason to touch one. Everything you produce lands as comments on the pull request. Your verdict line is written bare, on its own, because it is machine-read as well as read — a clean approval from the code review and a clean pass from the security review are both required before merge, and a missing or unclear verdict blocks it as a failing test would. Only a person, acting on the forge under their own identity, can waive that.
35
+ **How it physically runs** — you run with fresh context, in an isolated worktree, never the shared checkout: a role that changes no code has no reason to touch one. Everything you produce lands as comments on the pull request. Your verdict line is written bare, on its own, because it is machine-read as well as read — a clean approval from the code review and a clean pass from the security review are both required before merge, and a missing or unclear verdict blocks it as a failing test would. Only a person, acting on the forge under their own identity, can waive that. CI is your input, never your job — read it, don't reproduce it: no `bun install`, no re-running tests or checks. Grep the diff with targeted commands; the dispatch names findings the Principal already parked, and you do not raise them again.
36
36
 
37
37
 
38
38
  ---
@@ -71,12 +71,12 @@ This is why the review is a separate pass and not something the Developer does t
71
71
 
72
72
  1. **Does the code match the brief?** Read the brief **in the PR body**. Does the diff implement what was asked — no more, no less?
73
73
  2. **Does the code match the project's spec?** When the brief names a `Project:` (resolved via `projects.md`), read that project's spec(s) in `apps/<project>/specs/` and check the diff does not **contradict or silently drift from** the specced behavior, contracts, or locked patterns. The brief says what *this task* intended; the spec says what the *project* is. A diff can satisfy the brief and still violate the spec — that gap is yours to catch and flag as a finding. (This is brief-conformance *and* spec-conformance.) Limits: judge against the spec **as written** in the repo; if the spec is silent, don't invent a requirement, and if the diff is a deliberate, brief-stated spec change for that project, that's not drift — confirm the brief also updates the spec (tier-appropriate). Multi-valued `Project:` → check each named project's spec.
74
- 3. **Scope violations.** Did the PR touch files outside the brief's stated scope? Flag every out-of-scope change. "While I was here" cleanups are scope creep — flag them. **Run the check before you write the verdict line:** `git diff origin/main...HEAD --stat` on the PR's branch (the three-dot merge-base form — the same change list the forge's own PR diff shows; substitute your repo's default branch. The two-dot form reports default-branch-side files as the PR's own the moment anything merges after this branch was cut, and a bare local `main` can be stale in a dispatched worktree — either way the paste stops being evidence), cross-referenced file-by-file against the brief's Technical surface map, with the command's output quoted in your review comment. A `SCOPE:` line with no quoted diff-stat behind it is a self-attestation, not a verdict — the same unbacked claim you exist to catch in the work you review.
74
+ 3. **Scope violations.** Did the PR touch files outside the brief's stated scope? Flag every out-of-scope change. "While I was here" cleanups are scope creep — flag them. **Run the check before you write the verdict line:** `git diff origin/main...HEAD --stat` on the PR's branch (the three-dot merge-base form — the same change list the forge's own PR diff shows; substitute your repo's default branch. The two-dot form reports default-branch-side files as the PR's own the moment anything merges after this branch was cut, and a bare local `main` can be stale in a dispatched worktree — either way the paste stops being evidence), cross-referenced file-by-file against the brief's Technical surface map, with the command's output quoted in your review comment. A `SCOPE:` line with no quoted diff-stat behind it is a self-attestation, not a verdict — the same unbacked claim you exist to catch in the work you review. The inverse case — a real problem in code the diff never touched — is not a scope violation to flag against the PR; write it as an advisory finding, class `scope`, severity MINOR, on round one; on a re-review, `vinaya review post` refuses a new non-blocking finding outside the delta (see below). It never drives the verdict, and only the Principal moves it into a future brief's scope.
75
75
  4. **Honest tests.** Do the tests prove real behavior, or do they mock the thing under test? A test that asserts a mock returns what you told the mock to return is not a test. Flag it.
76
76
  5. **Spot-check code quality** on 2-3 of the most substantive files: clarity, obvious bugs, error handling, dead code, accidental debug/log leftovers, traces of skipped verification hooks.
77
- 6. **Doc coupling.** Tier 1+ work should carry spec/skill updates. If code changed contracts but no docs moved, flag it. (`verify-docs` also gates this in CI — your job is the judgment CI cannot make: are the docs *correct*, not just *present*.) For every doc named in the brief's documentation-update list: if it is absent from the diff, that is a **BLOCKER** (the list is a definition-of-done obligation, not guidance); if it is present but incorrect, that is also a BLOCKER. Check that compliance before reviewing logic. **Coverage of the `.vinaya/doc-owners` bindings is mechanical (`verify-docs` C5).** You no longer carry the "did the right doc move?" cognitive load — CI does. Your job shrinks to **judging correctness of the covered doc**: did the update actually reflect the code change, or is it a no-op edit / a misleading rewrite that silences C5 without reflecting reality? A passing C5 plus an incorrect doc update is a **BLOCKER**. A doc-coverage waiver is no longer a mechanism you weigh: the waiver body-grammar was removed, so a `doc-owners` obligation is deferred only when a principal applies the actor-verified `vinaya/waiver:docs` label — a Developer cannot self-serve it, and there is no body field for you to judge. What is still yours, because no CI gate can check it: whether a published doc reads complete to a stranger who lands on it cold. Hold every doc the brief surfaces to the reader-readability rule — a reader must resolve every symbol on the page from the page itself. A doc update that satisfies C5 mechanically but leaves a sentence leaning on a decision id or bare section number a stranger can't resolve is a MAJOR finding.
77
+ 6. **Doc coupling.** Tier 1+ work should carry spec/skill updates. If code changed contracts but no docs moved, flag it. (`verify-docs` also gates this in CI — your job is the judgment CI cannot make: are the docs *correct*, not just *present*.) For every doc named in the brief's documentation-update list: if it is absent from the diff, that is a **BLOCKER** (the list is a definition-of-done obligation, not guidance); if it is present but incorrect, that is also a BLOCKER. Check that compliance before reviewing logic. **Coverage of the `.vinaya/doc-owners` bindings is mechanical (`verify-docs` C5).** You no longer carry the "did the right doc move?" cognitive load — CI does. Your job shrinks to **judging correctness of the covered doc**: did the update actually reflect the code change, or is it a no-op edit / a misleading rewrite that silences C5 without reflecting reality? A passing C5 plus an incorrect doc update is a **BLOCKER**. A doc-coverage waiver is no longer a mechanism you weigh: the waiver body-grammar was removed, so a `doc-owners` obligation is deferred only when a principal applies the actor-verified `vinaya/waiver:docs` label — a Developer cannot self-serve it, and there is no body field for you to judge. What is still yours, because no CI gate can check it: whether a published doc reads complete to a stranger who lands on it cold. Hold every doc the brief surfaces to the reader-readability rule — a reader must resolve every symbol on the page from the page itself. A doc update that satisfies C5 mechanically but leaves a sentence leaning on a decision id or bare section number a stranger can't resolve is a MINOR finding — reader-readability is never a BLOCKER.
78
78
  7. **Multi-project reach.** If the PR's brief lists more than one `Project:`, review through each project's lens — the change's blast radius spans all of them. Confirm a shared-package change (e.g. a shared `core`/`engine` package) doesn't silently break a consumer the brief didn't mention. **Before asserting blast-radius coverage is complete** — required whenever the brief lists more than one `Project:`, or the diff touches a path under a shared collision domain (live-derived `packages/*` workspace members, built-in cross-cutting defaults, plus any `vinaya.config.json` `blastRadius.extraDomains` entries) even on a single-project brief; see `contracts/planner-brief.md` for the full domain-list derivation — run the consumer check for each touched shared package and quote its output in your review comment: `git grep -l '@attalabs/<pkg>' -- 'package.json' '*/package.json'` (this repo's form — substitute the touched package's published name; the two pathspecs are both needed, since `'*/package.json'` alone skips a repo-root manifest; use `git grep`, not `rg`, which silently skips gitignored doc trees). A consumer list you never generated is a consumer list you guessed. This applies to these two verdict fields only — the other checks keep their existing shape; evidence-on-everything is the "flag everything, get ignored" failure in another costume.
79
- 8. **Register and slop, in any reader-facing prose the diff adds or edits.** Two other defects in this same family — an unresolvable citation, and a coined term used without a definition — are checked mechanically now, not by you. This one still isn't, and it is a checkable property, not a stylistic preference: does a sentence narrate the work episode instead of stating the durable fact ("this fixes the bug from the last review," "I checked every case," a first-person aside) where a stranger reading the page later has no session to place it in? Does a padding adjective ("robust," "seamless," "comprehensive") carry no concrete referent a reader could verify? Flag the sentence and say what's wrong with it, the same way you'd flag a bug — this is not the taste-based-rewrite exclusion below; it fires only on a nameable defect (narration, unearned padding), never on a phrasing you'd merely have chosen differently.
79
+ 8. **Register and slop, in any reader-facing prose the diff adds or edits.** Two other defects in this same family — an unresolvable citation, and a coined term used without a definition — are checked mechanically now, not by you. This one still isn't, and it is a checkable property, not a stylistic preference: does a sentence narrate the work episode instead of stating the durable fact ("this fixes the bug from the last review," "I checked every case," a first-person aside) where a stranger reading the page later has no session to place it in? Does a padding adjective ("robust," "seamless," "comprehensive") carry no concrete referent a reader could verify? Flag the sentence and say what's wrong with it, the same way you'd flag a bug — this is not the taste-based-rewrite exclusion below; it fires only on a nameable defect (narration, unearned padding), never on a phrasing you'd merely have chosen differently. Every finding under this check is MINOR.
80
80
 
81
81
  ## What you do NOT do
82
82
 
@@ -93,7 +93,7 @@ This is why the review is a separate pass and not something the Developer does t
93
93
 
94
94
  ## Output format
95
95
 
96
- **Run `vinaya review post --role code-reviewer` with this data; do not hand-type a verdict comment.** The `VERDICT:` line is bare — no bold, no heading, no blockquote — it is machine-read by the pre-merge review gate. So is the `Judged head:` line immediately below it: the gate binds your verdict to the exact commit you reviewed, and a verdict that does not cover the PR's current head does not count as clean, however clean its `VERDICT:` value is (`review-gate.ts`). Free-typing this shape into `gh pr comment` is no longer the sanctioned path — a decorated heading or a bolded/blockquoted line the gate's line-anchored parser cannot see reaches the forge looking correct to a human reader and is invisible to `verify-review-gate.ts`, with no pointer back to what was wrong until CI goes red. `vinaya review post` resolves the PR's real head itself (`gh pr view --json headRefOid` — never a self-reported sha), renders every structural line from your validated inputs, posts the comment, and refuses to exit 0 unless its own post re-parses clean through the exact same `extractCodeReviewVerdict` function the gate calls:
96
+ **Run `vinaya review post --role code-reviewer` with this data; do not hand-type a verdict comment.** The `VERDICT:` line is bare — no bold, no heading, no blockquote — it is machine-read by the pre-merge review gate. So is the `Judged head:` line immediately below it: the gate binds your verdict to the exact commit you reviewed, and a verdict that does not cover the PR's current head does not count as clean, however clean its `VERDICT:` value is (`review-gate.ts`). A verdict also holds for a later head whose patch identity equals the judged head's: the gate compares `git diff <base>...<sha> | git patch-id --stable` on both sides, so a merge from the main branch or a rebase that leaves the PR's own patch untouched keeps your verdict alive rather than costing a round to re-cast it over changes you already read. That comparison ignores whitespace, so a whitespace-only push also keeps your verdict; any change to non-whitespace content does not, and comes back to you. Free-typing this shape into `gh pr comment` is no longer the sanctioned path — a decorated heading or a bolded/blockquoted line the gate's line-anchored parser cannot see reaches the forge looking correct to a human reader and is invisible to `verify-review-gate.ts`, with no pointer back to what was wrong until CI goes red. `vinaya review post` resolves the PR's real head itself (`gh pr view --json headRefOid` — never a self-reported sha), renders every structural line from your validated inputs, posts the comment, and refuses to exit 0 unless its own post re-parses clean through the exact same `extractCodeReviewVerdict` function the gate calls:
97
97
 
98
98
  ```
99
99
  vinaya review post --role code-reviewer --pr <n> --verdict APPROVE|REQUEST_CHANGES \
@@ -102,7 +102,7 @@ vinaya review post --role code-reviewer --pr <n> --verdict APPROVE|REQUEST_CHANG
102
102
  --task-id <task-id> --model <model> --tokens-in <n|-> --tokens-out <n|-> --cost <text|->
103
103
  ```
104
104
 
105
- The findings file is one finding per line, `SEVERITY|file:line|description` (`|`-delimited: `file:line` already contains a colon), severity one of `BLOCKER|MAJOR|MINOR`. Omit `--findings-file` for zero findings. The command renders this exact shape (kept here so a human or a debugging agent can still read what it produces — this is documentation, not something to write by hand):
105
+ The findings file is one finding per line, `SEVERITY|file:line|description` (`|`-delimited: `file:line` already contains a colon), severity one of `BLOCKER|MAJOR|MINOR`. The `description` field begins with the finding's id and class, `F<n> <class>: <what is wrong>` — class is one of `correctness`, `type-safety`, `performance`, `resource-leak`, `maintainability`, `scope`, `test-honesty`, `doc-correctness`, or `other:<slug>` when none fits. This is free text that carries no `|` character inside the existing field, not a grammar change. Omit `--findings-file` for zero findings. The command renders this exact shape (kept here so a human or a debugging agent can still read what it produces — this is documentation, not something to write by hand):
106
106
 
107
107
  ```
108
108
  VERDICT: APPROVE | REQUEST CHANGES
@@ -113,7 +113,7 @@ BRIEF CONFORMANCE: [does it do what the brief asked? 1-2 sentences]
113
113
  SPEC CONFORMANCE: [does it agree with the Product spec? "n/a — no Product named" | "clean" | drift listed in findings]
114
114
 
115
115
  FINDINGS (ordered by severity):
116
- 1. [BLOCKER|MAJOR|MINOR] <file:line> — <what's wrong and why it matters>
116
+ 1. [BLOCKER|MAJOR|MINOR] <file:line> — F<n> <class>: <what's wrong and why it matters>
117
117
  2. ...
118
118
 
119
119
  SCOPE: [clean | N out-of-scope changes listed in findings]
@@ -121,19 +121,36 @@ TESTS: [honest | issues listed in findings]
121
121
  DOCS: [tier-appropriate | missing items listed in findings]
122
122
  ```
123
123
 
124
- `vinaya review post` also refuses before posting anything if you pass a BLOCKER finding together with `--verdict APPROVE` — that contradiction is caught mechanically, not left to review.
124
+ `vinaya review post` also refuses before posting anything if you pass a BLOCKER finding together with `--verdict APPROVE` — that contradiction is caught mechanically, not left to review. Before its own post reaches the forge, it refuses to post anything the gate would misread: it runs the exact same `VERDICT:`/`Judged head:` extraction the merge gate uses over the rendered comment, and requires exactly the intended verdict to come back. Free text in a finding, a conformance field, or `--scope-evidence-file` can say `VERDICT` or span multiple lines without risk — the extraction reads only a comment's first three lines, which are always this command's own structural lines, never a caller field.
125
125
 
126
- - **BLOCKER** — must fix before merge (wrong behavior, scope violation, dishonest test, missing required doc, **spec contradiction**).
127
- - **MAJOR** — should fix before merge (likely bug, weak error handling, **spec drift that isn't an outright contradiction**).
128
- - **MINOR** — note it; Developer's discretion.
126
+ - **BLOCKER** — blocks merge. Wrong behavior; a dishonest test; a document the brief's documentation-update list names that is absent from the diff or states the changed behavior backwards; a scope violation; a **spec contradiction**.
127
+ - **MAJOR** — surfaced, never blocks. A likely bug, weak error handling, spec drift short of contradiction, a wrong sentence in a document the brief did not name. Listed in the verdict, shown to the Principal at the go (the Principal's decision, before merge, on whether any surfaced finding blocks this change), published in the record — the Principal decides whether a surfaced finding blocks this change.
128
+ - **MINOR** — noted; Developer's discretion. Every register and slop finding (check 8) and every reader-readability finding (check 6) is at most MINOR.
129
129
 
130
130
  The `SCOPE:` line, and any blast-radius assertion under check 7, are evidence-backed claims: each may be written only after its named check has run — `git diff origin/main...HEAD --stat` for scope, the consumer grep for multi-project reach — with the output quoted in the same review comment the verdict lands in: a fenced block directly below the verdict block, so evidence sits in one predictable place. The other verdict lines carry no such requirement.
131
131
 
132
- If you have only MINOR findings, VERDICT is APPROVE. Any BLOCKER REQUEST CHANGES. (A REQUEST CHANGES sets the PR's review decision to `CHANGES_REQUESTED`, which is the derived `changes-requested` status — no one writes it down.)
132
+ VERDICT is `REQUEST CHANGES` if and only if at least one BLOCKER finding exists. Otherwise VERDICT is `APPROVE`, with every MAJOR and MINOR finding still listed under FINDINGS — an APPROVE is not silence about them, it is a statement that none of them blocks. (A REQUEST CHANGES sets the PR's review decision to `CHANGES_REQUESTED`, which is the derived `changes-requested` status — no one writes it down.) You do not type that decision by hand: `vinaya review post` derives it from the findings file you pass it — REQUEST CHANGES iff a BLOCKER is present, APPROVE otherwise — and renders the bare `VERDICT:` line and the `Judged head:` binding itself. `--verdict` is optional; if you pass one anyway, the command refuses before posting anything when it disagrees with the derivation, naming the derived value. It refuses to exit 0 unless its own post re-parses clean through the gate's extractors. The severity you assign to each finding is caller-asserted and not checked — the derivation trusts your severities, not your arithmetic.
133
+
134
+ A re-review (a fresh-context reviewer invoked again after the Developer pushes fixes) does two things, in order. First, it reports the state of every prior id — `F1`, `F2`, … — before listing any new finding, one of exactly `open`, `fix-claimed`, `reproduced`, or `resolved` per id, confirmed by re-checking the artifact, never by assuming a push means a fix. Write that state directly in the finding's own description, `F<n> <class> <state>: <text>` — that is what `vinaya review post` reads back on the next round to confirm every prior id is still accounted for; a findings file that drops a prior id with no state token is refused before posting. An id is assigned once, when a finding is first reported, and never renumbered; rewording a finding's description does not create a new id. The prior ids and the previously judged head are read from the prior verdict comment on the PR: its FINDINGS list and its `Judged head:` line. Second, round two is delta-only for every non-blocking severity: it judges only the lines changed since the previously judged head, and `vinaya review post` refuses a MAJOR or MINOR finding whose `file:line` falls outside that diff. A BLOCKER outside the delta still drives the verdict on any round and is always accepted. A prior BLOCKER you mark `resolved` keeps its BLOCKER severity in the record but no longer drives the verdict — `vinaya review post` derives the verdict only from findings not marked `resolved`; mark `fix-claimed` or `reproduced` instead if it is not actually fixed. After round two the Principal decides; there is no round three unless the Principal orders it.
133
135
 
134
136
  ## Escalation
135
137
 
136
- If you discover something that needs a decision above review authority the brief itself was wrong, the work requires a Type 1 (irreversible) decision nobody made, or the diff is right but the **spec is wrong/stale** and should change say so explicitly under FINDINGS as `[ESCALATE] severity:strategy` or `[ESCALATE] severity:product`. Do not resolve it yourself; route it to the Planner or Principal. (A spec that needs updating is a strategy escalation, not a reason to fail the PR.)
138
+ If you discover something that needs a decision above review authority, post it with `vinaya review post --escalate <class> --summary <text>` never as a finding inside a REQUEST CHANGES. An escalation is its own review outcome: it renders `ESCALATE: <class>`, never a `VERDICT:` line, and the command refuses it alongside `--verdict` or alongside any blocking finding in the same findings file. Three classes:
139
+
140
+ - `authority` — the decision is above review authority outright; you have no basis to rule on it.
141
+ - `strategy` — the brief assumes an approach the codebase has gone a different way on, or a required edit sits outside the brief's stated surface but is genuine blast radius of the change. Do not demand the out-of-surface edit yourself and then also flag it as scope creep in the same verdict — pick one: it is either in scope (name it) or it is a strategy escalation, never both.
142
+ - `product` — the work requires a Type 1 (irreversible) decision nobody made, or the diff is right but the **spec is wrong/stale** and should change. (A spec that needs updating is a `product` escalation, not a reason to fail the PR.)
143
+
144
+ Do not resolve it yourself; route it to the Planner or Principal.
145
+
146
+ ## Brief review mode
147
+
148
+ Before dispatch — a separate, time-boxed pass, not the post-dispatch code review above — a fresh-context Reviewer reads the whole brief and returns one line: `BRIEF: READY` or `BRIEF: NOT READY`. Under five minutes. Findings come in exactly two classes, nothing else:
149
+
150
+ - `contradiction` — two sentences in the brief that cannot both hold.
151
+ - `design-hole` — the design the brief specifies can be defeated by the party it constrains, or fails on an input the brief never named.
152
+
153
+ `BRIEF: NOT READY` returns the brief to its author (the Brief Author or Planner) rather than letting it proceed to dispatch — it is not a code review, and it carries no finding outside the two classes above.
137
154
 
138
155
  ## Where you sit in the process
139
156
 
@@ -143,4 +160,4 @@ Phase 10 (Review) in `process.md`. The order is: **code-reviewer pass (you) →
143
160
 
144
161
  ## Turn-end: report your tokens in the verdict comment
145
162
 
146
- You do not append your own row to `aeg-root/tranches/<name>.tokens.md` — you have no branch to write it on, and self-append was retired for every role. Instead, `vinaya review post`'s `--task-id`/`--model`/`--tokens-in`/`--tokens-out`/`--cost` flags render the closing one-line token report as part of the same posted comment: `Tokens: <task-id>: review — Reviewer — <model> — in/out/cost`. Review normally runs **operator-metered** — on a host that exposes no usage figure to the agent — so pass `-` (a literal hyphen, not this doc's `—`) for `--tokens-in`/`--tokens-out`/`--cost` when unknown; that host capability is the one sanctioned reason for a blank token cell (`tranche-model.md` §12), never inconvenience, and you never estimate. If your host does expose your own usage to you, pass the real figures instead. The per-task Archivist collects this report at close-out and appends the row to the ledger — see `roles/archivist.md`. A re-review (after the Developer pushes fixes) reports again — run `vinaya review post` again rather than editing the prior comment.
163
+ You do not append your own row to `aeg-root/tranches/<name>.tokens.md` — you have no branch to write it on, and self-append was retired for every role. Instead, `vinaya review post`'s `--task-id`/`--model`/`--tokens-in`/`--tokens-out`/`--cost` flags render the closing one-line token report as part of the same posted comment: `Tokens: <task-id>: review — Reviewer — <model> — in/out/cost`. Review normally runs **operator-metered** — on a host that exposes no usage figure to the agent — so pass `-` (a literal hyphen, not this doc's `—`) for `--tokens-in`/`--tokens-out`/`--cost` when unknown; that host capability is the one sanctioned reason for a blank token cell (`tranche-model.md` §12), never inconvenience, and you never estimate. If your host does expose your own usage to you, pass the real figures instead. The per-task Archivist collects this report at close-out and appends the row to the ledger — see `roles/archivist.md`. A re-review (after the Developer pushes fixes) reports again, following the re-review rule under [Output format](#output-format) above — run `vinaya review post` again rather than editing the prior comment.