@attalabs/vinaya 0.25.0 → 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.
- package/README.md +4 -4
- package/aeg-root/contracts/architect-planner.md +5 -5
- package/aeg-root/contracts/developer-reviewer.md +7 -7
- package/aeg-root/contracts/planner-developer.md +142 -0
- package/aeg-root/contracts/reviewer-archivist.md +2 -2
- package/aeg-root/contracts/tranche-archivist-planner.md +12 -11
- package/aeg-root/enforcement.md +15 -5
- package/aeg-root/process.md +53 -75
- package/aeg-root/roles/archivist.md +3 -3
- package/aeg-root/roles/developer.md +10 -10
- package/aeg-root/roles/planner.md +88 -23
- package/aeg-root/roles/principal.md +11 -11
- package/aeg-root/roles/reviewer.md +10 -10
- package/aeg-root/roles/security.md +7 -7
- package/aeg-root/roles/tranche-archivist.md +2 -2
- package/aeg-root/skills/aeg/SKILL.md +5 -5
- package/aeg-root/skills/aeg-roles/SKILL.md +7 -7
- package/aeg-root/state-machine.md +35 -34
- package/aeg-root/task-model.md +3 -3
- package/aeg-root/templates/brief-template.md +2 -2
- package/aeg-root/templates/issue-rationale-template.md +3 -3
- package/aeg-root/tranche-model.md +21 -21
- package/dist/checks/bin/check-body-bare-digits.js +116 -19
- package/dist/checks/bin/check-branch-topology.js +116 -19
- package/dist/checks/bin/check-brief-shape.js +121 -1101
- package/dist/checks/bin/check-changeset-coverage.js +877 -26
- package/dist/checks/bin/check-closes-n.js +116 -19
- package/dist/checks/bin/check-coherence.js +116 -19
- package/dist/checks/bin/check-dead-branch-push.js +116 -19
- package/dist/checks/bin/check-dispatch-readiness.js +119 -22
- package/dist/checks/bin/check-doc-coverage-push.js +877 -26
- package/dist/checks/bin/check-doc-coverage.js +879 -28
- package/dist/checks/bin/check-doctrine-no-procedures.js +116 -19
- package/dist/checks/bin/check-doctrine-portability.js +877 -26
- package/dist/checks/bin/check-evidence-fresh.js +116 -19
- package/dist/checks/bin/check-exec-bits.js +877 -26
- package/dist/checks/bin/check-first-push-dispatch.js +116 -19
- package/dist/checks/bin/check-issue-assignment.js +116 -19
- package/dist/checks/bin/check-main-branch-refusal.js +117 -20
- package/dist/checks/bin/check-no-disk-state.js +116 -19
- package/dist/checks/bin/check-pr-premise-reassert.js +5401 -0
- package/dist/checks/bin/check-pr-report-density.js +116 -19
- package/dist/checks/bin/check-quoted-command.js +875 -24
- package/dist/checks/bin/check-reader-resolvable-prose.js +875 -24
- package/dist/checks/bin/check-registry-gates.js +147 -21
- package/dist/checks/bin/check-retired-vocabulary.js +875 -24
- package/dist/checks/bin/check-review-gate.js +116 -19
- package/dist/checks/bin/check-single-plan-pr.js +116 -19
- package/dist/checks/bin/check-surface-scope.js +5722 -0
- package/dist/checks/bin/check-test-plan.js +116 -19
- package/dist/checks/bin/check-token-collection-wired.js +116 -19
- package/dist/checks/bin/check-token-report.js +128 -26
- package/dist/checks/bin/check-workspace-escape.js +875 -24
- package/dist/index.js +1002 -214
- package/package.json +1 -1
- package/aeg-root/contracts/brief-developer.md +0 -141
- package/aeg-root/contracts/planner-brief.md +0 -143
- package/aeg-root/roles/brief-author.md +0 -116
- package/aeg-root/skills/brief-authoring/SKILL.md +0 -509
|
@@ -178,7 +178,7 @@ FORGE VERIFICATION:
|
|
|
178
178
|
|
|
179
179
|
RETROSPECTIVE: posted to pinned lessons Issue ✓ | INCOMPLETE (reason)
|
|
180
180
|
|
|
181
|
-
ARCHIVED: aeg-root/tranches/completed/<name>.md ✓ | FAILED (reason)
|
|
181
|
+
ARCHIVED: Milestone closed (forge-native) — or `aeg-root/tranches/completed/<name>.md` for a legacy pre-cutover tranche — ✓ | FAILED (reason)
|
|
182
182
|
|
|
183
183
|
PENDING RATIFICATIONS: [list with one-line description] | none
|
|
184
184
|
|
|
@@ -222,7 +222,7 @@ When you are **self-metering** — your host exposes your session's own usage to
|
|
|
222
222
|
|
|
223
223
|
**Trigger:** explicit Principal declaration. The command is: *"Run the Tranche Archivist for tranche <name>."* Nothing else triggers you. Not a CI event. Not a merge event. Not a post-checkout hook. The Principal makes a deliberate statement.
|
|
224
224
|
|
|
225
|
-
**Dispatch:** the Principal pastes the Tranche Archivist brief (or
|
|
225
|
+
**Dispatch:** the Principal pastes the Tranche Archivist brief (or delegates that to the Planner). The brief must include the tranche name and the explicit declaration. A Tranche Archivist without a declaration refuses at the entry gate.
|
|
226
226
|
|
|
227
227
|
**Why this design:** Tranche close involves a retrospective (which requires reflection) and a "what's next" declaration (which requires judgment). These are not mechanical operations. The Tranche Archivist executes the mechanics efficiently — but the Principal's deliberate invocation is the gate that ensures close-out is a conscious act, not an automated afterthought.
|
|
228
228
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: aeg
|
|
3
3
|
sidebar_title: Operating Model (aeg)
|
|
4
|
-
description: The front door to Agentic Execution Governance (AEG) — the operating model every agent works inside. Load at the start of ANY session in this repo, before doing anything substantive, regardless of role. Covers what AEG is, the four truth domains, forge-derived status, the tranche topology file, where the plan vs the flow vs governance live, the dispatch gates, the brief, the anti-regression rules, the orient-from-root layout (`aeg-root/` model + `aeg-project/` state), and the model-vs-product distinction. Ends by routing to the aeg-roles skill and the reading order. Does NOT cover role specifics (see aeg-roles + roles/*.md) or brief
|
|
4
|
+
description: The front door to Agentic Execution Governance (AEG) — the operating model every agent works inside. Load at the start of ANY session in this repo, before doing anything substantive, regardless of role. Covers what AEG is, the four truth domains, forge-derived status, the tranche topology file, where the plan vs the flow vs governance live, the dispatch gates, the brief, the anti-regression rules, the orient-from-root layout (`aeg-root/` model + `aeg-project/` state), and the model-vs-product distinction. Ends by routing to the aeg-roles skill and the reading order. Does NOT cover role specifics (see aeg-roles + roles/*.md) or the mechanics of dispatching one task's brief (see `roles/planner.md` § The dispatch act).
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- CANONICAL SOURCE. This file is the canonical home of the `aeg` skill, inside the AEG unit (aeg-root/skills/). provides for an agent-specific GENERATED VIEW under `.claude/skills/` (or another agent's equivalent), rebuilt from this file rather than authored by hand — but no such generator exists yet, and this repo has no generated view of this skill: agents are pointed at aeg-root/ directly (this repo's root agent-context file). Edit THIS file; if a generator is ever built, regenerate rather than hand-editing its output. -->
|
|
@@ -35,7 +35,7 @@ AEG runs on **the Repo + the Git forge (GitHub/GitLab) + plain git worktrees**,
|
|
|
35
35
|
- PR merged → merged · `aeg:blocked` label → blocked
|
|
36
36
|
Labels are only `tier:*`, `aeg:blocked`, `needs:*-input` — never status.
|
|
37
37
|
2. **The Repo** = code, specs, skills, PM docs, role docs, the thin tranche topology files, decisions. The source of truth for **plan and governance** (not live status).
|
|
38
|
-
3. **The
|
|
38
|
+
3. **The task Issue's `aeg:brief:v1` comment** = the **just-in-time brief** — a task's full execution context, rendered mechanically and posted once, frozen, never hand-written. The PR body carries only the Developer's report (optionally a reference copy of the brief in a collapsed `<details>` block).
|
|
39
39
|
4. **Local filesystem** = orchestration-tool runtime, worktrees, dev servers. Ephemeral, never canonical.
|
|
40
40
|
|
|
41
41
|
Conversation logs / thinking are **not** artifacts — never cite them as authority.
|
|
@@ -61,11 +61,11 @@ Conflicts are **package-level, and mostly derived, not hand-declared** (collisio
|
|
|
61
61
|
|
|
62
62
|
## 7. The brief
|
|
63
63
|
|
|
64
|
-
The brief is the task's full execution context: **
|
|
64
|
+
The brief is the task's full execution context: **rendered mechanically, never hand-written, posted once frozen as the task Issue's own `aeg:brief:v1` comment**, amended only via escalation. If it isn't in the brief, it doesn't exist. Rendering + dispatch rules: `roles/planner.md` § The dispatch act. Brief Step 0 is always worktree creation (`git worktree add .worktrees/task/<tranche>/<n> -b task/<tranche>/<n> --no-track origin/main`).
|
|
65
65
|
|
|
66
66
|
## 8. Roles (one line each — load the role doc for detail)
|
|
67
67
|
|
|
68
|
-
Principal → Planner →
|
|
68
|
+
Principal → Planner → Developer → Reviewer (code + security) → merge, plus the non-conversational Archivist. The Planner turns intent plus a backlog slice into a tranche (its plan act) and later checks a task's gates and dispatches its rendered brief (its dispatch act) — two acts, one role. **Do not operate from this list — load your role doc.** The **aeg-roles** skill routes you to the right one.
|
|
69
69
|
|
|
70
70
|
## 9. Tiers, decisions, ratification (the governance layer)
|
|
71
71
|
|
|
@@ -78,7 +78,7 @@ Principal → Planner → Brief Author → Developer → Reviewer (code + securi
|
|
|
78
78
|
|
|
79
79
|
- ❌ Never write task status anywhere (file, Issue field, label) — it is derived from the forge.
|
|
80
80
|
- ❌ Never add execution metadata (status, PR #, dates) to the tranche topology file — topology only.
|
|
81
|
-
- ❌ Never
|
|
81
|
+
- ❌ Never hand-write the brief into the Issue body — it is rendered mechanically and posted frozen as the Issue's own `aeg:brief:v1` comment, never in the PR body.
|
|
82
82
|
- ❌ Never put planning metadata (priority, estimates, points) on an Issue — that's the roadmap, outside AEG.
|
|
83
83
|
- ❌ Never build a dynamic conflict scanner — declare conservatively and serialize.
|
|
84
84
|
- ❌ Never read or write `roadmap.md` — retired.
|
|
@@ -20,8 +20,8 @@ Role is determined by **how you were invoked** — the *kind* of surface and the
|
|
|
20
20
|
|---|---|---|
|
|
21
21
|
| A **coding-agent surface** (CLI / IDE), executing a dispatched brief | **Developer** | `roles/developer.md` |
|
|
22
22
|
| A **chat / planning surface**, talking strategy / architecture | **Planner** | `roles/planner.md` |
|
|
23
|
-
| A **chat / planning surface**, turning intent + a backlog slice into a tranche | **Planner
|
|
24
|
-
| A **chat / planning surface**,
|
|
23
|
+
| A **chat / planning surface**, turning intent + a backlog slice into a tranche | **Planner — plan act** | `roles/planner.md` |
|
|
24
|
+
| A **chat / planning surface**, dispatching one task's rendered brief | **Planner — dispatch act** | `roles/planner.md` § The dispatch act |
|
|
25
25
|
| Invoked specifically to **review an open PR** (fresh context) | **Reviewer — code** | `roles/reviewer.md` |
|
|
26
26
|
| Invoked specifically to **security-review an open PR** | **Reviewer — security** | `roles/security.md` |
|
|
27
27
|
| Executing an **open PR's runtime Test Plan before merge** (the `[agent]` half is your Developer session; the Principal runs the `[principal]` half) | **Developer — Verification phase** (not a separate role) | `roles/developer.md` § Verification |
|
|
@@ -36,10 +36,10 @@ Always also skim `roles/principal.md` to know what sits in the Principal's seat
|
|
|
36
36
|
## 2. One line + entry gate per role (then open the doc)
|
|
37
37
|
|
|
38
38
|
- **Principal** — owns direction, ratifies Type 1 decisions and Tier 3 merges. The only role that ratifies irreversible decisions.
|
|
39
|
-
- **Planner** — intent + backlog slice → a thin tranche of sibling-aware tasks (a Milestone and labeled Issues; writes no briefs, no status). Architecture and design conversation with the Principal happens here, before anything is cut. May make Type 2 decisions ACTIVE immediately; Type 1 → PENDING.
|
|
40
|
-
- **
|
|
39
|
+
- **Planner (plan act)** — intent + backlog slice → a thin tranche of sibling-aware tasks (a Milestone and labeled Issues; writes no briefs, no status). Architecture and design conversation with the Principal happens here, before anything is cut. May make Type 2 decisions ACTIVE immediately; Type 1 → PENDING.
|
|
40
|
+
- **Planner (dispatch act)** — checks one task's dispatch gates (Issue exists, dependencies merged, no open conflicting sibling, render complete), then runs `vinaya task dispatch` (see `roles/planner.md` § The dispatch act) — there is no separate brief to hand-write, the render is mechanical. **Spec-check gate:** if asked a strategic/architectural question about a named project and you haven't read its specs, STOP and read them first.
|
|
41
41
|
- **Developer** — executes ONE dispatched brief. **Entry gate:** read the brief fully; confirm dispatch gates against the forge (`depends-on` merged, no `conflicts-with` sibling PR open); **Step 0 = create the worktree** (`task/<tranche>/<n>`); then pre-flight. Opens the PR and stops — does not merge, does not review itself, never writes status.
|
|
42
|
-
- **Reviewer (code)** — invoked fresh on an open PR. **Entry gate:** an open PR
|
|
42
|
+
- **Reviewer (code)** — invoked fresh on an open PR. **Entry gate:** an open PR whose task Issue carries the frozen `aeg:brief:v1` comment, else refuse. Reads the diff + the brief (from that comment) + (advisory) the project spec; emits a VERDICT; read + review-comment authority only; does not edit code, does not merge.
|
|
43
43
|
- **Reviewer (security)** — as above, security lens; runs a config-security scan if agent/MCP config changed.
|
|
44
44
|
- **Archivist** — **entry gate:** the PR is merged, else refuse. Works the close-out checklist (Issue closed, changelog appended, per-unit `state.md` updated, provenance block posted, orphan branch/worktree flagged). Writes **no** task status — the merge *is* the status. (`now.md` is retired.)
|
|
45
45
|
- **Verification** — a **phase**, not a role: nobody is dispatched as a Verifier. It runs on an open PR after the code-review and security passes and before merge. **Entry gate:** an open PR whose brief carries a tagged Test Plan; refuse if there is no open PR, no brief, no Test Plan section, or the plan is declared `unit-tests-only` while the diff touches a runtime surface. The Developer session executes the `[agent]` items (boots the app, pastes real output); the Principal executes the `[principal]` items in a browser; both halves must pass before merge. Writes no status. Documented in `roles/developer.md` § Verification, because the Developer is the actor that runs its agent half.
|
|
@@ -47,11 +47,11 @@ Always also skim `roles/principal.md` to know what sits in the Principal's seat
|
|
|
47
47
|
|
|
48
48
|
## 3. Authority boundaries (so roles don't bleed)
|
|
49
49
|
|
|
50
|
-
- Only the **Principal** ratifies Type 1 (irreversible) decisions. The **Planner**
|
|
50
|
+
- Only the **Principal** ratifies Type 1 (irreversible) decisions. The **Planner** (either act) may ratify Type 2 (reversible) — ACTIVE immediately.
|
|
51
51
|
- The **Developer** mutates code on its branch only; it never merges, never reviews its own work, never writes status.
|
|
52
52
|
- **Reviewers** have read + PR-review-comment authority only — no code edits, no merge. Review is always a **separate, fresh-context** invocation from the Developer.
|
|
53
53
|
- The **Archivist** updates living-state PM docs at close-out but writes no task status and authors no code.
|
|
54
|
-
- **Escalation severity** routes the ask: `execution` and `strategy` →
|
|
54
|
+
- **Escalation severity** routes the ask: `execution` and `strategy` → Planner (how to run the task vs which design path to take), `product` → Principal. Labels `needs:execution-input` / `needs:strategy-input` / `needs:principal-input`.
|
|
55
55
|
|
|
56
56
|
## 4. Reminder
|
|
57
57
|
|
|
@@ -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
|
|
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,
|
|
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 →
|
|
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
|
|
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-
|
|
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,7 +72,7 @@ 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:**
|
|
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
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
|
|
|
@@ -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:**
|
|
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
|
|
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
|
|
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
|
|
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 —
|
|
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 (
|
|
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 →
|
|
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;
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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. **
|
|
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:
|
|
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
|
|
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-
|
|
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→
|
|
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
|
|
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
|
|
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** —
|
|
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/
|
|
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 **
|
|
407
|
-
| `vinaya/needs:strategy-input` | Issue | Routes to the **
|
|
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
|
|
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-
|
|
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/
|
|
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/
|
|
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
|
|
package/aeg-root/task-model.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
**
|
|
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
|
|
|
@@ -54,7 +54,7 @@ You are the AEG Developer. Read `aeg-root/roles/developer.md` first[, then the h
|
|
|
54
54
|
**Step 0 (mandatory, verbatim):**
|
|
55
55
|
|
|
56
56
|
```
|
|
57
|
-
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
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
1. Clean status; parent `origin/main`; branch suffix literal-matches topology `#` column (`[n]`).
|
|
@@ -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: a `## Objectives` section above all eight producer fields of the `aeg-root/contracts/planner-
|
|
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
|
|
|
@@ -15,12 +15,12 @@ sidebar_title: "Template: Issue rationale"
|
|
|
15
15
|
|
|
16
16
|
## Objectives
|
|
17
17
|
|
|
18
|
-
O1. [OBJECTIVE — one observable outcome this task makes true, as a sentence — never a file path; the
|
|
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
19
|
O2. [OBJECTIVE — numbered contiguously from O1, one line per objective, as many as this task genuinely has.]
|
|
20
20
|
|
|
21
21
|
## Planner's rationale
|
|
22
22
|
|
|
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
|
|
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.]
|
|
24
24
|
|
|
25
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.]
|
|
26
26
|
|