@attalabs/vinaya 0.24.1 → 0.26.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 (130) hide show
  1. package/README.md +26 -3
  2. package/aeg-root/contracts/architect-planner.md +5 -5
  3. package/aeg-root/contracts/developer-reviewer.md +10 -7
  4. package/aeg-root/contracts/planner-developer.md +142 -0
  5. package/aeg-root/contracts/reviewer-archivist.md +2 -2
  6. package/aeg-root/contracts/tranche-archivist-planner.md +12 -11
  7. package/aeg-root/enforcement.md +25 -11
  8. package/aeg-root/process.md +53 -75
  9. package/aeg-root/roles/archivist.md +3 -3
  10. package/aeg-root/roles/developer.md +30 -24
  11. package/aeg-root/roles/planner.md +100 -21
  12. package/aeg-root/roles/principal.md +14 -11
  13. package/aeg-root/roles/reviewer.md +33 -17
  14. package/aeg-root/roles/security.md +28 -13
  15. package/aeg-root/roles/tranche-archivist.md +2 -2
  16. package/aeg-root/skills/aeg/SKILL.md +5 -5
  17. package/aeg-root/skills/aeg-roles/SKILL.md +7 -7
  18. package/aeg-root/state-machine.md +36 -35
  19. package/aeg-root/task-model.md +3 -3
  20. package/aeg-root/templates/brief-template.md +22 -6
  21. package/aeg-root/templates/issue-rationale-template.md +35 -2
  22. package/aeg-root/templates/pr-report-template.md +5 -13
  23. package/aeg-root/tranche-model.md +21 -21
  24. package/dist/checks/bin/check-body-bare-digits.js +684 -142
  25. package/dist/checks/bin/check-branch-topology.js +741 -88
  26. package/dist/checks/bin/check-brief-shape.js +708 -83
  27. package/dist/checks/bin/check-changeset-coverage.js +1445 -149
  28. package/dist/checks/bin/check-closes-n.js +741 -88
  29. package/dist/checks/bin/check-coherence.js +808 -149
  30. package/dist/checks/bin/check-dead-branch-push.js +617 -81
  31. package/dist/checks/bin/check-dispatch-readiness.js +811 -152
  32. package/dist/checks/bin/check-doc-coverage-push.js +1445 -149
  33. package/dist/checks/bin/check-doc-coverage.js +1447 -151
  34. package/dist/checks/bin/check-doctrine-no-procedures.js +684 -142
  35. package/dist/checks/bin/check-doctrine-portability.js +1445 -149
  36. package/dist/checks/bin/check-evidence-fresh.js +684 -142
  37. package/dist/checks/bin/check-exec-bits.js +1445 -149
  38. package/dist/checks/bin/check-first-push-dispatch.js +808 -149
  39. package/dist/checks/bin/check-issue-assignment.js +741 -88
  40. package/dist/checks/bin/check-main-branch-refusal.js +618 -82
  41. package/dist/checks/bin/check-no-disk-state.js +617 -81
  42. package/dist/checks/bin/check-pr-premise-reassert.js +5401 -0
  43. package/dist/checks/bin/check-pr-report-density.js +617 -81
  44. package/dist/checks/bin/check-quoted-command.js +1443 -147
  45. package/dist/checks/bin/check-reader-resolvable-prose.js +1481 -153
  46. package/dist/checks/bin/check-registry-gates.js +659 -85
  47. package/dist/checks/bin/check-retired-vocabulary.js +1443 -147
  48. package/dist/checks/bin/check-review-gate.js +758 -145
  49. package/dist/checks/bin/check-single-plan-pr.js +617 -81
  50. package/dist/checks/bin/check-surface-scope.js +5722 -0
  51. package/dist/checks/bin/check-test-plan.js +617 -81
  52. package/dist/checks/bin/check-token-collection-wired.js +617 -81
  53. package/dist/checks/bin/check-token-report.js +629 -88
  54. package/dist/checks/bin/check-workspace-escape.js +1443 -147
  55. package/dist/index.js +8548 -4335
  56. package/package.json +1 -1
  57. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-render.ts +2 -2
  58. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/main-branch-refusal.ts +26 -1
  59. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  60. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  61. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  62. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  63. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  64. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  65. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  66. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  67. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  68. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  69. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  70. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  71. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  72. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
  73. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  74. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  75. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  76. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  77. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  78. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  79. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  80. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  81. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  82. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  83. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  84. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  85. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
  86. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  87. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  88. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  89. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  90. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  91. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  92. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  93. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  94. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  95. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  96. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  97. 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
  98. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0053k9k._.js +1 -1
  99. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0112h-k._.js +1 -1
  100. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0o771t1._.js +1 -1
  101. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__1pndh3_._.js → [root-of-the-server]__0p8q38b._.js} +1 -1
  102. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0puovz5._.js +1 -1
  103. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +1 -1
  104. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
  105. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_03x_w6q._.js +1 -1
  106. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0gvm3og._.js +1 -1
  107. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0lwxg63._.js +1 -1
  108. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0wxycau._.js +1 -1
  109. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1n0cnq-._.js +1 -1
  110. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
  111. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/packages_ui_topbar_index_tsx_1h1gs1y._.js +1 -1
  112. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  113. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  114. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  115. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
  116. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3m1kgax7j2vgs.js → 0bn8c8v5q429o.js} +1 -1
  117. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0doqwpd81sjzn.js → 0keji7wvbe1d1.js} +1 -1
  118. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/1maoxbrl3pv5d.css +1 -0
  119. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1uogpj5w2n5ju.js → 3-6a3tinbdb-7.js} +1 -1
  120. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0zebsmmk1bvnb.js → 3_9xytdmiv1xi.js} +4 -4
  121. package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
  122. package/aeg-root/contracts/brief-developer.md +0 -141
  123. package/aeg-root/contracts/planner-brief.md +0 -130
  124. package/aeg-root/roles/brief-author.md +0 -114
  125. package/aeg-root/skills/brief-authoring/SKILL.md +0 -509
  126. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/3qc67qhcvbg0o.css +0 -1
  127. /package/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/vinaya/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
  128. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{QY2GhiI47-765D9DHGy1I → DMofV2-9FrHE7100GfrpI}/_buildManifest.js +0 -0
  129. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{QY2GhiI47-765D9DHGy1I → DMofV2-9FrHE7100GfrpI}/_clientMiddlewareManifest.js +0 -0
  130. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{QY2GhiI47-765D9DHGy1I → DMofV2-9FrHE7100GfrpI}/_ssgManifest.js +0 -0
@@ -28,11 +28,11 @@ Artifact class
28
28
  → escalation path (what happens when a decision exceeds role authority)
