mandrel 1.65.0 → 1.67.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/.agents/docs/configuration.md +7 -0
- package/.agents/docs/workflows.md +3 -2
- package/.agents/personas/engineer.md +27 -0
- package/.agents/schemas/agentrc.schema.json +38 -0
- package/.agents/schemas/audit-rules.json +12 -0
- package/.agents/scripts/epic-audit-prepare.js +53 -3
- package/.agents/scripts/epic-plan-healthcheck.js +191 -2
- package/.agents/scripts/lib/audit-suite/index.js +5 -0
- package/.agents/scripts/lib/audit-suite/selector.js +73 -0
- package/.agents/scripts/lib/config-settings-schema-quality.js +13 -0
- package/.agents/scripts/lib/config-settings-schema.js +11 -0
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +82 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +43 -18
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-authoring-grounding.js +147 -0
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +63 -0
- package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +11 -0
- package/.agents/scripts/lib/orchestration/retro-runner.js +19 -2
- package/.agents/scripts/lib/orchestration/spec-freshness.js +2 -2
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +17 -0
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +138 -0
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +15 -0
- package/.agents/skills/skills.index.json +12 -2
- package/.agents/workflows/audit-navigability.md +129 -0
- package/.agents/workflows/helpers/deliver-epic.md +116 -1
- package/.agents/workflows/helpers/plan-epic.md +60 -5
- package/.agents/workflows/qa-assist.md +214 -141
- package/docs/CHANGELOG.md +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: epic-plan-premortem
|
|
3
|
+
description: >-
|
|
4
|
+
Run a fresh-context, code-reading pre-mortem critic over the draft Story
|
|
5
|
+
ticket array an Epic's decompose phase produced. Use during Phase 8 of
|
|
6
|
+
`/plan`, after `epic-plan-decompose-author` / `epic-plan-consolidate` write
|
|
7
|
+
`temp/epic-<Epic_ID>/tickets.json` and before `epic-plan-decompose.js`
|
|
8
|
+
validates and persists it. Reads the PRD / Tech Spec AND the actual cited
|
|
9
|
+
code surfaces, then emits predicted-rework findings before any GitHub write.
|
|
10
|
+
allowed_tools:
|
|
11
|
+
- Read
|
|
12
|
+
- Write
|
|
13
|
+
- Bash
|
|
14
|
+
- Grep
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# epic-plan-premortem
|
|
18
|
+
|
|
19
|
+
## Policy Capsule
|
|
20
|
+
|
|
21
|
+
- Run only after a draft `temp/epic-<Epic_ID>/tickets.json` exists (authored by `epic-plan-decompose-author`, and consolidated by `epic-plan-consolidate` if that pass ran); fail loudly if the draft array is missing. Read the PRD / Tech Spec from `temp/epic-<Epic_ID>/decomposer-context.json` (the same envelope the author skill consumed) — never re-fetch from GitHub, and never call the GitHub API from this Skill.
|
|
22
|
+
- **You MUST read the actual cited code surfaces.** For every Story, open the files named in its `changes[]` / `references[]` (resolve each path against the repo root; use `Read` / `Grep`) and read enough of each to judge whether the Story's `acceptance[]` is verifiable against the real code and whether its `changes[]` assumptions hold. This is the load-bearing difference between this critic and the structural file-assumption gate: that gate proves a path **exists** (or does not); this critic reads what the file actually **contains**. A pre-mortem that did not open the cited files has not run.
|
|
23
|
+
- Emit exactly one artifact: a human-readable `temp/epic-<Epic_ID>/premortem-report.md` — the predicted-rework findings the operator reviews at the Phase 8 HITL diff. It MUST exist before returning.
|
|
24
|
+
- **This critic never writes to GitHub and never persists `tickets.json`.** It is read-and-report only: it does NOT mutate the draft array, does NOT create issues, and does NOT flip any label. Re-authoring on its findings is the author skill's job (the workflow re-runs `epic-plan-decompose-author` on the report before the persist call).
|
|
25
|
+
- **You are not scope-preserving.** Unlike `epic-plan-consolidate` (merge-and-rewire only), this critic MAY recommend splitting an under-specified Story, tightening or rewording an acceptance criterion, or flagging an over-specified Story — because it only *recommends* in a report; it never applies the change itself. The conservation invariant belongs to consolidation; this pass is deliberately a separate, additive-recommendation lens.
|
|
26
|
+
- Hunt for the three predicted-rework finding classes the structural gates cannot catch: **(1) unverifiable acceptance criteria** (an AC no `verify[]` command or readable code state can prove); **(2) over- or under-specified Stories** (a Story whose `acceptance[]` is far broader or narrower than its `changes[]` footprint and the cited code support); **(3) semantically-wrong assumptions** (the cited file exists but does not contain the seam / export / shape the Story assumes — the file-assumption gate passes, the work would still rework).
|
|
27
|
+
- Log only file/route/Story identifiers and short rationale in the report — never paste full source bodies, persona data, or secret material (per `rules/security-baseline.md` data-leakage MUSTs).
|
|
28
|
+
|
|
29
|
+
## Role
|
|
30
|
+
|
|
31
|
+
Senior Engineer + Architect, acting as a **fresh-context pre-mortem critic**.
|
|
32
|
+
This Skill is deliberately *separate* from `epic-plan-decompose-author` (the
|
|
33
|
+
generator) and from `epic-plan-consolidate` (the scope-preserving merge critic):
|
|
34
|
+
a same-pass self-critique is the weak mode this is built to escape. The
|
|
35
|
+
generator maps PRD capabilities to Stories against the spec text; this critic
|
|
36
|
+
opens the **actual cited code** and asks "if I tried to deliver this exact
|
|
37
|
+
backlog, where would it rework?" — before any GitHub write makes the rework
|
|
38
|
+
expensive.
|
|
39
|
+
|
|
40
|
+
## When to use
|
|
41
|
+
|
|
42
|
+
`/plan` Phase 8, as the **8.5 — Planning Pre-Mortem Critic** sub-step:
|
|
43
|
+
after `epic-plan-decompose-author` writes (and, when present,
|
|
44
|
+
`epic-plan-consolidate` consolidates) `temp/epic-<Epic_ID>/tickets.json`, after
|
|
45
|
+
the reachability completeness critic (8.4), and **before**
|
|
46
|
+
`epic-plan-decompose.js --tickets …` validates and persists. The pass operates
|
|
47
|
+
on the temp artifact and emits a report so the operator sees predicted rework in
|
|
48
|
+
the Phase 8 HITL diff before the GitHub write; the author re-runs on the
|
|
49
|
+
findings and the deterministic validator runs *after*, so nothing this critic
|
|
50
|
+
surfaces reaches GitHub unreviewed.
|
|
51
|
+
|
|
52
|
+
## Inputs
|
|
53
|
+
|
|
54
|
+
The workflow passes the Epic ID as the Skill argument. The Skill itself reads:
|
|
55
|
+
|
|
56
|
+
- `temp/epic-<Epic_ID>/tickets.json` — the **draft** (or consolidated) Story
|
|
57
|
+
array. This is the pre-mortem subject.
|
|
58
|
+
- `temp/epic-<Epic_ID>/decomposer-context.json` — the authoring envelope emitted
|
|
59
|
+
by `epic-plan-decompose.js --emit-context`. Read `prd.body` / `prd` and
|
|
60
|
+
`techSpec.body` / `techSpec` from it.
|
|
61
|
+
- **The repository working tree** — the actual files each Story's `changes[]` /
|
|
62
|
+
`references[]` name. Resolve each path against the repo root and read it.
|
|
63
|
+
|
|
64
|
+
## Outputs
|
|
65
|
+
|
|
66
|
+
- `temp/epic-<Epic_ID>/premortem-report.md` — a human-readable findings report.
|
|
67
|
+
Each finding names its Story, the cited surface it read, the finding class
|
|
68
|
+
(unverifiable-AC / over-or-under-specified / wrong-assumption), a one-line
|
|
69
|
+
rationale grounded in what the file actually contains, and a recommended
|
|
70
|
+
re-authoring action. End with a one-line verdict
|
|
71
|
+
(`findings: N` / `findings: 0 — no predicted rework`).
|
|
72
|
+
|
|
73
|
+
This file MUST exist before the Skill returns. The Skill writes **no** other
|
|
74
|
+
artifact and mutates **no** GitHub state.
|
|
75
|
+
|
|
76
|
+
## Procedure
|
|
77
|
+
|
|
78
|
+
### Step 1 — Load the draft and the spec
|
|
79
|
+
|
|
80
|
+
Read `temp/epic-<Epic_ID>/tickets.json` (the Story array) and
|
|
81
|
+
`temp/epic-<Epic_ID>/decomposer-context.json` (for the PRD / Tech Spec). If the
|
|
82
|
+
draft array is missing, fail loudly and instruct the caller to run the
|
|
83
|
+
`epic-plan-decompose-author` Skill first.
|
|
84
|
+
|
|
85
|
+
### Step 2 — Read the cited code surfaces
|
|
86
|
+
|
|
87
|
+
For each Story, collect the paths in its `changes[]` and `references[]`. For
|
|
88
|
+
each path that exists in the working tree, `Read` it (or `Grep` for the specific
|
|
89
|
+
export / seam the Story assumes when the file is large). Build, per Story, a
|
|
90
|
+
short note of what the cited code actually contains versus what the Story's
|
|
91
|
+
`acceptance[]` / `changes[]` assume.
|
|
92
|
+
|
|
93
|
+
### Step 3 — Hunt the three finding classes
|
|
94
|
+
|
|
95
|
+
Across the backlog, surface predicted rework:
|
|
96
|
+
|
|
97
|
+
- **Unverifiable AC** — an acceptance item nothing in `verify[]` (or readable
|
|
98
|
+
code state) can prove. Recommend a concrete verify command or a reworded,
|
|
99
|
+
checkable AC.
|
|
100
|
+
- **Over- / under-specified Story** — `acceptance[]` materially broader or
|
|
101
|
+
narrower than the `changes[]` footprint and the cited code support. Recommend
|
|
102
|
+
a split (under-specified one Story doing the work of several) or a tightening.
|
|
103
|
+
- **Semantically-wrong assumption** — the cited file exists (so the
|
|
104
|
+
file-assumption gate passes) but does not contain the seam, export, function,
|
|
105
|
+
or data shape the Story assumes. Recommend the corrected target or an explicit
|
|
106
|
+
"create the seam first" Story.
|
|
107
|
+
|
|
108
|
+
Record each finding with its Story, cited surface, class, rationale, and
|
|
109
|
+
recommended action.
|
|
110
|
+
|
|
111
|
+
### Step 4 — Write the report
|
|
112
|
+
|
|
113
|
+
Write all findings to `temp/epic-<Epic_ID>/premortem-report.md` with the verdict
|
|
114
|
+
line. Paste identifiers and short rationale only — never full source bodies.
|
|
115
|
+
|
|
116
|
+
### Step 5 — Hand back to `/plan`
|
|
117
|
+
|
|
118
|
+
Return control. The workflow shows the operator the pre-mortem report at the
|
|
119
|
+
Phase 8 HITL diff; on operator approval it re-runs
|
|
120
|
+
`epic-plan-decompose-author` on the findings **before** the persist call
|
|
121
|
+
(`node .agents/scripts/epic-plan-decompose.js --epic <Epic_ID> --tickets
|
|
122
|
+
temp/epic-<Epic_ID>/tickets.json`). This Skill itself persists nothing.
|
|
123
|
+
|
|
124
|
+
## Constraints
|
|
125
|
+
|
|
126
|
+
- Do **not** call the GitHub API from this Skill. It reads temp artifacts plus
|
|
127
|
+
the working tree and writes one temp report; persistence belongs to the
|
|
128
|
+
script, re-authoring belongs to `epic-plan-decompose-author`.
|
|
129
|
+
- Do **not** write outside `temp/epic-<Epic_ID>/`, and do **not** mutate
|
|
130
|
+
`temp/epic-<Epic_ID>/tickets.json` — this critic is report-only.
|
|
131
|
+
- Do **not** log full source bodies, persona data, or secrets into the report
|
|
132
|
+
(per `rules/security-baseline.md`). Identifiers and short rationale only.
|
|
133
|
+
- If `temp/epic-<Epic_ID>/tickets.json` is missing, fail loudly and instruct the
|
|
134
|
+
caller to run the `epic-plan-decompose-author` Skill first.
|
|
135
|
+
- The validator
|
|
136
|
+
([`lib/orchestration/ticket-validator.js`](../../../scripts/lib/orchestration/ticket-validator.js))
|
|
137
|
+
remains the authoritative post-re-author gate. This critic surfaces
|
|
138
|
+
*semantic* rework the structural validator cannot — it does not replace it.
|
|
@@ -72,6 +72,21 @@ reads:
|
|
|
72
72
|
`codebaseSnapshot.files`, surface that as a `<!-- DRIFT -->` callout
|
|
73
73
|
in the Tech Spec body naming the cited path, so the freshness gate
|
|
74
74
|
(Story #2635) has prose context for the operator to read.
|
|
75
|
+
- `codebaseSnapshot.grounding` (Story #4139) — operator-visible
|
|
76
|
+
grounding signals derived before you author:
|
|
77
|
+
- `grounding.truncation` — non-null when the snapshot dropped files
|
|
78
|
+
(the skinny-tier cap kept only the first ~250 of N matched files).
|
|
79
|
+
Carries `{ dropped, matched, shown, tier, remedies[] }`. When it is
|
|
80
|
+
present, the file tree you see is **partial** — do not assume a
|
|
81
|
+
module is absent just because it is missing from
|
|
82
|
+
`codebaseSnapshot.files`; raise the partiality in a `<!-- DRIFT -->`
|
|
83
|
+
callout and prefer the `medium` tier (or a narrowed `include`) for
|
|
84
|
+
a grounded spec.
|
|
85
|
+
- `grounding.citedButAbsent[]` — paths the Epic body already cites
|
|
86
|
+
that are **not** in the snapshot and are not phrased as net-new.
|
|
87
|
+
Treat each as a likely drift signal: confirm the path exists (it may
|
|
88
|
+
have been dropped by truncation) or mark it net-new explicitly in
|
|
89
|
+
the spec so the post-author freshness gate does not flag it.
|
|
75
90
|
- `systemPrompts.prd`, `systemPrompts.techSpec`, and
|
|
76
91
|
`systemPrompts.acceptanceSpec` — left in the envelope as a backstop;
|
|
77
92
|
this Skill's own body below carries the authoritative versions and is
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-06-
|
|
2
|
+
"generatedAt": "2026-06-15T13:34:12.163Z",
|
|
3
3
|
"generator": "generate-skills-index.js@1",
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
@@ -138,10 +138,20 @@
|
|
|
138
138
|
"category": "core",
|
|
139
139
|
"path": ".agents/skills/core/epic-plan-decompose-author/SKILL.md",
|
|
140
140
|
"description": "Author the Story ticket JSON for an Epic from the decomposer authoring context emitted by `epic-plan-decompose.js --emit-context`. Use during Phase 8 of `/plan` when the host LLM needs to write the ticket array before `epic-plan-decompose.js` validates and persists it.",
|
|
141
|
-
"policyCapsuleBullets":
|
|
141
|
+
"policyCapsuleBullets": 14,
|
|
142
142
|
"allowedTools": ["Read", "Write", "Bash"],
|
|
143
143
|
"vendor": null
|
|
144
144
|
},
|
|
145
|
+
{
|
|
146
|
+
"name": "epic-plan-premortem",
|
|
147
|
+
"tier": "core",
|
|
148
|
+
"category": "core",
|
|
149
|
+
"path": ".agents/skills/core/epic-plan-premortem/SKILL.md",
|
|
150
|
+
"description": "Run a fresh-context, code-reading pre-mortem critic over the draft Story ticket array an Epic's decompose phase produced. Use during Phase 8 of `/plan`, after `epic-plan-decompose-author` / `epic-plan-consolidate` write `temp/epic-<Epic_ID>/tickets.json` and before `epic-plan-decompose.js` validates and persists it. Reads the PRD / Tech Spec AND the actual cited code surfaces, then emits predicted-rework findings before any GitHub write.",
|
|
151
|
+
"policyCapsuleBullets": 7,
|
|
152
|
+
"allowedTools": ["Read", "Write", "Bash", "Grep"],
|
|
153
|
+
"vendor": null
|
|
154
|
+
},
|
|
145
155
|
{
|
|
146
156
|
"name": "epic-plan-spec-author",
|
|
147
157
|
"tier": "core",
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: >-
|
|
3
|
+
Audit the whole route tree against the consumer's nav-registry SSOT —
|
|
4
|
+
every route has a persona nav door and no nav href is dead. A
|
|
5
|
+
deliberately-global lens (Epic #4131, F2/F3) exempt from the
|
|
6
|
+
cross-epic-leak guard and routed onto route-adding change sets.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Navigability Audit
|
|
10
|
+
|
|
11
|
+
## Role
|
|
12
|
+
|
|
13
|
+
Information-Architecture Reviewer & Frontend Navigation Auditor
|
|
14
|
+
|
|
15
|
+
## Context & Objective
|
|
16
|
+
|
|
17
|
+
Evaluate the application's **navigability**: the property that every route a
|
|
18
|
+
persona is entitled to reach has a real navigation door (a menu item, link, or
|
|
19
|
+
button rendered in that persona's authenticated shell), and that every nav door
|
|
20
|
+
points at a route that actually exists. The two failure modes this lens names
|
|
21
|
+
are symmetric:
|
|
22
|
+
|
|
23
|
+
- **Orphaned route** — a route is registered in the route tree but no
|
|
24
|
+
nav-registry entry surfaces it for any persona. The feature ships but is
|
|
25
|
+
unreachable through the product; only a hardcoded deep-link reaches it.
|
|
26
|
+
- **Dead nav href** — a nav-registry entry points at a route (or path) that
|
|
27
|
+
does not exist in the route tree. The door is rendered but leads nowhere.
|
|
28
|
+
|
|
29
|
+
This is **mechanism, not content**: the lens reads the consumer's
|
|
30
|
+
configured route tree and nav-registry SSOT (see _Configuration_) and is a
|
|
31
|
+
**silent no-op when neither is configured**. Mandrel ships the slot and the
|
|
32
|
+
wiring; it never ships a specific consumer's route data or nav registry.
|
|
33
|
+
|
|
34
|
+
## Whole-route-tree scope (global lens — leak-guard-exempt)
|
|
35
|
+
|
|
36
|
+
Unlike the change-set-scoped lenses, this lens **always evaluates the whole
|
|
37
|
+
route tree + the whole nav registry**, even when the change that triggered it
|
|
38
|
+
touched only one route file. Reachability is a global property: adding one
|
|
39
|
+
route can orphan it, but removing or renaming a route elsewhere can also break
|
|
40
|
+
a nav href that the change set never touched.
|
|
41
|
+
|
|
42
|
+
Because of this, the navigability lens is registered in the **global-lens
|
|
43
|
+
allowlist** (`GLOBAL_LENS_ALLOWLIST` in
|
|
44
|
+
[`lib/audit-suite/selector.js`](../scripts/lib/audit-suite/selector.js)) and is
|
|
45
|
+
**exempt from the cross-epic-leak guard** (`#3362`) that narrows every other
|
|
46
|
+
lens's evidence to the Epic's `changedFiles`. The exemption is scoped to this
|
|
47
|
+
lens only — the guard is **not** weakened for any other lens, and the
|
|
48
|
+
exemption never lets a foreign Epic's change set leak into a scoped lens.
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
{{changedFiles}}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- For this lens, **ignore** the `{{changedFiles}}` block above even when it is
|
|
55
|
+
populated: navigability is evaluated codebase-wide regardless. The block is
|
|
56
|
+
rendered only for envelope-shape parity with the scoped lenses.
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
Read the consumer's navigability config (resolved from `.agentrc.json`):
|
|
61
|
+
|
|
62
|
+
- `delivery.quality.navigability.routeGlobs` — globs identifying the
|
|
63
|
+
route-adding files / route tree (e.g. `pages/**`, `app/**/route.ts`). Drives
|
|
64
|
+
both the route-tree enumeration here and the route-added routing in
|
|
65
|
+
[`epic-audit-prepare.js`](../scripts/epic-audit-prepare.js).
|
|
66
|
+
- `delivery.quality.navigability.navRegistry` — path(s) to the consumer's
|
|
67
|
+
nav-registry SSOT this lens reads.
|
|
68
|
+
|
|
69
|
+
If **neither** `routeGlobs` nor `navRegistry` is present, emit a one-line
|
|
70
|
+
"navigability not configured — skipped" note and exit without findings. Do
|
|
71
|
+
**not** invent a route tree or guess a nav registry.
|
|
72
|
+
|
|
73
|
+
## Step 1: Enumerate the route tree
|
|
74
|
+
|
|
75
|
+
Enumerate every route from the files matched by `routeGlobs`. Record each
|
|
76
|
+
route's path and the persona(s) entitled to reach it (from route metadata,
|
|
77
|
+
guards, or the consumer's documented persona model). Log route **identifiers
|
|
78
|
+
only** — never the full route body or any persona PII.
|
|
79
|
+
|
|
80
|
+
## Step 2: Enumerate the nav registry
|
|
81
|
+
|
|
82
|
+
Read every nav door from the `navRegistry` SSOT. Record each door's target
|
|
83
|
+
path and the persona shell it renders in.
|
|
84
|
+
|
|
85
|
+
## Step 3: Cross-check (the two invariants)
|
|
86
|
+
|
|
87
|
+
1. **Every route has a persona nav door.** For each enumerated route, assert at
|
|
88
|
+
least one nav-registry entry surfaces it for an entitled persona. A route
|
|
89
|
+
with no door for any of its personas is an **orphaned route**.
|
|
90
|
+
2. **No nav href is dead.** For each nav door, assert its target resolves to a
|
|
91
|
+
real route in the route tree. A door whose target is absent is a **dead nav
|
|
92
|
+
href**.
|
|
93
|
+
|
|
94
|
+
## Step 4: Output Requirements
|
|
95
|
+
|
|
96
|
+
Generate and save a structured Markdown audit report to
|
|
97
|
+
`{{auditOutputDir}}/audit-navigability-results.md`, using the template below.
|
|
98
|
+
|
|
99
|
+
```markdown
|
|
100
|
+
# Navigability Audit report
|
|
101
|
+
|
|
102
|
+
## Executive Summary
|
|
103
|
+
|
|
104
|
+
[Reachability health (Score 1-10): count of orphaned routes and dead hrefs.]
|
|
105
|
+
|
|
106
|
+
## Detailed Findings
|
|
107
|
+
|
|
108
|
+
[For every orphaned route or dead nav href, use the following strict
|
|
109
|
+
structure:]
|
|
110
|
+
|
|
111
|
+
### [Short Title of the Issue]
|
|
112
|
+
|
|
113
|
+
- **Dimension:** [Orphaned Route | Dead Nav Href]
|
|
114
|
+
- **Impact:** [High | Medium | Low]
|
|
115
|
+
- **Route / Door:** [the route path or nav-door identifier — identifier only]
|
|
116
|
+
- **Persona(s):** [the persona(s) affected]
|
|
117
|
+
- **Current State:** [why the route is unreachable or the href is dead]
|
|
118
|
+
- **Recommendation & Rationale:** [the nav-registry change that restores
|
|
119
|
+
reachability — add a door for the orphaned route, or fix/remove the dead
|
|
120
|
+
href]
|
|
121
|
+
- **Agent Prompt:**
|
|
122
|
+
`[A copy-pasteable, specific prompt to execute the nav-registry fix.]`
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Constraint
|
|
126
|
+
|
|
127
|
+
This is a **read-only** audit. Provide the critique and the nav-registry fixes,
|
|
128
|
+
but do not modify the route tree or the nav registry. Log route and door
|
|
129
|
+
identifiers only — never full route bodies, source contents, or persona data.
|
|
@@ -31,6 +31,7 @@ back to the operator-merges-button path.
|
|
|
31
31
|
→ Phase 4 — epic-audit (helpers/epic-audit.md — change-set audits via selectAudits)
|
|
32
32
|
→ Phase 5 — code-review (helpers/code-review.md with scope: epic)
|
|
33
33
|
→ Phase 6 — retro (.agents/scripts/lib/orchestration/retro-runner.js)
|
|
34
|
+
→ Phase 6.5 — integration gate (whole-product navigability + journey suite; @pending ≠ green for surface-adding Epics — blocks finalize)
|
|
34
35
|
→ Phase 7 — finalize (lifecycle-emit → epic.close.end → open PR to main)
|
|
35
36
|
→ Phase 8 — watch-and-iterate (poll `gh pr checks`; fix locally until green)
|
|
36
37
|
→ Phase 8.5 — auto-merge gate (lifecycle-emit → epic.automerge.start)
|
|
@@ -52,7 +53,7 @@ spawned.
|
|
|
52
53
|
## Arguments
|
|
53
54
|
|
|
54
55
|
```text
|
|
55
|
-
/deliver <epicId> [--skip-epic-audit] [--skip-code-review] [--skip-retro] [--full-retro]
|
|
56
|
+
/deliver <epicId> [--skip-epic-audit] [--skip-code-review] [--skip-retro] [--full-retro] [--skip-integration-gate]
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
- `epicId` — must carry `type::epic`. Otherwise STOP and tell the operator
|
|
@@ -63,6 +64,12 @@ spawned.
|
|
|
63
64
|
- `--skip-retro` — skip Phase 6 (use sparingly).
|
|
64
65
|
- `--full-retro` — force the six-section retro regardless of manifest
|
|
65
66
|
cleanliness. `--skip-retro` wins over `--full-retro`.
|
|
67
|
+
- `--skip-integration-gate` — skip Phase 6.5 (log the override). The
|
|
68
|
+
explicit operator override for the post-wave integration gate, consistent
|
|
69
|
+
with `--skip-epic-audit`. Use only when the deliberately-global checks are
|
|
70
|
+
known to be irrelevant for this Epic (e.g., a docs-only Epic that adds no
|
|
71
|
+
surface). Skipping the gate is recorded as a manual intervention and
|
|
72
|
+
disqualifies auto-merge, exactly like the other `--skip-*` overrides.
|
|
66
73
|
|
|
67
74
|
Every other runtime modifier is sourced from the Epic's labels or from
|
|
68
75
|
`delivery.deliverRunner` in `.agentrc.json`.
|
|
@@ -556,6 +563,109 @@ and never fails the phase.
|
|
|
556
563
|
|
|
557
564
|
---
|
|
558
565
|
|
|
566
|
+
## Phase 6.5 — Post-wave integration gate (Epic #4131, F1/F4)
|
|
567
|
+
|
|
568
|
+
This phase runs **after** the Phase 2 wave loop reports `epic-complete` and
|
|
569
|
+
**before** the Phase 7 finalize emit opens the PR to `main`. Where every gate
|
|
570
|
+
before it (`selectAudits`, the cross-epic-leak guard `#3362`, diff-against-base,
|
|
571
|
+
the file-assumption gate) is **change-set-scoped** — it narrows its evidence to
|
|
572
|
+
the Epic's `changedFiles` — this is the one **deliberately-global** gate: its
|
|
573
|
+
evidence spans the **whole product**, not just the change set. The disease it
|
|
574
|
+
catches is the surface that each Story shipped correctly in isolation yet that
|
|
575
|
+
the assembled product cannot reach: a route nobody can navigate to, or a
|
|
576
|
+
persona journey that the integrated waves silently broke. A change-set lens
|
|
577
|
+
never sees that, because no single Story's diff contains the orphan.
|
|
578
|
+
|
|
579
|
+
Skip when `--skip-integration-gate` (log the override; record a manual
|
|
580
|
+
intervention per [§ Recording manual interventions](#recording-manual-interventions)).
|
|
581
|
+
The gate is otherwise **always evaluated**, but it is a **silent no-op when
|
|
582
|
+
unconfigured** (see *No-op when unconfigured* below) so consumers who ship no
|
|
583
|
+
nav config and no journey suite are unaffected.
|
|
584
|
+
|
|
585
|
+
### 6.5a — Whole-product navigability (reuses the Phase 4 lens)
|
|
586
|
+
|
|
587
|
+
Run the `navigability` lens
|
|
588
|
+
([`audit-navigability.md`](../audit-navigability.md), the deliberately-global
|
|
589
|
+
lens delivered in this Epic's wave 0) in **whole-route mode** against the
|
|
590
|
+
integrated `epic/<epicId>` tip. Unlike its Phase 4 change-set-routed invocation,
|
|
591
|
+
here it is driven explicitly over the consumer's **entire** route tree + nav
|
|
592
|
+
registry — it is on the global-lens allowlist (`GLOBAL_LENS_ALLOWLIST` in
|
|
593
|
+
[`lib/audit-suite/selector.js`](../../scripts/lib/audit-suite/selector.js)) and
|
|
594
|
+
exempt from the cross-epic-leak guard `#3362`, so a route orphaned anywhere in
|
|
595
|
+
the product is in scope even when no Story in this Epic touched it.
|
|
596
|
+
|
|
597
|
+
The lens reads the consumer's navigability config (resolved from `.agentrc.json`):
|
|
598
|
+
|
|
599
|
+
- `delivery.quality.navigability.routeGlobs` — the route-tree SSOT the lens
|
|
600
|
+
enumerates.
|
|
601
|
+
- `delivery.quality.navigability.navRegistry` — the nav-registry SSOT the lens
|
|
602
|
+
cross-checks every route against.
|
|
603
|
+
|
|
604
|
+
A whole-product finding is a **hard failure**: an **orphaned route** (a route
|
|
605
|
+
with no nav door for any entitled persona) or a **dead nav href** (a nav door
|
|
606
|
+
pointing at a route that no longer exists). On a hard failure the gate **blocks
|
|
607
|
+
finalize** and names the orphaned surface (route identifier / nav-door
|
|
608
|
+
identifier only — never the route body or persona PII, per
|
|
609
|
+
`security-baseline.md`). Do **not** proceed to Phase 7.
|
|
610
|
+
|
|
611
|
+
### 6.5b — Consumer journey suite
|
|
612
|
+
|
|
613
|
+
Run the consumer's per-persona journey suite — the integrated, cross-Epic
|
|
614
|
+
persona-journey verification — over the `epic/<epicId>` tip:
|
|
615
|
+
|
|
616
|
+
- `delivery.quality.navigability.journeySuite` — path/command for the journey
|
|
617
|
+
suite. Absent ⇒ this sub-step is skipped (no-op).
|
|
618
|
+
|
|
619
|
+
A failing journey (a persona cannot complete an end-to-end journey through the
|
|
620
|
+
assembled product) is a **hard failure**: block finalize and name the broken
|
|
621
|
+
journey. This is the runtime complement to 6.5a's static reachability check —
|
|
622
|
+
6.5a proves a door exists; the journey suite proves the door, and everything
|
|
623
|
+
behind it, actually works when the waves are integrated.
|
|
624
|
+
|
|
625
|
+
### 6.5c — `@pending` ≠ green for surface-adding Epics (F4)
|
|
626
|
+
|
|
627
|
+
The Phase 7 finalize chain runs the acceptance-spec reconciler
|
|
628
|
+
([`acceptance-spec-reconciler.js`](../../scripts/acceptance-spec-reconciler.js)),
|
|
629
|
+
which classifies every AC ID into `satisfied` (covered by a non-pending
|
|
630
|
+
scenario), `pending` (covered **only** by scenarios tagged `@pending`), or
|
|
631
|
+
`missing`. For a **surface-adding Epic** — one whose risk verdict carries a
|
|
632
|
+
surface-adding signal (a route-adding change set, the `navigability` lens routed
|
|
633
|
+
in Phase 4, or a configured `routeGlobs` match) — this phase tightens that
|
|
634
|
+
contract: an AC whose coverage is **only `@pending`** is treated as
|
|
635
|
+
**unsatisfied**, not green. A surface-adding Epic whose acceptance coverage is
|
|
636
|
+
**only `@pending`** therefore **fails the close gate** instead of passing —
|
|
637
|
+
shipping a new surface behind a deferred-forever `@pending` scenario is exactly
|
|
638
|
+
the late-gate gap this Epic exists to close.
|
|
639
|
+
|
|
640
|
+
This is **purely additive** and **scoped to surface-adding Epics**:
|
|
641
|
+
refactor-only and docs-only Epics (no surface-adding signal) are **unaffected** —
|
|
642
|
+
their `@pending` handling is exactly as before, and the existing
|
|
643
|
+
`satisfied` / `missing` reconciliation is **not** de-scoped for any Epic.
|
|
644
|
+
|
|
645
|
+
### No-op when unconfigured
|
|
646
|
+
|
|
647
|
+
With **no** navigability config (`routeGlobs` / `navRegistry`) and **no**
|
|
648
|
+
`journeySuite` present in `.agentrc.json`, this entire phase degrades to a
|
|
649
|
+
**silent no-op**: 6.5a and 6.5b skip (nothing to enumerate or run) and 6.5c's
|
|
650
|
+
surface-adding signal cannot fire without route globs, so the `@pending`
|
|
651
|
+
tightening never engages. The gate adds **zero** behaviour to an unconfigured
|
|
652
|
+
consumer — it neither blocks finalize nor changes the existing change-set-scoped
|
|
653
|
+
gates. The override flag `--skip-integration-gate` is the explicit operator
|
|
654
|
+
escape hatch (consistent with `--skip-epic-audit`) for a configured consumer who
|
|
655
|
+
wants to bypass the gate for a specific run.
|
|
656
|
+
|
|
657
|
+
### Fail safe and loud
|
|
658
|
+
|
|
659
|
+
Per the Tech Spec's security note, this gate sits on the **critical path** to
|
|
660
|
+
finalize. It MUST fail **safe and loud**: a hard failure **blocks** finalize and
|
|
661
|
+
**names the cause** (the orphaned route, dead href, broken journey, or
|
|
662
|
+
`@pending`-only AC), never silently passes. A genuinely unconfigured consumer is
|
|
663
|
+
the **only** silent path, and that path **passes** (it is a no-op, not a block).
|
|
664
|
+
On a hard failure, post a friction structured comment naming the surface, flip
|
|
665
|
+
the Epic to `agent::blocked`, and park for the operator — do **not** open the PR.
|
|
666
|
+
|
|
667
|
+
---
|
|
668
|
+
|
|
559
669
|
## Phase 7 — Finalize (open PR to main)
|
|
560
670
|
|
|
561
671
|
### 7.0 — Sync Epic branch from `main` (Story #2580)
|
|
@@ -859,6 +969,11 @@ the `epic-run-progress` structured comment.
|
|
|
859
969
|
outcome.
|
|
860
970
|
- **Always** auto-invoke the epic-audit, code-review, and retro helpers
|
|
861
971
|
(Phases 4–6) when their artefacts aren't already present.
|
|
972
|
+
- **Always** run the Phase 6.5 integration gate after the wave loop
|
|
973
|
+
reports `epic-complete` and before Phase 7 finalize (unless
|
|
974
|
+
`--skip-integration-gate`); **never** open the PR while the gate
|
|
975
|
+
reports a hard failure (orphaned surface, dead nav href, broken
|
|
976
|
+
journey, or a surface-adding Epic with only `@pending` AC coverage).
|
|
862
977
|
- **Always** drive Phase 8 to green CI before returning control — the
|
|
863
978
|
host LLM owns the loop until the PR is mergeable or the Epic is
|
|
864
979
|
parked at `agent::blocked`.
|
|
@@ -743,7 +743,7 @@ node .agents/scripts/epic-plan-spec-validate.js \
|
|
|
743
743
|
`depends_on`** — and it MUST NOT add scope or invent tickets; it
|
|
744
744
|
consolidates fragmented slices by merging them into a cohesive Story,
|
|
745
745
|
never by splitting one. It runs **before** the deterministic
|
|
746
|
-
validator (step
|
|
746
|
+
validator (step 7), so the validator re-checks its output and the critic
|
|
747
747
|
cannot emit an invalid plan.
|
|
748
748
|
|
|
749
749
|
**Show the operator the consolidation report (the before/after diff +
|
|
@@ -752,7 +752,62 @@ node .agents/scripts/epic-plan-spec-validate.js \
|
|
|
752
752
|
rejection, persist the draft instead. This is a sub-step of Phase 8 — it
|
|
753
753
|
does **not** renumber the top-level lifecycle phases (9–12).
|
|
754
754
|
|
|
755
|
-
4. **
|
|
755
|
+
4. **Phase 8.4 — Reachability Completeness Critic (HITL diff gate, F6)**:
|
|
756
|
+
After consolidation (8.3) and **before** the persist call below, run the
|
|
757
|
+
completeness critic. This is a **distinct pass** from
|
|
758
|
+
[`epic-plan-consolidate`](../../skills/core/epic-plan-consolidate/SKILL.md)
|
|
759
|
+
and **must NOT relax** that skill's scope-preserving conservation invariant:
|
|
760
|
+
the consolidate critic is merge-and-rewire-only (it MUST NOT add scope or
|
|
761
|
+
invent tickets), whereas this critic is permitted exactly one **additive**
|
|
762
|
+
operation — appending a **single reachability Story** when, and only when, an
|
|
763
|
+
Epic introduces a user-facing surface (a new route/page) that **no Story
|
|
764
|
+
gives a navigation owner** (no nav-registry entry, no menu/link door from an
|
|
765
|
+
authenticated home). The two passes are deliberately separate so the
|
|
766
|
+
conservation invariant stays intact: consolidation never adds, and this
|
|
767
|
+
critic's only addition is the reachability Story.
|
|
768
|
+
|
|
769
|
+
- **Detection.** Scan the consolidated `tickets.json` for Stories that add a
|
|
770
|
+
route-shaped surface (paths under the consumer-configured
|
|
771
|
+
`planning.navigation.routeGlobs`, mirroring the
|
|
772
|
+
`epic-plan-healthcheck.js --paranoid` reachability check, F7). For each
|
|
773
|
+
such surface, confirm some Story's `acceptance[]` / `changes[]` references
|
|
774
|
+
the consumer's nav-registry SSOT (`planning.navigation.navRegistry`). A
|
|
775
|
+
surface with **no** nav owner is an orphan.
|
|
776
|
+
- **Action.** When at least one orphan surface exists, append **one**
|
|
777
|
+
reachability Story whose `acceptance[]` requires the orphaned surface to be
|
|
778
|
+
reachable by navigation from the relevant persona's authenticated home
|
|
779
|
+
(never by deep-link), and wire its `depends_on` to the surface-adding
|
|
780
|
+
Story. Append **at most one** reachability Story per decompose run — batch
|
|
781
|
+
every orphan under it — so the critic never fans out a parallel backlog.
|
|
782
|
+
- **No-op when unconfigured.** With no `planning.navigation` config present,
|
|
783
|
+
the critic degrades to a silent no-op (it cannot identify route-shaped
|
|
784
|
+
surfaces or a nav registry), exactly like the F7 healthcheck flag.
|
|
785
|
+
- **HITL.** The added Story is surfaced in the **same Phase 8 HITL diff** as
|
|
786
|
+
consolidation, **before** any GitHub write — never auto-persisted. On
|
|
787
|
+
operator rejection, drop the added Story and persist without it. The
|
|
788
|
+
deterministic validator (step 7) re-checks the critic's output, so an
|
|
789
|
+
invalid addition cannot reach GitHub.
|
|
790
|
+
|
|
791
|
+
5. **Phase 8.5 — Planning Pre-Mortem Critic (code-reading, F9)**: After the
|
|
792
|
+
reachability critic (8.4) and **before** the persist call below, activate the
|
|
793
|
+
[`epic-plan-premortem`](../../skills/core/epic-plan-premortem/SKILL.md)
|
|
794
|
+
skill with `[Epic_ID]` as input. This is a **fresh-context critic** sibling
|
|
795
|
+
to `epic-plan-consolidate`: it reads the drafted `tickets.json`, the PRD /
|
|
796
|
+
Tech Spec, **and the actual cited code surfaces** (the files each Story's
|
|
797
|
+
`changes[]` / `references[]` name), then emits predicted-rework findings —
|
|
798
|
+
unverifiable acceptance criteria, over- or under-specified Stories, and
|
|
799
|
+
semantically-wrong assumptions the structural file-assumption gate (step 7)
|
|
800
|
+
cannot catch — to `temp/epic-[Epic_ID]/premortem-report.md`.
|
|
801
|
+
|
|
802
|
+
Unlike the consolidate critic it is **not** scope-preserving-only: it may
|
|
803
|
+
recommend splitting an under-specified Story or tightening an AC. But it
|
|
804
|
+
**never writes to GitHub** and never persists `tickets.json` — it only emits
|
|
805
|
+
the report. Its findings are shown in the **same Phase 8 HITL diff**, and on
|
|
806
|
+
operator approval the author re-runs (Step 2) on the findings **before** the
|
|
807
|
+
persist call. The critic runs **before** the deterministic validator (step
|
|
808
|
+
7), so the persist below is the single GitHub write for the whole phase.
|
|
809
|
+
|
|
810
|
+
6. **Persist to GitHub**: Run the decompose CLI's persist half. It
|
|
756
811
|
validates the ticket array (`validateAndNormalizeTickets`), creates
|
|
757
812
|
the Story issues, flips the Epic to `agent::ready`, and
|
|
758
813
|
writes the `epic-plan-state` checkpoint.
|
|
@@ -767,7 +822,7 @@ node .agents/scripts/epic-plan-spec-validate.js \
|
|
|
767
822
|
--tickets temp/epic-[Epic_ID]/tickets.json --force
|
|
768
823
|
```
|
|
769
824
|
|
|
770
|
-
|
|
825
|
+
7. **Cross-Validation**:
|
|
771
826
|
- Hierarchy completeness, dependency-DAG acyclicity, and `risk::high`
|
|
772
827
|
labelling are deterministic invariants enforced by
|
|
773
828
|
`validateAndNormalizeTickets` in
|
|
@@ -808,11 +863,11 @@ node .agents/scripts/epic-plan-spec-validate.js \
|
|
|
808
863
|
--force
|
|
809
864
|
```
|
|
810
865
|
|
|
811
|
-
|
|
866
|
+
8. **Audit**:
|
|
812
867
|
- Check the Epic's comment thread to ensure the backlog summary was posted.
|
|
813
868
|
- Verify that at least one `type::story` issue was created.
|
|
814
869
|
|
|
815
|
-
|
|
870
|
+
9. **Cleanup**: The wrapper script (`epic-plan-decompose.js`) deletes the
|
|
816
871
|
Phase 8 temp files automatically on success — no operator action required.
|
|
817
872
|
The cleanup contract lives in
|
|
818
873
|
[`lib/plan-phase-cleanup.js`](../../scripts/lib/plan-phase-cleanup.js).
|