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.
Files changed (28) hide show
  1. package/.agents/docs/configuration.md +7 -0
  2. package/.agents/docs/workflows.md +3 -2
  3. package/.agents/personas/engineer.md +27 -0
  4. package/.agents/schemas/agentrc.schema.json +38 -0
  5. package/.agents/schemas/audit-rules.json +12 -0
  6. package/.agents/scripts/epic-audit-prepare.js +53 -3
  7. package/.agents/scripts/epic-plan-healthcheck.js +191 -2
  8. package/.agents/scripts/lib/audit-suite/index.js +5 -0
  9. package/.agents/scripts/lib/audit-suite/selector.js +73 -0
  10. package/.agents/scripts/lib/config-settings-schema-quality.js +13 -0
  11. package/.agents/scripts/lib/config-settings-schema.js +11 -0
  12. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +82 -0
  13. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +43 -18
  14. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-authoring-grounding.js +147 -0
  15. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +63 -0
  16. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +11 -0
  17. package/.agents/scripts/lib/orchestration/retro-runner.js +19 -2
  18. package/.agents/scripts/lib/orchestration/spec-freshness.js +2 -2
  19. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +17 -0
  20. package/.agents/skills/core/epic-plan-premortem/SKILL.md +138 -0
  21. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +15 -0
  22. package/.agents/skills/skills.index.json +12 -2
  23. package/.agents/workflows/audit-navigability.md +129 -0
  24. package/.agents/workflows/helpers/deliver-epic.md +116 -1
  25. package/.agents/workflows/helpers/plan-epic.md +60 -5
  26. package/.agents/workflows/qa-assist.md +214 -141
  27. package/docs/CHANGELOG.md +14 -0
  28. 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-11T19:47:29.752Z",
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": 12,
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 4), so the validator re-checks its output and the critic
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. **Persist to GitHub**: Run the decompose CLI's persist half. It
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
- 5. **Cross-Validation**:
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
- 6. **Audit**:
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
- 7. **Cleanup**: The wrapper script (`epic-plan-decompose.js`) deletes the
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).