29
29
  ```
30
30
 
31
- Roles exist because different decisions require different accountability levels. The Principal is accountable for irreversible (Type 1) decisions. The Planner / Brief Author is accountable for reversible (Type 2) decisions within a ratification window. The Developer executes. The Reviewer (code and security specializations) judges shipped code with fresh context but cannot mutate it. The Archivist closes out.
31
+ Roles exist because different decisions require different accountability levels. The Principal is accountable for irreversible (Type 1) decisions. The Planner is accountable for reversible (Type 2) decisions within a ratification window. The Developer executes. The Reviewer (code and security specializations) judges shipped code with fresh context but cannot mutate it. The Archivist closes out.
32
32
 
33
- The conversational role set is: **Principal, Planner, Brief Author, Developer, Reviewer** (plus the non-conversational Archivist). The Reviewer was added to an earlier three-role model; splitting the Planner from the Brief Author is what makes it five. **Planner and Brief Author are two roles** (`roles/planner.md`, `roles/brief-author.md`), each with its own entry gate: the Planner cuts a tranche, the Brief Author authors one task's brief. Architecture and design conversation is work both do with the Principal, not a third seat. Security is a specialization of Reviewer, not a separate role. **Verification (`roles/developer.md` § Verification) is a *phase*, not a fifth role:** it is jointly satisfied by the Developer-agent (the `[agent]` half of the brief's Test Plan) and the Principal (the `[principal]` half), so the role count remains unchanged (June 2026). The per-task **Archivist** named above closes out a single task at merge; a distinct non-conversational role, the **Tranche Archivist** (`roles/tranche-archivist.md`), closes out a whole tranche at Phase 13 — neither is conversational, so both sit outside the conversational role set without changing its count.
33
+ The conversational role set is: **Principal, Planner, Developer, Reviewer** (plus the non-conversational Archivist). The Reviewer was added to an earlier three-role model, making it four. **The Planner has two acts** (`roles/planner.md` § The dispatch act) plan and dispatch rather than being split across two roles: a task's Issue-cutting and its later dispatch gate check are the same role's two passes, one entry gate each, not two role docs. (2026-09: the former Brief Author role was retired and folded into the Planner's dispatch act; the role count dropped from five to four.) Architecture and design conversation is work the Planner does with the Principal, not a third seat. Security is a specialization of Reviewer, not a separate role. **Verification (`roles/developer.md` § Verification) is a *phase*, not a fifth role:** it is jointly satisfied by the Developer-agent (the `[agent]` half of the brief's Test Plan) and the Principal (the `[principal]` half), so the role count remains unchanged (June 2026). The per-task **Archivist** named above closes out a single task at merge; a distinct non-conversational role, the **Tranche Archivist** (`roles/tranche-archivist.md`), closes out a whole tranche at Phase 13 — neither is conversational, so both sit outside the conversational role set without changing its count.
34
34
 
35
- **Role seams are governed by contracts.** Where one role hands work to the next (Planner → Brief Author, Brief Author → Developer, Developer → Reviewer, …), what crosses the boundary is defined **once**, in a contract file under `aeg-root/contracts/`, not described twice in two role docs (which drift). A contract is the single source of truth for its seam: the producing role fills it, the consuming role drains it, and the two role docs *point at* the contract rather than redefining it. The first is `contracts/planner-brief.md`; others are added as each seam is modeled. See Section 2 (Class 1) and Section 3.
35
+ **Role seams are governed by contracts.** Where one role hands work to the next (Planner → Developer, Developer → Reviewer, …), what crosses the boundary is defined **once**, in a contract file under `aeg-root/contracts/`, not described twice in two role docs (which drift). A contract is the single source of truth for its seam: the producing role fills it, the consuming role drains it, and the two role docs *point at* the contract rather than redefining it. The first is `contracts/planner-developer.md`; others are added as each seam is modeled. See Section 2 (Class 1) and Section 3.
36
36
 
37
37
  **A task is a forge Issue, and its status is derived, never stored** (see Section 2, Class 2, and `tranche-model.md` §3). No role writes a status field; transitions are facts about the forge (branch exists, PR open, review decision, merged).
38
38
 
@@ -57,10 +57,10 @@ Every artifact falls into one of five persistence classes. Persistence class det
57
57
  **Persistence:** Survives anything short of repo deletion. Git history preserves every mutation with authorship and timestamp.
58
58
 
59
59
  **Create:** PR merged to main by Principal (or delegated merge for Tier 0/1).
60
- **Mutate:** PR opened by Developer, reviewed by Reviewer (code + security) and Brief Author (specs) and Principal (code), merged by Principal.
60
+ **Mutate:** PR opened by Developer, reviewed by Reviewer (code + security) and Planner (specs) and Principal (code), merged by Principal.
61
61
  **Read-only:** All roles always.
62
62
 
63
- **Contracts change as a unit.** A role-seam contract (`contracts/*.md`) is the single source of truth for what crosses a role boundary. Editing it is a **Tier 3** change, because it alters a cross-role interface; and the producer and consumer sides change **together** — you cannot change what one role emits without, in the same PR, updating what the next role consumes. The two role docs on either side reference the contract; they never redefine the seam, so they need no field-level edit when the contract's *prose* changes, but the same PR must confirm both still point at it and match. (The first contract is `contracts/planner-brief.md`: Planner produces, Brief Author consumes.)
63
+ **Contracts change as a unit.** A role-seam contract (`contracts/*.md`) is the single source of truth for what crosses a role boundary. Editing it is a **Tier 3** change, because it alters a cross-role interface; and the producer and consumer sides change **together** — you cannot change what one role emits without, in the same PR, updating what the next role consumes. The two role docs on either side reference the contract; they never redefine the seam, so they need no field-level edit when the contract's *prose* changes, but the same PR must confirm both still point at it and match. (The first contract is `contracts/planner-developer.md`: the Planner's dispatch act produces, Developer consumes.)
64
64
 
65
65
  ### Class 2: Forge objects (execution state + audit)
66
66
 
@@ -72,8 +72,8 @@ Every artifact falls into one of five persistence classes. Persistence class det
72
72
 
73
73
  **The provenance block is a Class 2 object too — a close-out projection, not stored status.** At close-out the Archivist assembles a provenance record (task → intent → reviews → model → merge metadata) and posts it as a comment on the **merged** PR. It is *assembled from facts the merge already froze*, written once, append-only — a projection of frozen forge facts in exactly the way derived status is a projection of live forge facts. It is therefore explicitly **not** the forbidden "stored status" of `tranche-model.md` §9: it lives on the merged PR (never in the tranche file or the Issue), it records history (not current state), and nothing ever updates it. See `roles/archivist.md` and §13.
74
74
 
75
- **Create:** Brief Author (Issues, in Planner mode); Developer (PRs); Reviewer (review verdicts/comments); Archivist (advisory PR comments + the provenance block); any role (Issue comments with appropriate authority).
76
- **Mutate:** Labels — the closed vocabulary in Section 14 (`vinaya/tier:*`, `vinaya/blocked`, `vinaya/needs:*-input`, `vinaya/needs:brief-correction`, `vinaya/override:docs`), applied by the role and at the transition Section 14 specifies. **No `status:*` labels** — status is derived. Issue/PR body — the brief lives in the **PR body** (frozen after open Section 7); the Issue body holds metadata only, never the brief, never planning fields (priority/estimates), which a required template + CI reject.
75
+ **Create:** Planner (Issues); Developer (PRs); Reviewer (review verdicts/comments); Archivist (advisory PR comments + the provenance block); any role (Issue comments with appropriate authority).
76
+ **Mutate:** Labels — the closed vocabulary in Section 14 (`vinaya/tier:*`, `vinaya/blocked`, `vinaya/needs:*-input`, `vinaya/needs:brief-correction`, `vinaya/override:docs`), applied by the role and at the transition Section 14 specifies. **No `status:*` labels** — status is derived. Issue/PR body — for a task branch, the brief lives on the task **Issue's `aeg:brief:v1` comment**, posted once, frozen, by `vinaya task dispatch`, before the Developer starts; the Issue's own body holds metadata only, never the brief, never planning fields (priority/estimates), which a required template + CI reject. The PR body itself never carries a brief copy (frozen after open — Section 7); a standalone `fix/*` brief, having no task Issue, is unaffected and still authored directly into its PR body.
77
77
  **Read-only:** All roles always.
78
78
 
79
79
  ### Class 3: Orchestration-tool runtime (ephemeral, optional)
@@ -105,7 +105,7 @@ This is a tool detail, not part of the AEG model. Nothing canonical depends on i
105
105
  **Persistence:** Ephemeral; not reliably retrievable across sessions.
106
106
 
107
107
  **Create:** Any conversational agent.
108
- **Promote:** Brief Author promotes decisions made during conversation into the spec for the surface they govern (Section 6); Principal ratifies Type 1 promotions.
108
+ **Promote:** the Planner promotes decisions made during conversation into the spec for the surface they govern (Section 6); Principal ratifies Type 1 promotions.
109
109
  **Cannot mutate:** No role retroactively edits conversation logs.
110
110
 
111
111
  ---
@@ -114,26 +114,26 @@ This is a tool detail, not part of the AEG model. Nothing canonical depends on i
114
114
 
115
115
  Rows = artifact types. Columns = roles. "—" means no authority. The Reviewer is absent as a mutation authority (it has read + PR-review-comment authority only — see the subsection after the table).
116
116
 
117
- | Artifact | Principal | Planner / Brief Author | Developer | Archivist |
117
+ | Artifact | Principal | Planner | Developer | Archivist |
118
118
  |----------|-----------|-------------|-----------|-----------|
119
119
  | **Conversation logs** | Flags for retention | Writes during chat; proposes what belongs in the pull request | Reads only | Cannot mutate |
120
120
  | **Tranche topology files** (`tranches/*.md`) | Approves PR | Writes (Planner mode) at plan time — task→issue map + edges + grouping; **no status, no PR numbers, no dates** | — | Flags execution-metadata creep in drift cron |
121
121
  | **Role-seam contracts** (`contracts/*.md`) | Approves PR; Type 1 ratification (a contract is a cross-role interface) | Proposes via PR; changes producer + consumer sides together (Tier 3) | — | Flags a role doc that contradicts its contract in drift cron |
122
122
  | **Task Issues** (identity + metadata) | Approves merge | Creates (Planner mode); metadata + Planner's rationale — no brief, no status, no planning fields | Reads; references via `Closes #N` | Validates template (no forbidden fields) |
123
123
  | **Briefs (dispatched)** | Can amend via reply to escalation | Can amend via reply to escalation — logged as an event, NOT a brief edit | Reads only — brief is frozen after dispatch; escalate if wrong | Cannot mutate |
124
- | **Briefs (pre-dispatch)** | Approves the brief | Writes the brief just-in-time per the `brief-authoring` skill, consuming the planner-brief contract; pastes to Developer (lands in PR body) | — | Validates structure; flags malformed (`vinaya/needs:brief-correction`) |
124
+ | **Briefs (pre-dispatch)** | Approves the underlying rationale | Renders it mechanically — no hand-authoring per the dispatch act (`roles/planner.md` § The dispatch act), consuming the planner-developer contract; `vinaya task dispatch` posts it once, frozen, as the task Issue's `aeg:brief:v1` comment — never into the PR body | — | Validates structure; flags malformed (`vinaya/needs:brief-correction`) |
125
125
  | **Specs** (`apps/*/specs/*.md`) | Approves PR; ratifies a spec-only change | Coherence review on PR; can open spec-only PRs | Writes in PR per brief scope | Validates cross-references; flags stale specs in drift cron |
126
126
  | **Skills** (canonical `aeg-root/skills/*/SKILL.md`) | Approves PR | Coherence review | Writes in PR per brief scope | Flags stale skill references in drift cron |
127
127
  | **Agent defs** | Approves PR | Coherence review | Writes in PR per brief scope | Flags stale agent references in drift cron |
128
128
  | **Per-project backlogs** (`apps/*/specs/*-backlog.md`) | Approves PR | Writes (held/future items — out of the flow) | — | — |
129
129
  | **`state-machine.md`** | Approves PR; final authority on system-level rule changes | Proposes changes via PR | — | Flags inconsistencies in drift cron |
130
- | **`thinking.md`** | Reads | Writes freely in any Brief Author session (best-effort, optional) | Reads | Flags if untouched >7 days |
130
+ | **`thinking.md`** | Reads | Writes freely in any Planner session (best-effort, optional) | Reads | Flags if untouched >7 days |
131
131
  | **Lessons log** (the standing lessons Issue) | Approves/rejects/defers items at windows | Appends items; marks resolved after Principal action | Appends via escalation (`severity: product`) | The **Tranche Archivist** appends the retrospective at Phase 13 close-out (`roles/tranche-archivist.md`) — one new comment per tranche, never an edit. (The hand-edited per-project state Issue this row once also covered is retired: everything it held is either forge-derived — Milestones + `vinaya/tranche:*` labels — or belongs in an ordinary open Issue closed when resolved.) |
132
132
  | **Source code** | Merges PR | — | Writes in PR per brief scope; opens PR | — |
133
133
  | **Forge labels** (the Section 14 vocabulary) | Applies `vinaya/override:docs` (Principal-only) | Applies `vinaya/tier:*` (Planner, at cut) + `vinaya/needs:*-input` / `vinaya/blocked` (by hand or via automation) | Applies `vinaya/needs:*-input` / `vinaya/blocked` (by hand or via automation) | Applies `vinaya/needs:brief-correction`; asserts `vinaya/tier:*` label == PR-body `Tier:` (drift cron) |
134
134
  | **Task status** | — | — | — | — *(nobody writes it — derived from the forge)* |
135
135
  | **Provenance block** (on the merged PR) | Reads (audit) | Reads (audit) | — | Assembles + posts at close-out (append-only; from frozen facts) |
136
- | **Token ledger** (`tranches/<name>.tokens.md`) | Fills a previously-`—` cell left by an operator-metered role, from whatever usage view its host shows a human (forward-reference exception, §13); approves PR | Reports tokens at turn-end, does not append — Planner mode in the plan PR (or planning report if none), Brief Author mode in its report | Reports exact tokens in the PR body ("Token report" section) at PR open + one per re-push, collected through whatever mechanism its host exposes (`tranche-model.md` §12); does not append the row itself | **Sole writer** of the ledger — collects every role's token report for the task and appends all rows at close-out; flags missing-row drift |
136
+ | **Token ledger** (`tranches/<name>.tokens.md`) | Fills a previously-`—` cell left by an operator-metered role, from whatever usage view its host shows a human (forward-reference exception, §13); approves PR | Reports tokens at turn-end, does not append — the plan act in the plan PR (or planning report if none), the dispatch act in its dispatch report | Reports exact tokens in the PR body ("Token report" section) at PR open + one per re-push, collected through whatever mechanism its host exposes (`tranche-model.md` §12); does not append the row itself | **Sole writer** of the ledger — collects every role's token report for the task and appends all rows at close-out; flags missing-row drift |
137
137
  | **Test Plan execution** (Verification phase, on the open PR) | Runs the `[principal]` half (auth-gated/key-dependent/visual items) in a browser and ticks those checkboxes on the PR | — | Runs the `[agent]` half (scriptable items) against the booted app and posts evidence comments on the PR; reports `[agent]` failures back to itself via new commits on the same branch | Flags an unticked checkbox / missing evidence comment as a merge-gate failure at close-out time (won't happen if discipline holds — merge is gated on ticked boxes) |
138
138
  | **Worktrees** | Removes after merge | — | Works in (created at dispatch) | Flags merged worktrees as cleanup candidates |
139
139
  | **Orchestration-tool runtime** (if used) | Edits config; reads (audit) | Reads | Appends events via the tool | — |
@@ -145,13 +145,13 @@ Rows = artifact types. Columns = roles. "—" means no authority. The Reviewer i
145
145
 
146
146
  The Reviewer role has two specializations — code review (`roles/reviewer.md`) and security review (`roles/security.md`) — and one narrow authority profile:
147
147
 
148
- - **Read:** all Class 1 (repo) and Class 2 (forge) artifacts, plus the brief (in the PR body) and the PR diff. **Including the `Project:` spec(s) in `apps/*/specs/`** — the code Reviewer checks the diff for **spec-conformance**, not only brief-conformance: a diff can satisfy its brief and still contradict or drift from the project's specced behavior, and catching that gap is the Reviewer's job. A spec **contradiction** is a BLOCKER; **drift** is a MAJOR finding; if the diff is right but the spec is stale, that is a `severity:strategy` escalation, not a failure. This adds **no new persistent artifact** — it reads the project spec that already exists. Always read-only on canonical artifacts.
148
+ - **Read:** all Class 1 (repo) and Class 2 (forge) artifacts, plus the brief (frozen on the task Issue's own `aeg:brief:v1` comment) and the PR diff. **Including the `Project:` spec(s) in `apps/*/specs/`** — the code Reviewer checks the diff for **spec-conformance**, not only brief-conformance: a diff can satisfy its brief and still contradict or drift from the project's specced behavior, and catching that gap is the Reviewer's job. A spec **contradiction** is a BLOCKER; **drift** is a MAJOR finding; if the diff is right but the spec is stale, that is a `severity:strategy` escalation, not a failure. This adds **no new persistent artifact** — it reads the project spec that already exists. Always read-only on canonical artifacts.
149
149
  - **Write:** PR review verdicts and review comments only (a Class 2 object) — **nothing to disk**. The verdict is the structured block in the role doc (`APPROVE | REQUEST CHANGES` for code, with a `SPEC CONFORMANCE` line; `PASS | FAIL` for security). A REQUEST CHANGES sets the PR's review decision, which is the derived `changes-requested` status — the Reviewer writes no status field. **Plus a one-line token report** in the same verdict comment — numeric cells `—` when the role is **operator-metered** — the host exposes no usage figure to the agent — which is the reviewing role's usual case and the one sanctioned reason for a blank token cell (`tranche-model.md` §12). The per-task Archivist reads this report and appends the ledger row (`tranches/<name>.tokens.md`) at close-out (§13 append-only artifacts; `tranche-model.md` §12).
150
150
  - **Cannot:** edit code, specs, skills, PM docs; mutate labels; or merge. The Reviewer reports; the Developer remediates; the Principal merges.
151
151
  - **Independence:** fresh context (a separate invocation), never reviewing work it authored. This is the whole point.
152
152
  - **Escalation:** a concern that exceeds review authority is posted via `--escalate authority | strategy | product` — its own review outcome, never a finding — and routed to the Planner (`strategy`) or Principal (`authority`/`product`).
153
153
 
154
- Because the Reviewer never mutates a canonical artifact, it has no column. Its position is Phase 10 (`process.md`): code-reviewer pass → security pass → Principal code review → Brief Author spec review → **Phase 11 Verification (`roles/developer.md` § Verification)** → merge.
154
+ Because the Reviewer never mutates a canonical artifact, it has no column. Its position is Phase 10 (`process.md`): code-reviewer pass → security pass → Principal code review → Planner spec review → **Phase 11 Verification (`roles/developer.md` § Verification)** → merge.
155
155
 
156
156
  ---
157
157
 
@@ -185,7 +185,7 @@ Used when designing future state, writing briefs, planning tranches. Ordering:
185
185
  7. Briefs / Issues / PR descriptions — prior intent
186
186
  8. Conversation logs — lowest
187
187
 
188
- **Mode selection:** "currently true?" → audit. "change it to?" → planning. verify-docs runs in audit mode; brief authoring and tranche planning run in planning mode.
188
+ **Mode selection:** "currently true?" → audit. "change it to?" → planning. verify-docs runs in audit mode; the Planner's dispatch act and tranche planning run in planning mode.
189
189
 
190
190
  ---
191
191
 
@@ -227,9 +227,9 @@ When a Developer reaches a decision not covered by the brief, it escalates throu
227
227
 
228
228
  ### Three severity levels
229
229
 
230
- **`severity: execution`** — routine, answerable by the Brief Author in Brief Author mode. ("Library X is deprecated"; "null or throw?"; "I need an unanticipated flag.") Adds label `vinaya/needs:execution-input`; the Brief Author replies; the Developer resumes.
230
+ **`severity: execution`** — routine, answerable by the Planner. ("Library X is deprecated"; "null or throw?"; "I need an unanticipated flag.") Adds label `vinaya/needs:execution-input`; the Planner replies; the Developer resumes.
231
231
 
232
- **`severity: strategy`** — which design path to take; a question for the Brief Author's design judgment rather than its dispatch work. ("The brief's approach A has a structural issue — switch to B?"; "this touches an undiscussed area"; "the diff is right but the spec is stale.") Adds `vinaya/needs:strategy-input`; same role, different question.
232
+ **`severity: strategy`** — which design path to take; a question for the Planner's design judgment rather than its dispatch work. ("The brief's approach A has a structural issue — switch to B?"; "this touches an undiscussed area"; "the diff is right but the spec is stale.") Adds `vinaya/needs:strategy-input`; same role, different question.
233
233
 
234
234
  **`severity: product`** — requires a Principal decision. Rare; reserved for Type 1 decisions discovered during execution. Adds `vinaya/needs:principal-input`. If the Principal is present, they decide and reply; if not, the item stays labeled `vinaya/needs:principal-input` and the Developer terminates, resuming via a follow-up dispatch after the window.
235
235
 
@@ -237,11 +237,11 @@ While blocked, the task carries a `vinaya/blocked` label (the one status with no
237
237
 
238
238
  ### Type 1 decisions during execution
239
239
 
240
- Type 1 (irreversible) decisions cannot be self-ratified by the Brief Author in a solo session. They ALWAYS get the `vinaya/needs:principal-input` label unless the Principal is actively present (has replied to an escalation in this session). For labeled items, the Developer terminates after acknowledgment and resumes after the window.
240
+ Type 1 (irreversible) decisions cannot be self-ratified by the Planner in a solo session. They ALWAYS get the `vinaya/needs:principal-input` label unless the Principal is actively present (has replied to an escalation in this session). For labeled items, the Developer terminates after acknowledgment and resumes after the window.
241
241
 
242
242
  ### Emergency override
243
243
 
244
- If the brief itself is wrong in a way that blocks all paths, the Developer escalates with `severity: execution` and a `brief_amendment_needed` flag. The Brief Author issues an amendment (logged as an event, not a brief edit — briefs are frozen) or kills the task. The original brief is preserved as the audit record.
244
+ If the brief itself is wrong in a way that blocks all paths, the Developer escalates with `severity: execution` and a `brief_amendment_needed` flag. The Planner issues an amendment (logged as an event, not a brief edit — briefs are frozen) or kills the task. The original brief is preserved as the audit record.
245
245
 
246
246
  ---
247
247
 
@@ -271,9 +271,9 @@ Every piece of work is assigned an impact tier; the tier determines required doc
271
271
 
272
272
  **Batches at a window:** Type 1 decisions; Tier 3 PR merges; `severity: product` escalations; PENDING Type 2 decisions.
273
273
 
274
- **Does NOT wait:** Tier 0/1 merges (anytime); `severity: execution`/`strategy` escalations (Brief Author resolves); Type 2 decisions made with the Principal present.
274
+ **Does NOT wait:** Tier 0/1 merges (anytime); `severity: execution`/`strategy` escalations (Planner resolves); Type 2 decisions made with the Principal present.
275
275
 
276
- **Cadence:** the Principal sets the times; the queue assumes no specific schedule. Items are labeled `vinaya/needs:principal-input`, not appended to a file. **Brief Author responsibility:** before the window, ensure labeled items carry enough context to decide without follow-up; after, remove the label and update artifacts to reflect what was ratified.
276
+ **Cadence:** the Principal sets the times; the queue assumes no specific schedule. Items are labeled `vinaya/needs:principal-input`, not appended to a file. **Planner responsibility:** before the window, ensure labeled items carry enough context to decide without follow-up; after, remove the label and update artifacts to reflect what was ratified.
277
277
 
278
278
  ---
279
279
 
@@ -283,7 +283,7 @@ The Archivist monitors for contradictions — shipped code and a ratified spec d
283
283
 
284
284
  **Triggers:** the drift cron (spec dates vs referenced code dates); post-merge semantic-relatedness checks; or a direct report by any role.
285
285
 
286
- **Entry format:** a forge Issue titled `Contradiction — <topic>`, labeled `vinaya/incoherent`, listing the conflicting artifacts, its severity and strategy, and `Owner: Brief Author`. It auto-escalates as `severity: strategy` and blocks new Tier 3 work on the affected subsystem until the Issue closes — the resolution is the closing comment.
286
+ **Entry format:** a forge Issue titled `Contradiction — <topic>`, labeled `vinaya/incoherent`, listing the conflicting artifacts, its severity and strategy, and `Owner: Planner`. It auto-escalates as `severity: strategy` and blocks new Tier 3 work on the affected subsystem until the Issue closes — the resolution is the closing comment.
287
287
 
288
288
  ---
289
289
 
@@ -304,16 +304,17 @@ The lowest-commitment way to run AEG: read-only over a team's existing process.
304
304
  - **Host-defined convention checks** — a repo may register its own diff-scoped checks for conventions the model has no opinion about, and they run on the same substrate as every gate above. Whatever patterns such a check encodes, and whichever document defines them, belong to the adopting repo, not to this model: the mechanism is "a registered check, scoped to added lines, blocking new violations without forcing a legacy cleanup," and the content is the host's. This repo's own instance is a UI-color check registered against its house style; another repo's would be something else entirely. Same enforcement substrate as above (CI red/green + merge gate + local hooks).
305
305
  - **Typecheck, tests** — standard CI gates; always blocking.
306
306
  - **Issue template / no forbidden fields** — a required Issue template + a CI check reject planning metadata (priority/estimates/points) on task Issues, keeping them execution-only.
307
- - **Brief validation** — the Archivist's `brief-validation` job runs `packages/aeg-core/bin/verify-brief.ts` against the PR body and checks presence of every required brief section (Tier, `For:` model attribution, `Project:` — both read from the PR body's header block, before the first `##` heading, the same region the post-merge Archivist's provenance assembly reads — tagged Test Plan, surface map, doc-update list, worktree Step 0, stop conditions, autonomy clause, `Closes #N`); flags malformed briefs (`vinaya/needs:brief-correction`) and fails CI. **The trigger is the body, not the branch (`fix/brief-gate-nontask`):** the gate runs when the branch is `task/<tranche>/<n>` **or** the body is brief-shaped (`isBriefShaped` — ≥2 of surface map / doc-update list / stop conditions / autonomy clause, matched on `stripCode`'d text so a brief *quoted inside a fence* stays exempt). Bodies that are neither still bypass — that exemption is for an ordinary non-AEG PR (a one-line dependency bump) which carries no brief and must not be forced to grow one. The old branch-only bypass was the wrong proxy for it: a standalone `fix/*` brief **is** a brief, and it skipped every section check — confirmed live on `fix/studio-tranche-href`, which shipped with no §7 documentation-update list because `checkDocUpdateList`, the checker that exists for exactly that, never ran. `Closes #N` remains **task-branch-only** (`BriefSectionsOptions.requireClosesN`): a standalone fix brief has no task Issue to close, and a `plan/*` PR is *forbidden* to carry one by the plan-PR guard below — requiring it there would make the two gates jointly unsatisfiable. The same validator also runs at **authoring time** as the `brief-shape` check — `PR_BODY="$(cat brief.md)" vinaya check brief-shape` (on this repo's toolchain, also `bun packages/aeg-core/bin/verify-brief.ts --body-file <brief.md>`) — before a PR or even a branch exists, so a Brief Author gates a brief before dispatch rather than after the Developer has done the work (`skills/brief-authoring/SKILL.md` protocol step 4); with no `BRANCH`, the branch is read from the brief's own Step 0 `git worktree add … -b` line. **Includes the plan-PR Closes guard**: a `plan/*` branch whose PR body carries `Closes #N` fails CI before the non-task bypass even runs — a plan PR creates Issues, it does not resolve one (`roles/planner.md`); this closed a confirmed pattern of plan PRs prematurely closing task Issues on merge. **Real**, now the `Brief Validation` step (9/9) of the AEG gate suite job in `.github/workflows/forge-lifecycle.yml`. Presence-only — it cannot judge whether a Test Plan item is truly scriptable or whether a declared `unit-tests-only` is justified by the surface map; those remain Reviewer + Verification judgment. Same enforcement substrate as above.
307
+ - **Brief validation** — the Archivist's `brief-validation` job runs `packages/aeg-core/bin/verify-brief.ts` against the PR body and checks presence of every required brief section (Tier, `For:` model attribution, `Project:` — both read from the PR body's header block, before the first `##` heading, the same region the post-merge Archivist's provenance assembly reads — tagged Test Plan, surface map, doc-update list, worktree Step 0, stop conditions, autonomy clause, `Closes #N`); flags malformed briefs (`vinaya/needs:brief-correction`) and fails CI. **The trigger is the body, not the branch (`fix/brief-gate-nontask`):** the gate runs when the branch is `task/<tranche>/<n>` **or** the body is brief-shaped (`isBriefShaped` — ≥2 of surface map / doc-update list / stop conditions / autonomy clause, matched on `stripCode`'d text so a brief *quoted inside a fence* stays exempt). Bodies that are neither still bypass — that exemption is for an ordinary non-AEG PR (a one-line dependency bump) which carries no brief and must not be forced to grow one. The old branch-only bypass was the wrong proxy for it: a standalone `fix/*` brief **is** a brief, and it skipped every section check — confirmed live on `fix/studio-tranche-href`, which shipped with no §7 documentation-update list because `checkDocUpdateList`, the checker that exists for exactly that, never ran. `Closes #N` remains **task-branch-only** (`BriefSectionsOptions.requireClosesN`): a standalone fix brief has no task Issue to close, and a `plan/*` PR is *forbidden* to carry one by the plan-PR guard below — requiring it there would make the two gates jointly unsatisfiable. The same validator also runs at **authoring time** as the `brief-shape` check — `PR_BODY="$(cat brief.md)" vinaya check brief-shape` (on this repo's toolchain, also `bun packages/aeg-core/bin/verify-brief.ts --body-file <brief.md>`) — before a PR or even a branch exists, so the Planner's dispatch act gates a brief before dispatch rather than after the Developer has done the work (`roles/planner.md` § The dispatch act); with no `BRANCH`, the branch is read from the brief's own Step 0 `git worktree add … -b` line. **Includes the plan-PR Closes guard**: a `plan/*` branch whose PR body carries `Closes #N` fails CI before the non-task bypass even runs — a plan PR creates Issues, it does not resolve one (`roles/planner.md`); this closed a confirmed pattern of plan PRs prematurely closing task Issues on merge. **Real**, now the `Brief Validation` step (9/9) of the AEG gate suite job in `.github/workflows/forge-lifecycle.yml`. Presence-only — it cannot judge whether a Test Plan item is truly scriptable or whether a declared `unit-tests-only` is justified by the surface map; those remain Reviewer + Verification judgment. Same enforcement substrate as above.
308
308
  - **Per-task Archivist close-out** — `.github/workflows/vinaya-archivist.yml::post-merge` (`packages/aeg-core/bin/archive-task.ts`) runs on every push to `main`, resolves the merge commit's PR via `gh api repos/{owner}/{repo}/commits/{sha}/pulls`, and — for task-branch PRs only — assembles and posts the Archivist provenance block (`roles/archivist.md` item 8) and explicitly closes the task's Issue, confirming the closed state before exiting. Idempotent **per PR**, not per Issue (an Issue can legitimately accrue multiple merged PRs over its life; idempotency never spans PRs): skips silently if the PR already carries a provenance comment. Skips silently on non-task branches (`plan/*`, `fix/*`, …). Fails loud (non-zero exit) on any `gh`/permission error — never a silent no-op. Real, installed with `contents: read`, `issues: write`, `pull-requests: write` permissions. Does not automate the Tranche Archivist or items 2–7 of per-task close-out (docs coherence, `docs-index.md`, token ledger) — those remain dispatched-Archivist judgment work.
309
309
  - **Manifest integrity** — `verify-docs --full` and the coherence oracle validate the doc-owners manifest: pointer existence (M1), glob syntax (M2 advisory), and duplicate globs (M3). There is no decision-number sequencing: the log it validated is gone.
310
310
  - **Closes #N gate** — task-branch PRs must declare `Closes #<its-issue>` in the PR body; absence fails CI. Non-task branches bypass this forward direction automatically. Real, installed at `.github/workflows/forge-lifecycle.yml::closes-n-gate`. Same enforcement substrate as above. **Forge-native-tranche fix (`fix/closes-n-forge-native`):** the gate's scoped `loadTrancheFiles(null, onlySlug)` call discovers which tranches exist by listing `aeg-root/tranches/*.md` filenames — a tranche with zero topology file never entered that enumeration, so the forge derivation was never invoked for it despite the tranche already being fully Milestone-capable, and the gate failed every such PR with a misleading "no topology file found." Fixed by falling back to a direct forge derivation (the Milestone-existence check plus the file-and-forge composition, today `indexTrancheMilestonesAsync` + `mergeFileTopology`) when the requested `onlySlug` isn't found via files, gated on an actual open Milestone existing so an unrecognized branch slug still fails honestly — the scoped-path counterpart to `verify-dispatch.ts`'s `otherActiveTrancheSlugs` fix. **Reverse direction (`fix/closes-n-reverse`):** a `Closes #N` that resolves to a real AEG task Issue (title matches the `[<tranche-slug>] <task-id>...` convention and carries a `vinaya/tranche:<slug>` label) now requires the branch to actually be named `task/<tranche-slug>/<task-id>`, regardless of what the branch itself is called — closing the blind spot that let a mis-named branch implement a real task Issue with zero forge-visible status. One batched forge query (`fetchTaskIssueRefs`) resolves each referenced Issue's task identity before `checkClosesN` runs. **Code-span hardening (`fix/closes-gate-hardening`):** both the pre-merge gate (`checkClosesN` in `brief-validation.ts`) and the coherence-side parse (`extractClosesReferences` in `coherence-checks.ts`) now `stripCode` the body before matching the closing keyword, as GitHub's own auto-close parser ignores `Closes #N` inside a code span or fenced block. Inline spans of **any backtick-run length** are covered (`` `x` ``, `` ``x`` ``, …) via CommonMark's `(`+)…\1` matched-run rule — the double-backtick form was a false-green in the first cut (peeled as two empty spans). Fenced blocks are matched by a line scanner that pairs a fence with its own closer by **character and run length**: `~~~` tilde fences, info strings (```` ```js ````), and runs longer than three (```` `````` ````) all strip correctly, and an unclosed fence runs to end of body as GitHub renders it — the earlier `` /```[\s\S]*?```/g `` regex missed all three. The keyword set matches GitHub's own: `close`/`closes`/`closed`, `fix`/`fixes`/`fixed`, `resolve`/`resolves`/`resolved`. Before this, a body whose only `Closes #N` was backticked passed the gate **green** yet merged **without** closing its Issue, then read red on every open PR via A3 `auto-close-misfire`; "verify-docs green" now implies "GitHub will auto-close" for fenced + inline-code forms. 4-space **indented** code blocks are stripped too, conservatively: a ≥4-column-indented run counts as code only when it follows a blank line *and* is not inside a list — since within a list item that indentation is the item's own content indent, which GitHub *does* auto-close (blanking it would be a false-red, the brief's over-strip stop condition). The strip runs on the **whole body, before any region is sliced out of it** — never on a slice. Every rule in it is block-structural (a fence pairs with its own closer; an indented run is code only after a blank line and outside list context), so a fragment strips differently from the same text in place: stripping the sliced `AEG:CLOSES` region blanked an anchor indented inside a list item — list content GitHub *does* auto-close — and the Archivist's `extractIssue` returned no Issue, stranding it on merge exactly as before. Markers are HTML comments and survive the strip, so selecting the region from stripped text loses nothing and subsumes the decoy protection: a decoy anchor inside code never survives to be sliced. The same grammar governs `maskCode`, the index-preserving variant `anchoredRegion` runs to find the `AEG:*` markers themselves — it is **upstream** of every `stripCode` call, so while it stayed on the naive fence/inline regexes a decoy `AEG:CLOSES` anchor inside a tilde fence, a ≥4-backtick fence, a double-backtick span, or an indented block won the region outright and the gate resolved a **wrong** Issue number (worse than the strandings above: the post-merge Archivist's `extractIssue` would explicitly close an unrelated Issue). Both now delegate to one pair of scanners, differing only in what they emit per code line — nothing for `stripCode`, same-length filler for `maskCode` — so a divergence is a compile-level impossibility rather than a convention. This hardens all five anchored fields (`CLOSES`, `PROJECT`, `TIER`, `PREMISE`, `TEST-PLAN`) at once, not just the closing reference. `stripCode` normalises `\r\n`/`\r` to `\n` before any of these scanners run: the fence scanners anchor per line, and JS's `.`/`$` never match `\r`, so a CRLF body opened no fence at all and let a fenced `Closes #N` walk free — the same false-green along a new axis, and it hit exactly the web-UI-authored bodies this CI backstop exists for (HTML normalises textarea newlines to CRLF on submit). The closing-keyword separator is **bounded** (`\s{0,8}:?\s{0,8}`, identical in both parsers): two adjacent unbounded `\s*` groups backtrack quadratically on `closes` + long whitespace + no `#` — ~2.0 s at GitHub's 65,536-char body cap, run twice on the fail path — where the bound is 0.1 ms. **Known residual:** a genuine indented code block *nested inside a list* is therefore left unstripped — the deliberately safe direction of that trade. A `Closes #N` surviving only inside code fails with an actionable message pointing at a bare reference in the `AEG:CLOSES` anchor. `stripCode` is the one shared stripper, exported from `anchored-region.ts` (no duplicated regex).
311
311
  - *(The practitioner-facing map of every enforcement mechanism — prevention, detection, audit — lives in `aeg-root/enforcement.md`; this section remains the normative gate registry.)*
312
- - **Tool-layer forge gates (ring 0)** — the earliest enforcement point: a tool-interception hook, wired into the agent's own host so it runs before any raw forge-mutating call reaches GitHub, denies raw `gh pr create`/`gh pr edit --body*`/`gh issue create`/`gh issue edit --body*` (and `gh api` creation POSTs), directing agents to the validated wrappers `packages/aeg-core/bin/open-pr.ts` (runs verify-brief + verify-docs `--pr` + the Closes #N gate locally, calls `gh` only on green) and `bin/open-issue.ts` (a task Issue with a `vinaya/tranche:*` label must carry the full eight-field Planner rationale — `checkIssueRationale`, planner-brief contract, **and pass three content checks on what those fields say, `checkBlastRadiusScope` (the declared surface may not reach a shared collision domain — live-derived `packages/*` workspace members, built-in cross-cutting defaults, plus any `vinaya.config.json` `blastRadius.extraDomains` entries — that no declared project owns, absent a second **registered** project or a `blast-radius-ack:` line), `checkNoBriefContent` (no brief-shaped section in the Issue), `checkRationaleNamesDocs` (the rationale names a concrete doc/skill path, or the `no-doc-surface` sentinel). `checkConflictCompleteness` warns on an undeclared collision-domain overlap and never blocks**). Prevention, not detection: a malformed PR/Issue body is refused at the agent's own tool call and never reaches the forge; the CI gates below re-run the identical aeg-core checks purely as a backstop for non-hook writers. Also enforced at the same layer: title grammar (`checkForgeTitle`, both wrappers), the single-plan-PR guard (`checkSinglePlanPr` — a diff touching a tranche's topology file is refused when another OPEN PR already touches that same tranche's topology file), `gh api` PATCH/curl write-method denies, and a `.husky/pre-push` gate refusing `task/<tranche>/<id>` pushes whose id has no topology row (mechanical). On this repo's shipped reference host, this hook type is a `PreToolUse` hook (`.claude/hooks/check-forge-gates.sh`, wired in `.claude/settings.json`) — one instance of the pattern, not the concept itself; see `tranche-model.md` §12 for how this doctrine names its one concrete example. Operational rule: restart running agent sessions after merging hook/settings changes — hooks load at session start. Same mechanism as skill-check enforcement and the T9 merge gate.
312
+ - **Tool-layer forge gates (ring 0)** — the earliest enforcement point: a tool-interception hook, wired into the agent's own host so it runs before any raw forge-mutating call reaches GitHub, denies raw `gh pr create`/`gh pr edit --body*`/`gh issue create`/`gh issue edit --body*` (and `gh api` creation POSTs), directing agents to the validated wrappers `packages/aeg-core/bin/open-pr.ts` (runs verify-brief + verify-docs `--pr` + the Closes #N gate locally, calls `gh` only on green) and `bin/open-issue.ts` (a task Issue with a `vinaya/tranche:*` label must carry the full eight-field Planner rationale — `checkIssueRationale`, planner-developer contract, **and pass three content checks on what those fields say, `checkBlastRadiusScope` (the declared surface may not reach a shared collision domain — live-derived `packages/*` workspace members, built-in cross-cutting defaults, plus any `vinaya.config.json` `blastRadius.extraDomains` entries — that no declared project owns, absent a second **registered** project or a `blast-radius-ack:` line), `checkNoBriefContent` (no brief-shaped section in the Issue), `checkRationaleNamesDocs` (the rationale names a concrete doc/skill path, or the `no-doc-surface` sentinel). `checkConflictCompleteness` warns on an undeclared collision-domain overlap and never blocks**). Prevention, not detection: a malformed PR/Issue body is refused at the agent's own tool call and never reaches the forge; the CI gates below re-run the identical aeg-core checks purely as a backstop for non-hook writers. Also enforced at the same layer: title grammar (`checkForgeTitle`, both wrappers), the single-plan-PR guard (`checkSinglePlanPr` — a diff touching a tranche's topology file is refused when another OPEN PR already touches that same tranche's topology file), `gh api` PATCH/curl write-method denies, and a `.husky/pre-push` gate refusing `task/<tranche>/<id>` pushes whose id has no topology row (mechanical). On this repo's shipped reference host, this hook type is a `PreToolUse` hook (`.claude/hooks/check-forge-gates.sh`, wired in `.claude/settings.json`) — one instance of the pattern, not the concept itself; see `tranche-model.md` §12 for how this doctrine names its one concrete example. Operational rule: restart running agent sessions after merging hook/settings changes — hooks load at session start. Same mechanism as skill-check enforcement and the T9 merge gate.
313
313
  - **Single-plan-PR CI backstop** — `checkSinglePlanPr` (`packages/aeg-core/src/single-plan-pr.ts`) was extracted from `open-pr.ts` (previously private to that wrapper) so the identical predicate could also run forge-side: `.github/workflows/forge-lifecycle.yml::single-plan-pr-gate` re-fetches this PR's touched files plus every other open PR's touched files and fails CI on the same violation the ring-0 hook refuses locally. Closes the gap where a PR opened directly via the GitHub web UI bypasses `open-pr.ts` entirely. No-ops (never fires) for an ordinary task-branch PR, since its diff never touches a tranche topology file. **Dormant in this repo entirely, as of the forge-native cutover:** a plan is now a Milestone plus labeled Issues, and `aeg-root/tranches/` holds no active topology file for a diff to collide over. The guard is kept, not deleted, because it is correct and live for any repo that does keep plans as files — but nothing here can trip it, and a gate that cannot fire should say so rather than read as active protection. One implementation, two enforcement points — no second copy of the predicate.
314
314
  - **Coherence oracle (A1/A2/A3/T1/T2/T3/D1/M1/M3)** — `packages/aeg-core/bin/verify-coherence.ts` runs against every PR and genuinely blocks CI — the CLI's own exit code, non-zero on any `fail`-status check, is what the `coherence-gate` job exits with. Failures in A1 (closed-without-merge), A2 (archived-without-provenance), A3 (auto-close-misfire), T1 (phantom-issue-ref), T2 (orphan-task — **plan PRs only, see below**), T3 (tbd-in-active-tranche), D1 (dispatched-on-unmet-deps), M1 (manifest-dangling), and M3 (manifest-duplicate-glob) fail CI. L1–L5 and M2 are advisory (info-only) — every `checkL*` returns `status: info`, so a premature-archive, lifecycle-hygiene, Milestone-attachment-drift, or open-Milestone-all-closed finding is surfaced for a human to investigate but never fails CI. On A1 failures the relevant Issues receive the `vinaya/incoherent` label (Section 14). Real, installed at `.github/workflows/forge-lifecycle.yml::coherence-gate`. Same enforcement substrate as above. **The job's repo-state inputs (topology files, tranche list) are read from a freshly-fetched `origin/main` — not the checkout's `refs/pull/N/merge`, which GitHub materializes lazily and can lag behind main (5+ false-red cycles, 2026-07-03/04). A plan PR's own topology diff still reads from its head ref.**
315
- - **Planner→Brief rationale completeness (R1)** — the same coherence oracle's **R1** check re-runs `checkIssueRationale` **and `checkProjectsRegistered`** (`packages/aeg-core`) against every open task Issue's body, batched per active tranche alongside T2. A non-grandfathered Issue missing any of the eight `contracts/planner-brief.md` rationale fields — or whose `Project:` field names a project with no row in the project registry — fails CI. Paired with the ring-0 creation gate (`bin/open-issue.ts`, same tool-layer-forge-gates row above) — R1 is the continuous half, the hook is the point-of-creation half; one grammar, two enforcement points (`aeg-root/enforcement.md`). Pre- Issues are grandfathered by explicit Issue number (`R1_GRANDFATHERED_ISSUES`), reported `info`, never blocking — see Section 15b. **Real.** Moves this seam's rationale-completeness half from Trusted (below) to Enforced; the "role doc matches contract prose" half of contract conformance remains Trusted.
315
+ - **Planner→Developer rationale completeness (R1)** — the same coherence oracle's **R1** check re-runs `checkIssueRationale` **and `checkProjectsRegistered`** (`packages/aeg-core`) against every open task Issue's body, batched per active tranche alongside T2. A non-grandfathered Issue missing any of the eight `contracts/planner-developer.md` rationale fields — or whose `Project:` field names a project with no row in the project registry — fails CI. Paired with the ring-0 creation gate (`bin/open-issue.ts`, same tool-layer-forge-gates row above) — R1 is the continuous half, the hook is the point-of-creation half; one grammar, two enforcement points (`aeg-root/enforcement.md`). Pre- Issues are grandfathered by explicit Issue number (`R1_GRANDFATHERED_ISSUES`), reported `info`, never blocking — see Section 15b. **Real.** Moves this seam's rationale-completeness half from Trusted (below) to Enforced; the "role doc matches contract prose" half of contract conformance remains Trusted.
316
316
  - **Review gate — code-review + security-review verdicts, bound to the commit they judged** — `packages/aeg-core/bin/verify-review-gate.ts`, a step of the AEG gate suite job, blocks merge on a task-branch PR unless a clean code-reviewer `APPROVE` verdict comment AND a clean security-review `PASS` verdict comment both exist AND both cover the PR's current head commit — `REQUEST CHANGES`, `FAIL`, a missing verdict, an unclear one, or a clean verdict bound to a superseded commit (or carrying no binding at all) all fail CI (reviewed-commit binding). Reuses `extractCodeReviewVerdict`/`extractSecurityReviewVerdict` (`verdict-extraction.ts`) — the exact detection the post-merge Archivist's provenance assembly already ran, previously advisory-only (a DANGLING note on the merged PR, never a block) — now also parsing a same-comment `Judged head: <sha>` line and comparing it against `gh pr view --json headRefOid`, resolved from GitHub, never an env var or local git. A principal can waive it for one PR with an actor-verified `vinaya/waiver:review` label (the same `isWaiverLabelActorVerified` pattern, parameterized by label — see the waiver-label-actor row above); label presence alone is never sufficient. **Real, installed at `.github/workflows/forge-lifecycle.yml::aeg-gate-suite`.** Closes the gap where task PRs could previously merge with no review pass at all, and the second gap where a clean verdict could still merge stale code because nothing checked which commit it covered. **What remains Trusted, not Enforced: *dispatching* the code-reviewer/security-reviewer subagents in the first place** — see below; this gate only verifies a dispatched review's verdict is clean and current, it cannot make a review happen.
317
+ - **Verification phase — `[principal]` Test Plan checkbox state** — `packages/aeg-core/bin/verify-test-plan.ts` (`evaluateTestPlanGate`, `test-plan-gate.ts`), the registered `test-plan` check, runs in the same blocking suite as every other check above and fails CI on any unticked `- [ ] **[principal]**` line, or on a missing Test Plan section for a task branch. **Real.** Moves the `[principal]`-checkbox-state half of the Verification phase from Trusted (below) to Enforced; the `[agent]` half is no longer a checkbox at all (task 12) — it is a fenced command list `vinaya pr report` executes, graded by the Evidence block's own freshness check instead.
317
318
  - **Registry load-bearing checks G1/G3/G4/G5/G6** — `apps/cli/src/checks/bin/check-registry-gates.ts` (the registered `registry-gates` check; `packages/aeg-core/bin/verify-registry.ts` is the standalone aeg-core-purity mirror, minus G6 — see below), a step of the AEG gate suite job, blocks merge on: G1 (a row's non-empty `implementation` that does not resolve to a real path on disk — report-only through its own rollout window, re-graded to blocking at task 8 once that window had cleared the orphan backlog it existed to surface); G3 (a file making the exact class of GitHub-mutating call `check-forge-gates.sh` gates — PR/Issue create, PR/Issue body/title edit, `gh api` create/edit, raw curl/wget writes — with no guarding Ring-0 row); G4 (a `#NNN` cited anywhere in `aeg-root/enforcement.md`'s body that does not resolve to a real Issue or PR in the forge); G5 (a contract's `producer`/`consumer` that is not a real `role_id`, or a role with an empty `performs`/`refuses_when`); G6 (task 8 — a row whose `Audience` cell reads `product` but whose `implementation` does not resolve to a real `coreCheckRegistry()` entry, the parity check "claimed checks must ship" exists for). **G2 (no-orphan-hook/CLI) stays report-only** — unchanged by task 8, it runs in the same step and prints `info` findings but never affects the exit code. Pure evaluators in `packages/aeg-core/src/registry-checks.ts`, parsing `packages/aeg-core/src/registry-parse.ts` — same shape (pure predicate + thin I/O shim) as `coherence-checks.ts`/`verify-coherence.ts`, deliberately a separate mechanism (different registry: `enforcement.md`'s own ring tables, not the tranche/forge state coherence-checks.ts reads). `registry-parse.ts`'s `GateRow` additionally carries `summary`/`category` and `description`/`spec` — purely descriptive fields consumed by the Vinaya `/how-it-works` page, not read by any G-check; G1–G6's pass/fail behavior is unaffected. `description` is the row's plain-language sentence and is resolved by header name, not column index; `spec` is the enforcement column it must never be confused with (the "what must be true"/"Re-verifies"/"Catches" slot before `implementation`). `GateRow.audience` (task 8) is the third by-header-name field, `product` or `repo-own`, defaulting to `repo-own` when the column is absent — the sole input G6 reads; it maps a `product` row's `implementation` to its claimed `coreCheckRegistry()` name(s) via `GATE_AUDIENCE` (`gate-audience.ts`, same package, no import cycle) for a `packages/aeg-core/bin/*` path, or 1:1 for a direct `apps/cli/src/checks/bin/check-*.ts` path. G6 needs `coreCheckRegistry()` itself, which lives in `apps/cli`; `aeg-core` cannot import it without closing a dependency cycle (same reasoning `gate-audience.ts` documents), so G6 runs only from `check-registry-gates.ts` — the standalone `verify-registry.ts` prints an explanatory note and skips it.
318
319
  - **Canonical action set** — `packages/aeg-core/src/actions.ts` exports `ACTIONS`, the 10-entry list of every AEG action that either crosses into GitHub (`crosses: 'into-github'`) or hands work across a role-seam contract (`crosses: 'none'`) — 6 crossings plus 4 seam-only actions, with no duplicate id for the two seams already accomplished by a crossing. Pure data (zero I/O, same shape as `waiver-label.ts`); `commit-the-work` is `'none'` because `git commit` is local-only (only `git push`/`publish-the-branch` reaches the forge). G3's crossing-file detection uses the `crosses: 'into-github'` subset; the `DiagramModel` derivation (`deriveDiagramModel`, `packages/aeg-core/src/diagram-model.ts`) now exists and places each action on a ring-0 gate (via `guards` edges) or a role/contract edge (via `performs`/`produces`/`consumes`), consuming this same `ACTIONS` list plus its `CROSSING_KEYWORDS` map (promoted into `actions.ts` alongside `ACTIONS` this task) — so G3's completeness set and the diagram's edge count cannot drift apart. The derivation is pure and takes doctrine through a `DoctrineSource` seam (`packages/aeg-core/src/doctrine-source.ts`, I/O-free; file-backed adapter `createFileDoctrineSource` in `@attalabs/vinaya-sources`), never `aeg-root/` paths directly. `actions.test.ts` cross-checks every `into-github` action against a real Ring-0 gate row in `enforcement.md` and every `performedBy` against a real `role_id`. Each entry also carries a `summary` question and a **required** `description` — required, so a new action cannot be added without one; `deriveDiagramModel` threads both onto the action's node, along with its `crosses`, which a client renderer cannot read off `ACTIONS` itself (importing it drags `node:child_process` into the browser bundle).
319
320
  - **Code-owned state machine + label vocabulary** — the same one-list-of-pure-data discipline, applied to status derivation. `packages/aeg-core/src/state-machine-model.ts` exports three parts: `FORGE_FACT_INPUTS` (every `ForgeFacts` field and the GitHub object it is read from), `DERIVED_STATUSES` (the 9-value set; `backlog` stays a member but derivation never emits it inside a tranche), and `DERIVATION_RULES` — the ordered rule list, first match wins, each entry carrying its predicate, its prose, and the reason its position matters. `deriveStatus` (`derive-tranche.ts`) executes that list, so the rules a reader sees rendered and the rules that actually run are the same objects. The list is total (its final rule matches unconditionally), and `state-machine-model.test.ts` pins the order rule-for-rule, proves every derivable status reachable, proves no rule is shadowed by an earlier one, and asserts equivalence with the pre-refactor `if`-chain across all 432 combinations of the facts derivation reads. The label vocabulary lives in `packages/aeg-forge-state/src/labels.ts` (`LABELS` + `AEG_BLOCKED_LABEL`) — in `aeg-forge-state`, not `aeg-core`, because the dependency direction is `aeg-core → aeg-forge-state → aeg-types` and the vocabulary's first consumer, `map-forge-facts.ts`, lives there and cannot import backward. Each label records the single orthogonal fact it carries; there is deliberately no `status:*` label, since status is derived and never written. `state-machine-model.ts` does **not** import that vocabulary: derivation works on `ForgeFacts`, never on label strings, and importing `@attalabs/aeg-forge-state` would drag its `node:child_process` into any browser bundle rendering the model — the same hazard the `ACTIONS` note above records.
@@ -321,12 +322,12 @@ The lowest-commitment way to run AEG: read-only over a team's existing process.
321
322
  ### Trusted (agent discipline — no CI enforcement in V0)
322
323
 
323
324
  - **Dispatching code-review and security-review passes** — Phase 10 requires them, including the spec-conformance check, but no CI bot dispatches the Reviewer subagents automatically yet; Principal + agent discipline decides when to invoke them. Automating dispatch is future work. **Once a review IS dispatched and posts a verdict, that verdict's existence and cleanliness is mechanically enforced pre-merge — see the Review gate row above.** This is the same shape as the Verification phase below: doctrine + one enforced half + one still-trusted half.
324
- - **Verification phase — runtime Test Plan execution**. Phase 11 requires the brief's §9 Test Plan to be executed before merge: the Developer-agent runs the `[agent]` items against the booted app and posts evidence on the PR; the Principal runs the `[principal]` items in a real signed-in browser and ticks the checkboxes. **Merge is gated on every checkbox being ticked AND the review verdicts being clean** — the runtime Test Plan executed-and-passing is a merge precondition alongside CI passing. The doctrine is mechanical (an unticked checkbox = not mergeable); the *enforcement* is trusted discipline today no CI bot blocks merge on an unticked checkbox yet. The optional `verify-test-plan` CI check is the path to enforced (a checkbox-state parse over the PR body); it ships per-tranche. `Test Plan: unit-tests-only` (a first-class allowed value for pure-logic briefs whose §4 surface has no runtime path) satisfies the phase by the CI unit-test gate alone. **Parser hardening:** the section locator (`locateTestPlanSection`, `packages/aeg-core/src/test-plan-section.ts`) now recognizes both the inline `**Test Plan:**` marker and the `## N. Test Plan` heading form — the original inline-only regex is what let a heading-form body advisory-PASS with an unticked `[principal]` box, since "no section found" (a parser miss, not a genuinely missing section) read as PASS. A task-branch PR with no Test Plan section in either form now FAILs loud instead, naming what was searched for; non-task branches keep the advisory bypass. The runtime Test Plan check — now the `Runtime Test Plan checkbox state` step (4/9) of the AEG gate suite job in `.github/workflows/forge-lifecycle.yml` — now also passes `BRANCH` (previously only `PR_BODY`), without which the task-branch distinction could never fire in real CI.
325
+ - **Verification phase — runtime Test Plan execution**. Phase 11 requires the brief's §9 Test Plan to be executed before merge: the Developer-agent runs the `[agent]` items against the booted app and posts evidence on the PR; the Principal runs the `[principal]` items in a real signed-in browser and ticks the checkboxes. **Merge is gated on every checkbox being ticked AND the review verdicts being clean** — the runtime Test Plan executed-and-passing is a merge precondition alongside CI passing. The doctrine is mechanical (an unticked checkbox = not mergeable); the `[principal]`-checkbox-state half of that mechanism is now Enforced (see the Enforced list above `verify-test-plan` fails CI on any unticked `[principal]` box). What remains trusted discipline: that the Developer-agent genuinely ran each `[agent]` command against a booted app rather than fabricating its output, and that the Principal genuinely exercised each `[principal]` item in a browser before ticking it no CI can watch either happen. `Test Plan: unit-tests-only` (a first-class allowed value for pure-logic briefs whose §4 surface has no runtime path) satisfies the phase by the CI unit-test gate alone. **Parser hardening:** the section locator (`locateTestPlanSection`, `packages/aeg-core/src/test-plan-section.ts`) now recognizes both the inline `**Test Plan:**` marker and the `## N. Test Plan` heading form — the original inline-only regex is what let a heading-form body advisory-PASS with an unticked `[principal]` box, since "no section found" (a parser miss, not a genuinely missing section) read as PASS. A task-branch PR with no Test Plan section in either form now FAILs loud instead, naming what was searched for; non-task branches keep the advisory bypass. The runtime Test Plan check — now the `Runtime Test Plan checkbox state` step (4/9) of the AEG gate suite job in `.github/workflows/forge-lifecycle.yml` — now also passes `BRANCH` (previously only `PR_BODY`), without which the task-branch distinction could never fire in real CI.
325
326
  - **Dispatch gates** (depends-on merged / no conflicting PR open) — read from the forge and complied with; mechanical enforcement arrives when an automation tool runs dispatch (`tranche-model.md` §8).
326
327
  - **Label discipline** (Section 14) — `vinaya/tier:*` present on every task Issue and kept in sync with the PR-body `Tier:`; `vinaya/needs:*-input` / `vinaya/blocked` present-when-true *and removed when false*; no `status:*` labels; no `project:*` labels; no label outside the closed set. Trusted discipline; the Archivist drift cron asserts the tier label/field match and flags stale `vinaya/needs:*` labels.
327
- - **Contract conformance** (a role doc matches its `contracts/*.md` seam) — trusted discipline; the Archivist drift cron flags a role doc that contradicts its contract. The contract is the source of truth; a divergent role doc is the bug. (The Planner→Brief contract's *rationale field-completeness* — every task Issue body carries all eight fields — moved to Enforced via R1 above; only the "role doc text matches contract prose" half remains trusted here.)
328
+ - **Contract conformance** (a role doc matches its `contracts/*.md` seam) — trusted discipline; the Archivist drift cron flags a role doc that contradicts its contract. The contract is the source of truth; a divergent role doc is the bug. (The Planner's own rationale grammar's *field-completeness* — every task Issue body carries all eight fields (`roles/planner.md` § Rationale grammar) — moved to Enforced via R1 above; only the "role doc text matches contract prose" half remains trusted here.)
328
329
  - **Provenance assembly at close-out — items 1/8 moved to Enforced**, see "Per-task Archivist close-out" above; items 2–7 of close-out (docs coherence, `docs-index.md`, token ledger) remain trusted, dispatched-Archivist judgment work — it records, it never gates.
329
- - **Decision logging during chat** — Brief Author announces and logs during the conversation; CI cannot verify.
330
+ - **Decision logging during chat** — the Planner announces and logs during the conversation; CI cannot verify.
330
331
  - **No execution metadata in the tranche file; no dynamic conflict scanner** — the two anti-regression rules (`tranche-model.md` §9); trusted discipline, flagged by the Archivist drift cron and the Planner's gates.
331
332
  - **`thinking.md` updates; ratification-window attendance; lock acknowledgment (advisory in V0); spec ratification passes** — all trusted.
332
333
 
@@ -400,11 +401,11 @@ No label outside this table may be applied to a task Issue or its PR. (The Archi
400
401
  |---|---|---|---|---|
401
402
  | `vinaya/tier:0` / `vinaya/tier:1` / `vinaya/tier:3` | Issue (+ mirrors the PR-body `Tier:`) | Impact tier — drives required docs (§9) and whether it merges at a ratification window. The forge has no concept of "impact." | **Planner** sets it at Issue cut (plan-time estimate). The **PR-body `Tier:`** is the binding value at merge; the Developer corrects the field if execution reveals a different tier, and re-syncs the label. | **Always-mandatory** — exactly one per task |
402
403
  | `vinaya/type:build` / `vinaya/type:chore` / `vinaya/type:docs` / `vinaya/type:feat` / `vinaya/type:fix` / `vinaya/type:perf` / `vinaya/type:refactor` / `vinaya/type:revert` / `vinaya/type:style` / `vinaya/type:test` | Issue | What kind of change the task is — the same ten commit-type names `roles/developer.md`'s commit conventions use, applied to the Issue instead of the commit. The forge has no concept of a task's shape. | **Planner** sets it at Issue cut. | **Always-mandatory, checked at Issue creation only** — `open-issue.ts` requires exactly one on every `create`; it never re-checks the label on a later `edit`, so this is forward-only from this axis's own merge without retroactively refusing an edit to an Issue that predates it (no backfill) |
403
- | `vinaya/blocked` | Issue | A block that has **no forge fact** behind it ("waiting on an answer" isn't visible from branch/PR state). | Developer/Brief Author when a task is blocked on an escalation; **removed** the moment it unblocks. | Conditional-mandatory |
404
+ | `vinaya/blocked` | Issue | A block that has **no forge fact** behind it ("waiting on an answer" isn't visible from branch/PR state). | Developer/Planner when a task is blocked on an escalation; **removed** the moment it unblocks. | Conditional-mandatory |
404
405
  | `vinaya/backlog` | Issue | Marks deliberate unplanned-ness — the Issue belongs to no tranche and is excluded from dispatch, a fact no forge field shows. | Planner/Principal when an Issue is consciously left unplanned; **removed** when a Planner promotes it into a tranche. | Conditional-mandatory |
405
406
  | `vinaya/incoherent` | Issue | A `COMPLETED` close with **no merged-PR link** — the forge shows "closed" but cannot show *whether the one law was honored* (done iff a merged `Closes #N`). Marks the anomaly for a human to resolve; AEG never auto-reopens. | Detected by `verify-coherence` (A1) / surfaced in Studio; applied when the incoherence is found, **removed** when a human clears it (link the merge, or re-close `NOT_PLANNED`). | Conditional-mandatory |
406
- | `vinaya/needs:execution-input` | Issue | Routes an open escalation to the **Brief Author** (§7). | Developer at escalation; removed when answered. | Conditional-mandatory |
407
- | `vinaya/needs:strategy-input` | Issue | Routes to the **Brief Author** — a design-path question, not an execution one. | Developer at escalation; removed when answered. | Conditional-mandatory |
407
+ | `vinaya/needs:execution-input` | Issue | Routes an open escalation to the **Planner** (§7). | Developer at escalation; removed when answered. | Conditional-mandatory |
408
+ | `vinaya/needs:strategy-input` | Issue | Routes to the **Planner** — a design-path question, not an execution one. | Developer at escalation; removed when answered. | Conditional-mandatory |
408
409
  | `vinaya/needs:principal-input` | Issue | Routes to the **Principal** — the surface the Principal scans to see what is waiting on them. | Developer at escalation; removed when answered. | Conditional-mandatory |
409
410
  | `vinaya/needs:brief-correction` | Issue/PR | The Archivist's "this brief is malformed" flag (§3, §12). | Archivist (automation); removed when the brief is fixed. | Conditional-mandatory |
410
411
  | `vinaya/override:docs` | PR | Suppresses the verify-docs gate for one PR (§12) — honored identically in `--pr` and `--push` mode (previously dead code in push mode). | **Principal only**, deliberately. | **Optional** (escape hatch) |
@@ -496,7 +497,7 @@ The Reviewer no longer carries the cognitive load of remembering *which* doc liv
496
497
  - **One-time staleness audit** of existing skills/specs to seed the initial bindings — backlog T4.
497
498
  - **Decision-number reservation** (the failure mode that caused the recent→ renumber) — backlog T2.
498
499
 
499
- **Planner §7 auto-derivation from `doc-owners`.** No longer out of scope — `packages/aeg-core`'s `deriveSection7` mechanically matches a task's intended surface globs against `doc-owners` bindings at brief-authoring time; the union of matched pointers is the floor for §7. This is a Planner/Brief-Author aid only, invoked upstream of this section's C5 gate — it does not change what C5 enforces at PR time. See `roles/planner.md`'s "Docs to keep coherent" field and `contracts/planner-brief.md`.
500
+ **Planner §7 auto-derivation from `doc-owners`.** No longer out of scope — `packages/aeg-core`'s `deriveSection7` mechanically matches a task's intended surface globs against `doc-owners` bindings at dispatch time; the union of matched pointers is the floor for §7. This is a Planner-dispatch-act aid only, invoked upstream of this section's C5 gate — it does not change what C5 enforces at PR time. See `roles/planner.md`'s "Docs to keep coherent" field and `contracts/planner-developer.md`.
500
501
 
501
502
  These are tracked as backlog Issues on the forge, not a topology file; this section governs only the seam itself.
502
503
 
@@ -530,7 +531,7 @@ Sibling to `verify-docs.ts`, both in `packages/aeg-core/bin/`. Runnable as the `
530
531
  | T2 | `orphan-task` | Every open Issue labeled `vinaya/tranche:X` appears in X's topology. **Blocks CI only for a plan PR** (its diff touches that tranche's topology file) as of see "T2 branch scoping" below |
531
532
  | T3 | `tbd-in-active-tranche` | No `#TBD` rows in an active tranche |
532
533
  | D1 | `dispatched-on-unmet-deps` | An open-PR task has all `depends-on` Issues closed |
533
- | R1 | `missing-rationale-field` | Every open task Issue's body carries all eight planner-brief rationale fields (`checkIssueRationale`), and every project its line-anchored `Project:` field names has a registry row (`checkProjectsRegistered`, resolving names through the same `projectsFromBody` parser the task derivation uses; dormant when the repo has no project registry). A `Project:` field that is present but resolves to **no** name (prose, a parenthetical, an empty value) also fails: an unparseable declaration is not the same as no declaration, and the check may not pass on a name it never received. A `Project:` line inside code — a balanced fence OR a ≥4-column-indented block — is an example, not a declaration, and is not read; a body whose FENCES do **not** balance fails closed, because an unterminated fence swallows the foot field and the read cannot be trusted (an indented block has no equivalent unterminated state — it always ends by definition, so it stays a pass like any other example). Still NOT closed, and out of this check's scope: a `Project:` line inside an HTML comment outranks the real one, since a comment is not code |
534
+ | R1 | `missing-rationale-field` | Every open task Issue's body carries all eight planner-developer rationale fields (`checkIssueRationale`), and every project its line-anchored `Project:` field names has a registry row (`checkProjectsRegistered`, resolving names through the same `projectsFromBody` parser the task derivation uses; dormant when the repo has no project registry). A `Project:` field that is present but resolves to **no** name (prose, a parenthetical, an empty value) also fails: an unparseable declaration is not the same as no declaration, and the check may not pass on a name it never received. A `Project:` line inside code — a balanced fence OR a ≥4-column-indented block — is an example, not a declaration, and is not read; a body whose FENCES do **not** balance fails closed, because an unterminated fence swallows the foot field and the read cannot be trusted (an indented block has no equivalent unterminated state — it always ends by definition, so it stays a pass like any other example). Still NOT closed, and out of this check's scope: a `Project:` line inside an HTML comment outranks the real one, since a comment is not code |
534
535
  | L1 | `stale-active-tranche` | Active tranche with zero open Issues → should archive |
535
536
  | L2 | `premature-archive` | Archived tranche with any open Issue → investigate |
536
537
  | L3 | informational | Active tranche count (does not affect exit code) |
@@ -605,7 +606,7 @@ Wired into `verify-docs.ts` full mode. Full mode is a repo-wide structural sweep
605
606
 
606
607
  ## Section 15d: Coherence Seam — Dispatch Readiness (`verify-dispatch`)
607
608
 
608
- The seam between **a task's dispatch preconditions** (row-existence, Issue-existence, dependency/conflict forge state, prior-tranche archival) and **the prose entry-gate items** `roles/developer.md` and `contracts/brief-developer.md` currently ask every Developer (and, upstream, the Brief Author) to re-derive by hand. (2026-07-13, removed prior-task/row-adjacency archival from this list — it was superseded, not one of the preconditions still enforced.) makes these preconditions mechanically re-checkable in one command, applying the same founding principle as Sections 15/15b/15c: *any fact that is knowable by querying git/the forge/the filesystem in seconds must never be asserted as prose an agent has to remember or re-derive.*
609
+ The seam between **a task's dispatch preconditions** (row-existence, Issue-existence, dependency/conflict forge state, prior-tranche archival) and **the prose entry-gate items** `roles/developer.md` and `contracts/planner-developer.md` currently ask every Developer (and, upstream, the Planner's dispatch act) to re-derive by hand. (2026-07-13, removed prior-task/row-adjacency archival from this list — it was superseded, not one of the preconditions still enforced.) makes these preconditions mechanically re-checkable in one command, applying the same founding principle as Sections 15/15b/15c: *any fact that is knowable by querying git/the forge/the filesystem in seconds must never be asserted as prose an agent has to remember or re-derive.*
609
610
 
610
611
  ### The composed gate: `packages/aeg-core/bin/verify-dispatch.ts`
611
612
 
@@ -627,7 +628,7 @@ Sibling to `verify-docs.ts`/`verify-coherence.ts`, same thin-CLI-shim discipline
627
628
 
628
629
  Exit 0 iff `checkDispatchReadiness` reports `ready: true` AND the leftover verdict is not `stop`; exit 1 otherwise, printing every failing predicate by name — mirroring the message family of `coherence-checks.ts`'s A1/A2/T2/etc.
629
630
 
630
- **`--premise <body-file>`** re-asserts every `Premise:` pin in the given body file (`parsePremiseBlock` + `checkPremises`, `src/premise-check.ts`) against the current on-disk state — the Developer's mechanized re-check of `contracts/brief-developer.md`'s premise obligation, immediately before Step 0. A failed premise means the surface moved since the brief was authored; the Developer stops and re-digs rather than executing against a stale mental model.
631
+ **`--premise <body-file>`** re-asserts every `Premise:` pin in the given body file (`parsePremiseBlock` + `checkPremises`, `src/premise-check.ts`) against the current on-disk state — the Developer's mechanized re-check of `contracts/planner-developer.md`'s premise obligation, immediately before Step 0. A failed premise means the surface moved since the brief was rendered; the Developer stops and re-digs rather than executing against a stale mental model.
631
632
 
632
633
  **`--simulate <body-file>`** dry-runs the exit gates *before* work starts — `verify-brief`, `verify-docs --pr`, and push-mode C5 (via `PR_BODY_FILE`), all against the intended body file. Because no diff exists yet at this point, premise **coverage** (which needs the real changed-file list) is not evaluated here — only that the `Premise:` block parses to at least one assertion. Full premise-coverage enforcement happens post-diff, in `verify-task.ts` (below) and, in the future, `verify-docs --pr` itself.
633
634
 
@@ -13,7 +13,7 @@ A **task** is a forge Issue. It is the bottom altitude — nothing sits below it
13
13
 
14
14
  ## 1. What a task is, and is not
15
15
 
16
- A task **is** its Issue — not a row in a file, not an entry in a tracker AEG maintains. It carries the Planner's rationale (Boundary, Sizing, Project(s)+blast radius, Dependency rationale, Traps to avoid, Suggested agent-class, Stop-and-escalate) in the Issue body, written once at plan time. Nothing downstream — not the Brief Author, not the Developer, not the Reviewer — repeats that rationale elsewhere; every role reads it from the Issue, which is its only home.
16
+ A task **is** its Issue — not a row in a file, not an entry in a tracker AEG maintains. It carries the Planner's rationale (Boundary, Sizing, Project(s)+blast radius, Dependency rationale, Traps to avoid, Suggested agent-class, Stop-and-escalate) in the Issue body, written once at plan time. Nothing downstream — not the dispatch act's render, not the Developer, not the Reviewer — repeats that rationale elsewhere; every role reads it from the Issue, which is its only home.
17
17
 
18
18
  A task's status is **not a field anyone writes.** It is computed by asking the forge what is true right now: Issue state, branch existence, PR state, review decision. This is the change that removed AEG's original fatal flaw — a hand-edited status column that raced, drifted, and lied under parallelism. `blocked` is the one state with no native forge fact behind it, so it is a label (cheap, native, doesn't race) — every other state below is read straight off the forge.
19
19
 
@@ -31,8 +31,8 @@ A closed-without-merge Issue never resolves to `todo` — `todo` implies not-sta
31
31
 
32
32
  This is the operational sequence — distinct from §2's derived-status vocabulary the same way a Milestone's flow (`milestone-model.md` §5) is distinct from its `planned`/`active`/`complete` display state. Today each stage below is a human or a thin dispatch script deciding to start the next one; the Developer, Reviewer, and every other role read prose to know what to do. Nothing about the sequence itself changes once the Atta Engine can run it as a compiled flow — same stages, same order, same role per stage; only the transition mechanism moves from a human dispatching the next turn to the engine calling the next node.
33
33
 
34
- 1. **Brief** — the Brief Author turns the task's Issue (already fully shaped by the Planner) into one executable brief: the context, the boundary, the definition of done. Just-in-time, never written before the task is picked up, never stored in the Issue (`tranche-model.md` §7).
35
- 2. **Code** — the Developer executes the brief: provisions the worktree/branch (`task/<tranche>/<n>`), implements, commits in small frequent steps, runs the tier-appropriate Task Done checklist, opens the PR with the brief pasted into the PR body. This is the stage that actually produces the change.
34
+ 1. **Brief** — the Planner's dispatch act checks the task's gates, then renders the task's Issue (already fully shaped by its plan act) into one executable brief: the context, the boundary, the definition of done. Just-in-time, never rendered before the task is picked up, posted once frozen as the Issue's own `aeg:brief:v1` comment, never stored in the Issue's own body (`tranche-model.md` §7).
35
+ 2. **Code** — the Developer executes the brief: provisions the worktree/branch (`task/<tranche>/<n>`), implements, commits in small frequent steps, runs the tier-appropriate Task Done checklist, opens the PR carrying its own report, never the brief. This is the stage that actually produces the change.
36
36
  3. **Review** — code-reviewer pass (`roles/reviewer.md`) and security pass (`roles/security.md`), each emitting a structured verdict. A REQUEST CHANGES or FAIL loops back to **Code** — bounded by a revision ceiling so the loop always ends, never open-ended.
37
37
  4. **Verify** — the tagged Test Plan runs for real: the `[agent]` items the Developer executes against the running app, the `[principal]` items a human confirms. This is the gap between "CI is green" and "the feature actually works" — static review alone cannot exercise an auth-gated, key-dependent, or browser-rendered path.
38
38
  5. **Merge** — the PR merges once Review and Verify are both satisfied. This is the boundary, not a stage with its own actor: merging is the mechanical consequence of the two gates above clearing, not a separate decision.
@@ -3,7 +3,7 @@ sidebar_title: "Template: Task brief"
3
3
  ---
4
4
  # Template — Task brief (the 12-section shape)
5
5
 
6
- **Copy the block below the divider and replace every `[…]` placeholder with real content.** This packages the brief shape defined by `aeg-root/skills/brief-authoring/SKILL.md` the required sections, their order, and the fields the gates read. The skill remains the source of truth for what each section must contain and for the authoring discipline around it (the contract-conformance checklist, the Dig obligations, the premise-pin rules, the Test Plan tagging rules); this file is the container you start from, not a substitute for reading it. The brief is **pasted to the Developer, never committed as a repo file** it lands in the PR body (inside a collapsed `<details>` block, per the skill's §12) when the Developer opens the PR.
6
+ **This file is the render's shape reference, not a hand-fill-in-the-blanks template any more.** The brief is no longer hand-authored: the Planner's dispatch act runs `vinaya task dispatch`, which mechanically renders every section below from the task Issue's own rationale and judgment sections (see `aeg-root/roles/planner.md` § The dispatch act and § The Planner's rationale) and posts the result, frozen, as the Issue's `aeg:brief:v1` comment. This file exists so a reader can see the 12-section shape the render fills the required sections, their order, and the fields the gates read without reverse-engineering it from the renderer's source. The brief is **never committed as a repo file** on its own; it is posted as the Issue comment, and a reference copy rides along inside a collapsed `<details>` block in the Developer's PR report when the Developer opens the PR.
7
7
 
8
8
  The brief itself carries no anchor comments: it rides into the PR body as the *reference copy*, and the anchored gate-read fields live in the Developer's PR report (`aeg-root/templates/pr-report-template.md`) — anchoring the same fields twice in one body would recreate the very ambiguity anchors exist to remove.
9
9
 
@@ -18,6 +18,10 @@ The brief itself carries no anchor comments: it rides into the PR body as the *r
18
18
 
19
19
  You are the AEG Developer. Read `aeg-root/roles/developer.md` first[, then the host repo's own execution-discipline skill, e.g. `.claude/skills/executor-protocol/SKILL.md`]. Both mandatory.
20
20
 
21
+ ## Objectives
22
+
23
+ [Copy the Issue's `## Objectives` section here byte-for-byte — `checkObjectivesCopy` refuses a brief whose section does not match the Issue's, compared normalised (whitespace never fails it; a changed word does). Every numbered Part in §6 must cite at least one `O<n>` from this list, and every `O<n>` here must be cited by at least one Part — `checkObjectivesCoverage` refuses either gap.]
24
+
21
25
  ## 2. Context — read before doing anything
22
26
 
23
27
  - **Tranche:** [`tranche-slug`], task [n], Issue #[N]. Branch `task/[tranche-slug]/[n]`. `Depends-on: [—|ids]`, `Conflicts-with: [—|ids]`. Confirm `READY TO DISPATCH` at your own Step 0.
@@ -36,7 +40,8 @@ You are the AEG Developer. Read `aeg-root/roles/developer.md` first[, then the h
36
40
  **Modify:**
37
41
  - [exact file paths to modify, with what changes in each]
38
42
 
39
- **Out of surface:** [adjacent files/dirs the executor must NOT touch, named explicitly]
43
+ <!-- AEG:CLAIM: packages/aeg-core/src/brief-render.ts contains:'**Out of surface:** ' + -->
44
+ **Out of surface:** [adjacent files/dirs the executor must NOT touch, named explicitly — `vinaya brief render` fills this verbatim from the Issue's `## Surface` `out:` list, never a hand-authored placeholder]
40
45
 
41
46
  #### Premise pins
42
47
 
@@ -49,7 +54,7 @@ You are the AEG Developer. Read `aeg-root/roles/developer.md` first[, then the h
49
54
  **Step 0 (mandatory, verbatim):**
50
55
 
51
56
  ```
52
- git worktree add .worktrees/task/[tranche-slug]/[n] -b task/[tranche-slug]/[n] origin/main && cd .worktrees/task/[tranche-slug]/[n] && bun install --frozen-lockfile --silent
57
+ git worktree add .worktrees/task/[tranche-slug]/[n] -b task/[tranche-slug]/[n] --no-track origin/main && cd .worktrees/task/[tranche-slug]/[n] && git config push.autoSetupRemote true && bun install --frozen-lockfile --silent
53
58
  ```
54
59
 
55
60
  1. Clean status; parent `origin/main`; branch suffix literal-matches topology `#` column (`[n]`).
@@ -60,8 +65,11 @@ On any failure: STOP and report.
60
65
 
61
66
  ## 6. Numbered parts — commit after EACH part; push once, before opening the PR
62
67
 
63
- 1. **Part 1:** [exact files + exact function/type signatures + constraints — not prose. A Part that depends on a fact about current code opens with the fenced command that establishes it, followed by the executed output (skill §2's rule).]
64
- 2. **Part 2:** [next bounded unit of work]
68
+ <!-- AEG:CLAIM: packages/aeg-core/src/brief-render.ts contains:function renderPartCitation(part: IssuePart): string { -->
69
+ [Rendered from the Issue's `## Parts` section — `vinaya brief render` fills one numbered Part per `Part <k> (O<n>[, O<m>]) — <outcome>` line, citation reconstructed verbatim, files grouped by package as today. Hand-authoring the same: exact files + exact function/type signatures + constraints — not prose. A Part that depends on a fact about current code opens with the fenced command that establishes it, followed by the executed output (skill §2's rule). Cite at least one `O<n>` from the Objectives section above; an administrative Part with no objective of its own (a changeset commit, the final push) may omit the citation.]
70
+
71
+ 1. **Part 1** (O[n]) — [exact files + exact function/type signatures + constraints — not prose.]
72
+ 2. **Part 2** (O[n]) — [next bounded unit of work]
65
73
 
66
74
  ## 7. Documentation-update list
67
75
 
@@ -69,12 +77,17 @@ On any failure: STOP and report.
69
77
 
70
78
  ## 8. Verification before claiming done
71
79
 
72
- - [the repo's static gates, by command — this repo: `bun run typecheck`, lint, build; `bunx turbo test --affected` per Part the full `bun run test` suite is CI's to run, on the one push, never the Developer's to run locally]
80
+ <!-- AEG:CLAIM: apps/cli/src/lib/artifacts.ts contains:bunx turbo test --affected --concurrency=1 || exit 1 -->
81
+
82
+ - [the repo's static gates, by command, and nothing else — this repo: `bun run typecheck`, `bun run format-and-lint`, and the production build. Do NOT ask for a test-suite run per Part: the managed `pre-push` hook runs `bunx turbo test --affected` itself, once, on the one push, and refuses the push when it fails. A brief that also asks for it per Part buys nothing and pays the suite's full wall-clock on every Part.]
73
83
  - [every blast-radius consumer named in §4 re-verified, by name]
74
84
  - `roles/developer.md`'s tier checklist genuinely satisfied, and `PR_BODY="$(cat <body-file>)" vinaya check doc-coverage` green. (On this repo's toolchain, `PR_BODY="$(cat <body-file>)" bun packages/aeg-core/bin/verify-docs.ts --pr` runs both as one command.)
75
85
 
76
86
  ## 9. Test Plan
77
87
 
88
+ <!-- AEG:CLAIM: packages/aeg-core/src/brief-render.ts contains:...facts.testPlan.lines, -->
89
+ [Rendered from the Issue's `## Test plan` section — `vinaya brief render` copies it verbatim, never re-deriving one from the surface file list.]
90
+
78
91
  - [ ] **[agent]** [scriptable, non-auth check — the exact command + the concrete observable; evidence pasted, not paraphrased]
79
92
  - [ ] **[principal]** [auth-gated / vendor-key / visual check — what the Principal does and what they should observe]
80
93
 
@@ -82,6 +95,9 @@ On any failure: STOP and report.
82
95
 
83
96
  ## 10. Stop conditions
84
97
 
98
+ <!-- AEG:CLAIM: packages/aeg-core/src/brief-render.ts contains:`${facts.rationale.stopAndEscalate}` -->
99
+ [Rendered from the Issue's `## Stop conditions` bullets, plus the Stop-and-escalate rationale field verbatim — never the field alone.]
100
+
85
101
  STOP and report if: pre-flight fails; [the Planner's stop-and-escalate conditions, substance-verbatim]; [task-specific stop conditions]; about to touch files outside the §4 surface; any destructive action not explicitly authorized.
86
102
 
87
103
  ## 11. Constraints
@@ -3,7 +3,7 @@ sidebar_title: "Template: Issue rationale"
3
3
  ---
4
4
  # Template — Issue rationale (the Planner's eight fields)
5
5
 
6
- **Copy the block below the divider into the task Issue's body and replace every `[…]` placeholder with real content.** This is the rationale grammar that the ring-0 creation gate (`vinaya issue create`/`vinaya issue edit`) and `vinaya check coherence`'s R1 check parse: all eight producer fields of the `aeg-root/contracts/planner-brief.md` contract, in bold-inline form (`**<Field>** — …`; the `### <Field>` heading form is equally valid). The contract and `aeg-root/roles/planner.md` § "The Planner's rationale" remain the source of truth for what each field must *contain* — this file packages the shape; it does not lower the reasoning bar. A field filled with boilerplate is still a malformed rationale, even though it parses.
6
+ **Copy the block below the divider into the task Issue's body and replace every `[…]` placeholder with real content.** This is the rationale grammar that the ring-0 creation gate (`vinaya issue create`/`vinaya issue edit`) and `vinaya check coherence`'s R1 check parse: a `## Objectives` section above all eight producer fields of the `aeg-root/contracts/planner-developer.md` contract, in bold-inline form (`**<Field>** — …`; the `### <Field>` heading form is equally valid). The contract and `aeg-root/roles/planner.md` §§ "The Planner's rationale" and "Rationale grammar" remain the source of truth for what each field must *contain* — this file packages the shape; it does not lower the reasoning bar. A field filled with boilerplate is still a malformed rationale, even though it parses.
7
7
 
8
8
  ---
9
9
 
@@ -13,9 +13,14 @@ sidebar_title: "Template: Issue rationale"
13
13
  **Project:** [project(s), comma-separated, matching the blast radius stated in the "Project(s) + blast radius" field below]
14
14
  **Type:** [build | chore | docs | feat | fix | perf | refactor | revert | style | test — the commit-type word this task belongs to; free-text metadata, not a forge label]
15
15
 
16
+ ## Objectives
17
+
18
+ O1. [OBJECTIVE — one observable outcome this task makes true, as a sentence — never a file path; the dispatch act's render maps it to files.]
19
+ O2. [OBJECTIVE — numbered contiguously from O1, one line per objective, as many as this task genuinely has.]
20
+
16
21
  ## Planner's rationale
17
22
 
18
- **Boundary** — [BOUNDARY — what this task is and, crucially, what it is NOT: what was deliberately split out, where the edges sit. Make the in/out sets concrete enough that the Brief Author can bound a file surface from them.]
23
+ **Boundary** — [BOUNDARY — what this task is and, crucially, what it is NOT: what was deliberately split out, where the edges sit. Make the in/out sets concrete enough that the dispatch act's render can bound a file surface from them.]
19
24
 
20
25
  **Sizing** — [SIZING — state that the task passed the four "too big?" tests (one verification story; one agent can hold it; bounded file surface; single failure mode) — or how a larger candidate was split when it failed one. Name the single verification story.]
21
26
 
@@ -31,6 +36,34 @@ sidebar_title: "Template: Issue rationale"
31
36
 
32
37
  **Docs to keep coherent** — [DOCS — which specs/skills/docs this task will make incoherent and must update, derived from reading them, not from memory — or state "No docs touched." explicitly.]
33
38
 
39
+ ## Surface
40
+
41
+ in: [directory-level glob list, comma-separated, e.g. `packages/aeg-core/src`, `apps/cli/src/commands` — never a file path]
42
+ out: [directory-level glob list explicitly excluded from this task's surface — comma-separated, or empty]
43
+
44
+ ## Parts
45
+
46
+ Part 1 (O1) — [OUTCOME — one observable outcome this Part makes true, naming outcomes and symbols, never a file path.]
47
+ Part 2 (O2) — [OUTCOME — numbered contiguously from 1, one line per Part, as many as this task genuinely has.]
48
+
49
+ ## Test plan
50
+
51
+ [Either the sentinel below, for a pure-logic task with no runtime-observable surface —]
52
+
53
+ Test plan: unit-tests-only
54
+
55
+ [— or a fenced command list, one command per line, each with its expected observable after a literal `→`, plus any auth-gated/visual `[principal]` items:]
56
+
57
+ ```
58
+ [command] → [expected observable]
59
+ ```
60
+
61
+ - [ ] **[principal]** [auth-gated / vendor-key-dependent / visual check, if any]
62
+
63
+ ## Stop conditions
64
+
65
+ - [the condition under which the executing agent must stop and escalate rather than improvise]
66
+
34
67
  ## Origin
35
68
 
36
69
  [ORIGIN — where this task came from: Principal-directed, backlog item, incident follow-up — with dates and the Issue/PR references that motivated it.]