@metasession.co/devaudit-cli 0.1.69 → 0.1.70

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/dist/index.js +11 -4
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -2
  4. package/scripts/upload-evidence.sh +10 -1
  5. package/sdlc/CLAUDE.md +25 -2
  6. package/sdlc/SKILLS.md +1 -1
  7. package/sdlc/ai-rules/INSTRUCTIONS-SDLC.md +21 -5
  8. package/sdlc/files/_common/3-compile-evidence.md +63 -2
  9. package/sdlc/files/_common/4-submit-for-review.md +2 -1
  10. package/sdlc/files/_common/5-deploy-main.md +1 -0
  11. package/sdlc/files/_common/Test_Plan_TEMPLATE.md +1 -1
  12. package/sdlc/files/_common/Test_Strategy.md +1 -1
  13. package/sdlc/files/_common/governance/nil-incident-report.md.template +38 -0
  14. package/sdlc/files/_common/scripts/validate-compliance-artifacts.sh +13 -0
  15. package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +12 -2
  16. package/sdlc/files/_common/skills/requirements-aligner/SKILL.md +6 -1
  17. package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +137 -25
  18. package/sdlc/files/_common/skills/sdlc-implementer/references/call-graph.md +1 -1
  19. package/sdlc/files/_common/skills/sdlc-implementer/references/change-request-loop.md +27 -2
  20. package/sdlc/files/ci/ci.yml.template +83 -13
  21. package/sdlc/files/ci/compliance-evidence.yml.template +353 -53
  22. package/sdlc/files/ci/feature-e2e.yml.template +3 -2
  23. package/sdlc/files/ci/incident-export.yml.template +306 -8
  24. package/sdlc/files/ci/label-retention.yml.template +144 -0
  25. package/sdlc/files/ci/post-deploy-prod.yml.template +80 -3
  26. package/sdlc/files/ci/python/ci.yml.template +60 -16
  27. package/sdlc/files/sdlc-config.example.json +1 -1
  28. package/sdlc/.claude/settings.local.json +0 -11
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sdlc-implementer
3
- description: Take a GitHub issue end-to-end through the Metasession SDLC. Opens with a Workflow Triage step (Phase 0) that classifies the change and routes it — tracked work continues into the full cycle; housekeeping/trivial/doc-only is driven to merge down a lightweight path (same step-by-step guidance, no tracked ceremony). Use when the user wants to implement a single GitHub issue as a complete SDLC cycle — Phase 1 (classify risk, write implementation plan, update RTM) through Phase 4 (open PR, request UAT review on the portal), then halt; and Phase 5 (merge, post-deploy smoke evidence, mark Released, or change-request loop) on resume. Trigger phrases — "implement issue #N under the SDLC", "run the SDLC for issue #N", "automate REQ-XXX from issue to release", "do the SDLC stages for [issue]". Resume phrase — "resume REQ-XXX". MUST delegate end-to-end and visual-regression test work to the e2e-test-engineer skill in Phase 2; never authors e2e tests directly. Do NOT use for partial work — for stage-1 planning only, run the manual walkthrough; for test work alone, invoke e2e-test-engineer directly.
3
+ description: Take a GitHub issue end-to-end through the Metasession SDLC. Opens with a Workflow Triage step (Phase 0) that classifies the change and routes it — tracked work continues into the full cycle; housekeeping/trivial/doc-only is driven to merge down a lightweight path (same step-by-step guidance, no tracked ceremony). Use when the user wants to implement a single GitHub issue as a complete SDLC cycle — Phase 1 (classify risk, write implementation plan, update RTM) through Phase 4 (open PR, request UAT review on the portal), then halt; and Phase 5 (merge, post-deploy smoke evidence, mark Released, or change-request loop) on resume. Trigger phrases — "implement issue #N", "fix issue #N", "do issue #N", "implement #N", "implement issue #N under the SDLC", "run the SDLC for issue #N", "automate REQ-XXX from issue to release", "do the SDLC stages for [issue]". Resume phrase — "resume REQ-XXX". MUST delegate end-to-end and visual-regression test work to the e2e-test-engineer skill in Phase 2; never authors e2e tests directly. Do NOT use for partial work — for stage-1 planning only, run the manual walkthrough; for test work alone, invoke e2e-test-engineer directly.
4
4
  tags: [sdlc, orchestration, compliance, automation]
5
5
  ---
6
6
 
@@ -103,6 +103,56 @@ When the user asks for a change that goes beyond the current REQ's acceptance cr
103
103
 
104
104
  This gate is distinct from Phase 2 step 4's "any deviation from the plan must be noted" rule: that covers implementation *approach* deviations (how to build something the plan says to build), not *scope* expansions (building something the plan doesn't say to build at all).
105
105
 
106
+ ## Requirements gap flow (devaudit-installer#212)
107
+
108
+ A **requirements gap** is "the ACs themselves were wrong, incomplete, or missing" — discovered by the skill or sub-skills mid-cycle, not by a user request. This is distinct from:
109
+
110
+ - **Scope expansion** (user asks for something beyond the ACs → scope-expansion halt gate above)
111
+ - **Defect** (implementation doesn't match the ACs → incident filing)
112
+
113
+ ### When it fires
114
+
115
+ The requirements gap flow is triggered by:
116
+
117
+ - **Phase 2 step 4** — plan deviation classified as "requirements deviation" (an AC is wrong/incomplete/missing, not just the implementation approach)
118
+ - **Phase 3 step 1** — `requirements-aligner` returns `GAPS_FOUND` (ACs that don't trace to SRS items, or unresolved drift)
119
+ - **Phase 2** — `e2e-test-engineer` classifies an AC as "impossible to test" or "missing AC" (returns a requirements gap report instead of filing a defect)
120
+ - **Phase 3 step 4** — test failure classified as "requirements gap" (test is correct, implementation is correct, but the AC they both derive from is wrong)
121
+ - **Phase 5** — change-request loop classifies feedback as "requirements gap" (reviewer identified behaviour that should have been an AC but wasn't)
122
+
123
+ ### The flow
124
+
125
+ When any skill or sub-skill discovers a requirements gap, it surfaces a **requirements gap report**:
126
+
127
+ - Which AC is affected (or which missing AC should exist)
128
+ - What's wrong (impossible to satisfy / incomplete / incorrect / missing)
129
+ - What the corrected or new AC should be
130
+ - Impact on existing evidence (what needs re-compilation)
131
+
132
+ The `sdlc-implementer` presents the operator with three options:
133
+
134
+ **(a) Accept the gap** — proceed as-is. Mark the AC with `@requirements-gap-accepted: <reason>` in the plan. Document in a `## Requirements gap accepted` section: the gap, the reason for acceptance, and the operator's name + date. Appropriate for LOW-risk or when the gap is cosmetic. Continue from the current phase.
135
+
136
+ **(b) Amend the ACs** — update the plan's AC table, re-invoke `requirements-aligner` to propose new/updated SRS stubs, operator edits SRS stubs into canonical prose, update the RTM, invalidate affected evidence, re-walk affected phases. This carries the same evidence-invalidation warning as the scope-expansion halt gate option (b): *"Amending ACs after evidence is compiled (Phase 3+) invalidates the existing test-execution-summary, screenshots, and UAT verification. All affected Stage 3 evidence must be re-compiled."*
137
+
138
+ Amendment steps:
139
+ 1. Update `compliance/plans/REQ-XXX/implementation-plan.md` AC table
140
+ 2. Re-invoke `requirements-aligner` → propose new/updated SRS stubs
141
+ 3. Operator edits SRS stubs into canonical Given/When/Then prose in `docs/SRS.md`
142
+ 4. Update `compliance/RTM.md`
143
+ 5. Commit: `compliance: [REQ-XXX] amend ACs — requirements gap <description>`
144
+ 6. If Phase 2+: re-run affected tests (delegating to `e2e-test-engineer` if e2e)
145
+ 7. If Phase 3+: re-compile affected evidence
146
+ 8. Continue from current phase
147
+
148
+ **(c) File a follow-up REQ** — ship the current REQ as-is, file a separate issue for the requirements gap. Appropriate when the gap is a genuine new behaviour that deserves its own cycle. Continue with the current REQ unchanged.
149
+
150
+ ### Distinction from scope-expansion halt gate
151
+
152
+ - Triggered by **skill/sub-skill discovery**, not by a user request
153
+ - Includes "accept the gap" option (scope-expansion gate doesn't — scope expansion is always halt-or-amend)
154
+ - Classification is "the requirements were wrong" not "the user wants more"
155
+
106
156
  ## SDLC navigability — LAST/NEXT status sticky (devaudit#131)
107
157
 
108
158
  Long-running SDLC issues accumulate dozens of comments across multiple Claude Code sessions. The operator returning to the thread should be able to answer two questions in under five seconds:
@@ -173,6 +223,8 @@ Skip it for trivial turns (acknowledging a "merged" / one-line confirmations / c
173
223
 
174
224
  Do **not** update on every internal step within a phase — that just spams the sticky. The transition + handoff cadence is the right frequency.
175
225
 
226
+ **Sticky update failure (devaudit-installer#211 Gap 14).** Check the exit code of `update-sdlc-status.sh`. If it fails (gh auth expired, GitHub API rate-limited, network error), log a warning in the chat: "⚠️ SDLC status sticky update failed (gh CLI error). The LAST/NEXT pointer on issue #N may be stale. Operator — update manually if needed." Continue the workflow — the sticky is a navigation aid, not a control gate.
227
+
176
228
  ## The workflow
177
229
 
178
230
  A triage step (Phase 0) routes the issue, then up to five phases for tracked work. Phase 0 plus Phases 1–4 run in one Claude Code session; Phase 5 is invoked separately by the user after UAT. The off-ramps from Phase 0 (housekeeping / trivial / doc-only) don't enter Phase 1 — they run the **Lightweight path** (below), which the skill drives to merge.
@@ -184,7 +236,7 @@ INTEGRATION_BRANCH=$(jq -r '.integration_branch // "develop"' sdlc-config.json)
184
236
  RELEASE_BRANCH=$(jq -r '.release_branch // "main"' sdlc-config.json) # the protected production branch
185
237
  ```
186
238
 
187
- For a **develop-first** repo these are `develop` and `main`: implementation lands on `$INTEGRATION_BRANCH`, and the UAT-approved release PR is `$INTEGRATION_BRANCH → $RELEASE_BRANCH`. A **trunk-only** repo sets both to `main`, collapsing the two hops into a single `feature → main` PR. Where the two branches differ, the release PR's head is `$INTEGRATION_BRANCH`; where they're equal, it's the feature branch.
239
+ The framework uses a **develop-first** branching model: implementation lands on `$INTEGRATION_BRANCH` (default `develop`), and the UAT-approved release PR is `$INTEGRATION_BRANCH → $RELEASE_BRANCH` (default `main`). The release PR's head is always `$INTEGRATION_BRANCH`.
188
240
 
189
241
  ### Phase 0 — Workflow triage (classify → announce → confirm → route)
190
242
 
@@ -265,7 +317,9 @@ Reached from Phase 0 for non-tracked change-types. The skill drives this end-to-
265
317
  **CI trigger shape — read once before step 7.** DevAudit-Installer-generated `ci.yml.template` runs `Quality Gates` on PRs to the integration branch and on pushes to the integration branch. Older consumers may still have post-merge-only CI (`push: branches: [<integration>]`, no `pull_request:` trigger) until they re-run `devaudit update`. The skill must adapt step 7's wording to whichever shape the project uses; never poll a PR for checks that won't arrive on that consumer yet.
266
318
 
267
319
  1. **Branch off `$INTEGRATION_BRANCH`** with a housekeeping prefix — `chore/…`, `docs/…`, `ci/…`, `build/…`, `test/…`, or `compliance/…` for a doc-only change against an existing REQ.
268
- 2. **Make the change**, single-purpose. If it turns out to touch runtime behaviour in `app/` / `lib/`, stop and reclassify as tracked — the commit-type rule is the backstop.
320
+ 2. **Make the change**, single-purpose. If it turns out to touch runtime behaviour in `app/` / `lib/`, stop and reclassify as tracked — the commit-type rule is the backstop. **Transition:**
321
+ - **PR not yet merged:** Close the PR (`gh pr close <N>`), delete the branch, restart from Phase 0 with the tracked classification. The housekeeping commit is abandoned (branch deleted).
322
+ - **PR already merged:** Halt — "This change was classified as housekeeping and merged, but implementation touched runtime behaviour (`app/` / `lib/`). The commit <SHA> is on `$INTEGRATION_BRANCH` without REQ tracking, evidence, or portal release. Operator action — decide: (a) revert the merge and re-run as tracked (`git revert <SHA>` + new `sdlc-implementer` invocation), or (b) accept the gap and file a retroactive REQ-XXX to backfill evidence. Option (b) leaves a compliance gap — the change shipped without plan, test scope, or UAT review."
269
323
  3. **Run all gates locally** (`npm run lint`, `npx tsc --noEmit`, the test suite, `semgrep`, `npm audit` — or the stack-adapter equivalents). Trivial ≠ unverified; never `--no-verify`.
270
324
  4. **Commit** with a housekeeping type and **no** `REQ-XXX` — `docs:` / `chore:` / `ci:` / `build:` / `test:` / `revert:` are exempt from the `[REQ-XXX]` rule; a `compliance:` doc-only change references the existing REQ. `Co-Authored-By: Claude` if AI-assisted.
271
325
  5. **Push and open the PR** into `$INTEGRATION_BRANCH` (`gh pr create --base "$INTEGRATION_BRANCH" --head <branch>`). CI runs the same quality gates; `compliance-validation.yml` finds no `REQ-XXX` and skips artifact validation.
@@ -285,7 +339,7 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
285
339
  0. **Initialise SDLC status sticky** on the issue: `bash scripts/update-sdlc-status.sh "$ISSUE_NUM" "Phase 0 complete — classified as tracked SDLC work" "Phase 1 — sdlc-implementer authoring implementation plan"`. From now until the issue closes, the sticky is the always-current pointer to "what's next" — the operator scans it on every return to the issue.
286
340
  1. **Confirm the issue scope.** Re-read the `gh issue view <N>` output from Phase 0 — title, body, all comments — with implementation in mind.
287
341
  2. **Classify risk** per `Test_Policy.md` §Risk-Based Testing. Emit a one-paragraph rationale citing the signals you used (auth surface, financial calc, data egress, RBAC, AI decisioning, etc.).
288
- 3. **Assign REQ-XXX.** Inspect `compliance/RTM.md` for existing entries; take the next free number. If the issue references an existing REQ, use that instead.
342
+ 3. **Assign REQ-XXX.** Inspect `compliance/RTM.md` for existing entries; take the next free number. If the issue references an existing REQ, use that instead. **Create the portal release immediately** (DevAudit-Installer#200 Fix 3) by running `bash scripts/upload-evidence.sh {{PROJECT_SLUG}} REQ-XXX compliance_document compliance/evidence/REQ-XXX/test-scope.md --category planning --release REQ-XXX --create-release-if-missing --environment uat --sdlc-stage 1` (or a placeholder file if test-scope.md isn't written yet). This ensures: (a) incident reports during Stage 2 implementation attach to the correct `REQ-XXX` release instead of a bare-date housekeeping release, (b) planning evidence can upload as Stage 1 evidence, (c) CI `register-release` on push to `develop` becomes a safety net (finds the existing release) rather than the primary creation mechanism.
289
343
  4. **Detect over-scoping.** If the issue spans clearly distinct deliverables (e.g. "build SAML SSO + reorganise the admin dashboard + migrate from Postgres 14 to 16"), halt with a clear message asking the user to split the issue into separate ones. Do not proceed past Phase 1.
290
344
  5. **Write the implementation plan.** Create `compliance/plans/REQ-XXX/implementation-plan.md` from `sdlc/files/_common/Implementation_Plan_TEMPLATE.md` (synced into the consumer's `SDLC/` directory at install). The template's shape is load-bearing — it carries the **Framework attribution** section that closes four framework clauses on upload:
291
345
 
@@ -301,8 +355,15 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
301
355
  **Don't delete sections** — mark with `N/A — <reason>` if a clause genuinely doesn't apply (e.g. UI-only change with no personal-data scope). Empty stubs commit-then-upload as placeholder evidence and break the audit trail.
302
356
 
303
357
  6. **Invoke `requirements-aligner` to populate the SRS-ID column on the AC table.** The plan's "Acceptance criteria" table carries an SRS-ID column per AC; `requirements-aligner` fuzzy-matches each AC against `docs/SRS.md` and proposes new `REQ-AREA-NNN` stubs, flags stale items, or annotates `@srs-deferred`. Don't author the SRS-ID column inline — call via the standard Claude Code Skill mechanism (`Skill(name: "requirements-aligner", …)`). Block plan APPROVAL until every AC has a resolved SRS-ID per the skill's Phase 1 contract (configurable via `sdlc-config.json:requirements_aligner.block_on_stage_1`; ramp-up mode default-on for legacy projects).
358
+
359
+ **SRS stub editing (devaudit-installer#212 Gap 5).** After `requirements-aligner` returns, the plan's SRS items proposed/touched section may contain N new stubs and M stale items. The operator must edit `docs/SRS.md` to flesh out the stubs into canonical Given/When/Then prose and update stale items. Do not proceed to Phase 2 until the SRS is updated. The skill enforces this by checking that no stub placeholders remain in `docs/SRS.md` for the proposed SRS-IDs before auto-continuing. A stub placeholder is any SRS entry containing `<TODO>` or the `@srs-stub` marker.
304
360
  7. **Invoke `adr-author` to decide ADR-worthiness + draft the ADR if needed.** The plan's "Architecture decisions" section is no longer authored inline as bullets — `adr-author` applies its decision tree (new third-party dependency / new database/cache/queue / new external service / pattern change spanning >3 files / HIGH-CRITICAL risk class / file-path signals from `sdlc-config.json:adr_author.file_paths_signal_architecture`), allocates the next `ADR-NNN`, drafts a Context/Decision/Consequences/Alternatives/Status stub at `docs/ADR/ADR-NNN-<slug>.md`, and injects either _"Produced ADR-NNN: <title>"_ or _"No ADR needed — <rationale>"_ into the plan's section. Call via the standard Claude Code Skill mechanism (`Skill(name: "adr-author", …)`). Configurable via `sdlc-config.json:adr_author.block_on_stage_1`; advisory by default in v1.
305
361
  8. **Invoke `risk-register-keeper` for MEDIUM/HIGH risk classifications.** The plan's "Threat model" / Risks section is no longer authored as orphan bullets — when risk class is MEDIUM or HIGH (LOW skipped by default per `sdlc-config.json:risk_register_keeper.stage_1_min_risk_class`), `risk-register-keeper` reads the plan + diff, identifies discrete risks the change introduces, allocates `RISK-NNN` per project, drafts canonical rows in `compliance/risk-register.md`, and injects the RISK-NNN reference list into the plan's "Risk register entries" sub-section. The skill also enforces the `solo_with_gap` control-gap entry exists for projects in that approval mode. Call via the standard Claude Code Skill mechanism (`Skill(name: "risk-register-keeper", …)`). Configurable via `sdlc-config.json:risk_register_keeper.block_on_stage_1`; advisory by default in v1.
362
+
363
+ **Sub-skill failure behavior (devaudit-installer#211 Gap 15).** If a sub-skill fails during steps 6–8, behavior is defined per-skill:
364
+ - **`requirements-aligner` fails** (e.g. no `docs/SRS.md`): If `block_on_stage_1` is true, halt — "requirements-aligner could not find SRS.md. Operator action — create `docs/SRS.md` or disable `requirements_aligner.block_on_stage_1` in `sdlc-config.json`." If false, warn and continue with `@srs-deferred` on all ACs.
365
+ - **`adr-author` fails**: Warn and continue — ADR is advisory by default. Mark the plan's "Architecture decisions" section as "ADR assessment skipped — <error>".
366
+ - **`risk-register-keeper` fails**: If `block_on_stage_1` is true, halt. If false, warn and continue — mark the plan's "Risk register entries" section as "Risk assessment skipped — <error>".
306
367
  9. **Update `compliance/RTM.md`** with the new entry: REQ-XXX, title, risk class, linked issue, linked test cases (placeholder).
307
368
  10. **Post plan summary as an issue comment.** Format: TL;DR; Risk class + signals; Acceptance criteria (with SRS-IDs); Architectural decisions (ADR-NNN reference or no-ADR rationale); Risk register entries (RISK-NNN list); Technical approach (one paragraph); Dependencies; Test scope.
308
369
  11. **Checkpoint** — pause for human approval **iff** risk class is HIGH or CRITICAL. LOW and MEDIUM pass through to Phase 2 automatically. The checkpoint can be forced on for all classes via the `--require-plan-approval` flag (or `DEVAUDIT_REQUIRE_PLAN_APPROVAL=1` env var) for orgs that want it always-on.
@@ -310,7 +371,7 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
310
371
 
311
372
  ### Phase 2 — Implement and test (SDLC stage 2)
312
373
 
313
- 1. **Branch off `$INTEGRATION_BRANCH`.** `git checkout "$INTEGRATION_BRANCH" && git pull && git checkout -b feat/REQ-XXX-<slug>`. The slug is a kebab-case fragment of the issue title (max 6 words).
374
+ 1. **Branch off `$INTEGRATION_BRANCH`.** `git checkout "$INTEGRATION_BRANCH" && git pull && git checkout -b feat/REQ-XXX-<slug>`. The slug is a kebab-case fragment of the issue title (max 6 words). **Advisory — concurrent REQ check:** check for existing open PRs to `$INTEGRATION_BRANCH` that touch overlapping files (`gh pr list --base "$INTEGRATION_BRANCH" --state open --json files`). If overlap is detected: warn the operator — "PR #M (REQ-YYY) is open and touches <overlapping files>. Merging this REQ first may cause conflicts for REQ-YYY. Proceed with caution." This is advisory, not blocking.
314
375
  2. **Write failing tests first** per [`Test_Architecture.md`](../../Test_Architecture.md). Depth scales with risk class:
315
376
  - LOW — unit tests on the changed function(s); no e2e required unless the change touches a user-facing flow.
316
377
  - MEDIUM — unit + integration; e2e for any UI-facing change.
@@ -323,7 +384,11 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
323
384
  c. **Do not author or edit any `e2e/**/*.spec.ts` file in this skill's own tool calls.** The e2e-test-engineer skill owns spec authoring end-to-end — including the "this AC needs no e2e" decision. If you feel the urge to write a spec inline, that's the inertia trap — STOP and re-invoke the skill.
324
385
 
325
386
  When in doubt about whether work qualifies: visual-regression tests, screenshot diffs, browser-driven flows, any file ending in `.spec.ts` under `e2e/`, and any `playwright.config.ts`/`evidence/`/`baselines/` directory all qualify. Unit/integration tests under `tests/unit/`, `tests/integration/`, or stack-equivalent paths stay with this orchestrator.
326
- 4. **Implement against the plan.** Reference `compliance/plans/REQ-XXX/implementation-plan.md` as you go. Any deviation from the plan must be noted in the plan itself under a `## Plan deviation` section — never silently diverge.
387
+ 4. **Implement against the plan.** Reference `compliance/plans/REQ-XXX/implementation-plan.md` as you go. Any deviation from the plan must be classified:
388
+ - **Implementation deviation** (approach changed, ACs still correct) — note in a `## Plan deviation` section of the plan. Continue.
389
+ - **Requirements deviation** (an AC is wrong, incomplete, or missing) — trigger the [requirements gap flow](#requirements-gap-flow-devaudit-installer212). Do NOT just note it as a plan deviation. The AC table must be updated, `requirements-aligner` must be re-invoked to re-check SRS alignment, and the RTM must be updated.
390
+
391
+ The classification question: "Did the implementation deviate from the plan's approach (how to build it), or did the plan's requirements deviate from reality (what to build)?" If the former, note and continue. If the latter, trigger the requirements gap flow.
327
392
  5. **Run gates locally, cheap-first.** The gates are not equivalent-cost — `npm run lint` is seconds, `npx playwright test` is 30–60 minutes. Iterate on the fast gates; spend the e2e cost once.
328
393
 
329
394
  **Fast gates** (run on every change, ideally pre-commit):
@@ -336,11 +401,9 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
336
401
  **E2E gate** — run _once_, after the fast gates are clean:
337
402
  - `npx playwright test` (delegated to `e2e-test-engineer`, which has its own focused-iteration discipline for within-e2e fix-and-verify loops)
338
403
 
339
- 6. **On gate failure**, iterate up to N=3 attempts. Each iteration: read the failure output, propose a fix, apply, re-run. On exhausted attempts, halt with the full failure output and surface to the human never use `--no-verify`, `eslint-disable`, `@ts-expect-error`, `xfail`, or any other bypass.
404
+ 6. **On gate failure**, iterate up to N=3 attempts. Each iteration: read the failure output, propose a fix, apply, re-run. On exhausted attempts, halt with the full failure output and explicit resume instructions: "Gate <name> failed after N=3 attempts. Last failure: <output>. Operator action — fix the failure, commit to the feature branch, push, then ping `resume REQ-XXX`. The skill will re-run the gate from where it left off." Update the sticky with the same. Never use `--no-verify`, `eslint-disable`, `@ts-expect-error`, `xfail`, or any other bypass.
340
405
  7. **Commit** using Conventional Commits with `Ref: REQ-XXX` trailer and `Co-Authored-By: Claude` trailer. One commit per logical step; never amend a commit that's already been pushed.
341
- 8. **Land the work on `$INTEGRATION_BRANCH`.** Push the feature branch, then:
342
- - **If `$INTEGRATION_BRANCH` ≠ `$RELEASE_BRANCH`** (develop-first): open a PR `feat/REQ-XXX-<slug> → $INTEGRATION_BRANCH` and merge it once CI is green. This is the **integration hop** — there is no UAT four-eyes gate here (that's the release PR in Phase 4); for MEDIUM+ risk get a peer review on this PR per the project's norms. The push to `$INTEGRATION_BRANCH` is what triggers `ci.yml` to register the release and upload gate evidence.
343
- - **If `$INTEGRATION_BRANCH` = `$RELEASE_BRANCH`** (trunk-only): do **not** merge to the protected branch here — leave the work on the feature branch; it becomes the release PR's head in Phase 4.
406
+ 8. **Land the work on `$INTEGRATION_BRANCH`.** Push the feature branch, then open a PR `feat/REQ-XXX-<slug> → $INTEGRATION_BRANCH` and merge it once CI is green. This is the **integration hop** — there is no UAT four-eyes gate here (that's the release PR in Phase 4); for MEDIUM+ risk get a peer review on this PR per the project's norms. The push to `$INTEGRATION_BRANCH` is what triggers `ci.yml` to register the release and upload gate evidence. **Merge conflict resolution:** if the PR has merge conflicts (another feature branch merged first), pull the latest `$INTEGRATION_BRANCH` into the feature branch (`git merge "$INTEGRATION_BRANCH" --no-edit`), resolve conflicts (preferring the feature branch's changes for files this REQ touches), push, wait for CI. If conflicts are in files this REQ doesn't touch, halt — "Merge conflict in <files> from another feature. Operator action — review the conflict, these files are outside REQ-XXX's scope."
344
407
 
345
408
  9. **E2E delegation self-audit — mandatory before Phase 3 (devaudit#132).** Run `git diff "$INTEGRATION_BRANCH"...HEAD --name-only` and walk the file list. For **every** entry matching `e2e/**/*.spec.ts`, state out loud one of:
346
409
 
@@ -359,15 +422,43 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
359
422
 
360
423
  Then proceed to Phase 3 step 1 in the same turn. The integration-PR merge *feels* terminal — that is the inertia trap. The next required operator handoff is Phase 4 step 5 (release PR opened, awaiting portal UAT approval), not the develop merge. The only thing that interrupts this auto-continue is the scope-expansion halt gate (#171) firing on a new user request.
361
424
 
425
+ **CI failure on `$INTEGRATION_BRANCH` post-merge.** If CI on `$INTEGRATION_BRANCH` fails after the integration PR merges:
426
+ - Read the failure output, attempt fix-forward (commit fix to `$INTEGRATION_BRANCH` directly, push, wait for CI) up to N=3 attempts.
427
+ - If fix-forward succeeds: auto-continue to Phase 3.
428
+ - If fix-forward exhausts attempts: halt — "CI on `$INTEGRATION_BRANCH` failed after integration merge. N=3 fix-forward attempts exhausted. Last failure: <output>. Operator action — fix CI on `$INTEGRATION_BRANCH`, then ping `resume REQ-XXX`."
429
+ - If the failure is unrelated to this REQ's changes (e.g. another feature broke `$INTEGRATION_BRANCH`): document on the PR, attempt cancel-and-admin-merge per the existing external-gate rule (adapted for post-merge context), or halt and surface.
430
+
362
431
  ### Phase 3 — Compile evidence (SDLC stage 3)
363
432
 
364
- 1. **Invoke `requirements-aligner` to drop the per-REQ SRS-alignment artefact.** The skill's Phase 2 produces `compliance/evidence/REQ-XXX/srs-alignment.md` — the per-REQ trace from each AC to its SRS item, with an operator sign-off block. The artefact uploads with `evidence_type=srs_alignment` (visible in Documents tab + audit-pack export; v1 orphan-by-design per META-COMPLY framework-registry-auditor). Call via the standard Skill mechanism; don't inline the alignment logic.
433
+ 1. **Invoke `requirements-aligner` to drop the per-REQ SRS-alignment artefact.** The skill's Phase 2 produces `compliance/evidence/REQ-XXX/srs-alignment.md` — the per-REQ trace from each AC to its SRS item, with an operator sign-off block. The artefact uploads with `evidence_type=srs_alignment` (visible in Documents tab + audit-pack export; v1 orphan-by-design per META-COMPLY framework-registry-auditor). Call via the standard Skill mechanism; don't inline the alignment logic. **Gap status return (devaudit-installer#212 Gap 3):** `requirements-aligner` returns a gap status to `sdlc-implementer`:
434
+ - `CLEAN` — all ACs trace to SRS items, no drift. Continue to step 2.
435
+ - `GAPS_FOUND` — one or more ACs don't trace, or drift is unresolved. Return the gap details.
436
+
437
+ When `sdlc-implementer` receives `GAPS_FOUND`:
438
+ - If `block_on_stage_3: true` (configurable via `sdlc-config.json:requirements_aligner.block_on_stage_3`) — halt with the [requirements gap flow](#requirements-gap-flow-devaudit-installer212). The operator must resolve the gaps (edit SRS stubs, update ACs, or accept with `@requirements-gap-accepted`) before evidence upload proceeds. The block verifies artefact content (no unresolved gaps without `@srs-deferred`), not just artefact existence.
439
+ - If `block_on_stage_3: false` — warn: "SRS alignment gaps found: <details>. Evidence will upload with gaps recorded. Consider resolving before UAT review." Continue to step 2.
365
440
  2. **Invoke `adr-author` to drop the per-REQ architecture-decision artefact.** The skill's Phase 2 produces `compliance/evidence/REQ-XXX/architecture-decision.md` — either _"Produced ADR-NNN: <title>"_ with the file pointer, or _"No ADR needed — <rationale>"_. Operator sign-off block at the bottom. The artefact uploads with `evidence_type=architecture_decision`; clause attribution per the META-COMPLY framework-registry-auditor review. Call via the standard Skill mechanism.
366
441
  3. **Invoke `risk-register-keeper` to drop the per-REQ risk-assessment artefact.** The skill's Phase 3 produces `compliance/evidence/REQ-XXX/risk-assessment.md` — a summary table of RISK-NNN entries this REQ opened / mitigated / accepted, framework cross-references, and an operator sign-off block. The artefact uploads with `evidence_type=risk_assessment`; clause attribution per the META-COMPLY framework-registry-auditor review. Call via the standard Skill mechanism.
367
- 4. **Re-run the full test pack** with artefact capture:
442
+ 4. **Re-run the full test pack** with artefact capture. **Classify test failures (devaudit-installer#212 Gap 7):**
443
+ - **Implementation bug** — test is correct, implementation is wrong. Fix the implementation. (Current behaviour.)
444
+ - **Test bug** — test is wrong, implementation is correct. Fix the test. (Already handled by `e2e-test-engineer` Phase 6 triage.)
445
+ - **Requirements gap** — the test is correct, the implementation is correct, but the AC the test derives from is wrong or incomplete. The test reveals behaviour the AC didn't anticipate. Trigger the [requirements gap flow](#requirements-gap-flow-devaudit-installer212) instead of fixing code or test.
446
+
447
+ The classification question: "Is the test testing the right thing? Is the implementation doing the right thing? If both are 'right' but they disagree, the AC they both derive from is wrong."
448
+
449
+ **On test failure — delegate incident filing to `e2e-test-engineer` (devaudit-installer#210 §6a).** When the test pack re-run fails, do NOT file incidents inline. Instead, invoke `Skill(name: "e2e-test-engineer", args: "Phase 6 triage — test pack re-run failed for REQ-XXX. Triage failures and file application defects with the `incident` label + `### Framework attribution` section per the standard convention.")`. `e2e-test-engineer` returns with a summary of filed incidents. This ensures all incidents follow the correct filing convention (label + attribution) so `incident-export.yml` fires on close → `incident_report` evidence lands on the portal → `ISO29119.3.5.4` flips to COVERED.
450
+
368
451
  - `npm run test:e2e -- --reporter=html` (produces `playwright-report/`)
369
452
  - `npx vitest run --coverage` (produces `coverage/`)
370
- 5. **Organise artefacts** under `compliance/evidence/REQ-XXX/` with date-prefixed naming:
453
+
454
+ 5. **Generate nil incident report if no incidents were closed (devaudit-installer#210 §8a).** After the test pack re-run passes, check whether any `incident`-labelled issues were closed during this REQ's lifecycle (`gh issue list --label incident --state closed --search "REQ-XXX" --json number`). If none were closed and the test pack re-run passes:
455
+ - Create `compliance/governance/nil-incident-report-<version>.md` from the `nil-incident-report.md.template` template.
456
+ - Fill in the scope section (test cases executed/passed/failed, defects filed, incidents reported).
457
+ - Leave the sign-off section with REPLACE markers — the operator fills it in.
458
+ - The nil report uploads as `incident_report` evidence via `compliance-evidence.yml`'s `upload_incident_report` function, flipping `ISO29119.3.5.4` to COVERED for clean releases.
459
+ - If incidents WERE closed, skip nil report generation — the populated incident report(s) from `incident-export.yml` serve as the evidence.
460
+
461
+ 6. **Organise artefacts** under `compliance/evidence/REQ-XXX/` with date-prefixed naming:
371
462
 
372
463
  ```
373
464
  compliance/evidence/REQ-XXX/
@@ -383,7 +474,7 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
383
474
 
384
475
  Copy Playwright's `test-results/` folder verbatim into `YYYY-MM-DD_traces/` so trace-by-test-name is available for audit without walking the HTML report's hash-name index. For HIGH/CRITICAL releases the traces are part of the audit trail — _"what state was the page in when test X failed and was overridden?"_ answers in one `ls` instead of an HTML-report walk.
385
476
 
386
- 6. **Upload each artefact to the portal**:
477
+ 6. **Upload each artefact to the portal** — retry each upload up to 3 times with backoff (5s, 15s, 45s). If an upload still fails after 3 retries, mark it in the RTM as `UPLOAD_FAILED` and surface a warning. Do not mark the RTM as verified for failed uploads. On `resume REQ-XXX`, re-attempt failed uploads before proceeding to Phase 4.
387
478
  ```bash
388
479
  devaudit push <project-slug> REQ-XXX <evidence-type> <file> \
389
480
  --release "v$(date +%Y.%m.%d)" --create-release-if-missing \
@@ -392,15 +483,13 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
392
483
  --branch "$(git rev-parse --abbrev-ref HEAD)"
393
484
  ```
394
485
  Evidence types: `screenshot`, `e2e_result`, `test_report`, `audit_log`, `compliance_document`, `manual_upload`, `srs_alignment` (from step 1), `architecture_decision` (from step 2), `risk_assessment` (from step 3).
395
- 7. **Verify uploads landed.** `gh api` or `curl` against `https://devaudit.metasession.co/projects/<slug>/requirements/REQ-XXX/evidence` should show every artefact.
396
- 8. **Update `compliance/RTM.md`** with portal links for each evidence row.
486
+ 7. **Verify uploads landed.** `gh api` or `curl` against `https://devaudit.metasession.co/projects/<slug>/requirements/REQ-XXX/evidence` should show every artefact. Any artefact marked `UPLOAD_FAILED` in step 6 should be re-attempted here.
487
+ 8. **Update `compliance/RTM.md`** with portal links for each evidence row. Rows with `UPLOAD_FAILED` status remain marked as failed — do not mark them verified.
397
488
  9. **Update SDLC status sticky** before exiting Phase 3: `bash scripts/update-sdlc-status.sh "$ISSUE_NUM" "Phase 3 complete — evidence uploaded; SRS-alignment + ADR + risk-assessment artefacts attached" "Phase 4 — sdlc-implementer auto-continuing (open release PR)"`.
398
489
 
399
490
  ### Phase 4 — Submit for UAT review (SDLC stage 4)
400
491
 
401
- 1. **Open the release PR** — the PR that carries the UAT four-eyes approval gate (`check-release-approval.yml`), always into `$RELEASE_BRANCH`:
402
- - develop-first (`$INTEGRATION_BRANCH` ≠ `$RELEASE_BRANCH`): `gh pr create --base "$RELEASE_BRANCH" --head "$INTEGRATION_BRANCH"` (e.g. `develop → main`). The implementation already landed on `$INTEGRATION_BRANCH` in Phase 2; this promotes it. (Note: if other work is also waiting on `$INTEGRATION_BRANCH`, this is a bundled release — every in-scope REQ keeps its own release record and Production approval.)
403
- - trunk-only (`$INTEGRATION_BRANCH` = `$RELEASE_BRANCH`): `gh pr create --base "$RELEASE_BRANCH" --head feat/REQ-XXX-<slug>` (the feature branch from Phase 2).
492
+ 1. **Open the release PR** — the PR that carries the UAT four-eyes approval gate (`check-release-approval.yml`), always into `$RELEASE_BRANCH`: `gh pr create --base "$RELEASE_BRANCH" --head "$INTEGRATION_BRANCH"` (e.g. `develop → main`). The implementation already landed on `$INTEGRATION_BRANCH` in Phase 2; this promotes it. (Note: if other work is also waiting on `$INTEGRATION_BRANCH`, this is a bundled release — every in-scope REQ keeps its own release record and Production approval.)
404
493
 
405
494
  PR body per the SDLC PR template (see [`.github/pull_request_template.md`](../../../../../.github/pull_request_template.md)):
406
495
  - Closes #N
@@ -428,36 +517,58 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
428
517
  3. **Cancel-and-admin-merge is allowed** when **all three** hold: (a) ≥3 other required gates are green, (b) the change has no scope-overlap with the failing gate (e.g. service-layer fix vs hung UI e2e, or an `E2E: N/A by scope` test-plan), and (c) a fallback verification exists (nightly e2e on `$INTEGRATION_BRANCH`, post-deploy smoke, etc.). If any of the three fail, hold the merge and surface the blocker to the operator.
429
518
  4. **Record the decision in the release ticket.** The release ticket's `## Verification` section must mention the cancelled gate by run-ID and the fallback that justifies bypassing it. Auditors look here first.
430
519
 
520
+ **When the cancel-and-admin-merge conditions are NOT met.** If only one or two of the three conditions hold, the skill cannot bypass the gate. Escalation path:
521
+ - Update the sticky: "Phase 4 BLOCKED — gate <name> failing for unrelated reasons. Cancel-and-admin-merge conditions not met (<which condition failed>). Operator action — resolve the gate, re-classify it as unrelated with evidence, or accept the risk and admin-merge manually."
522
+ - If the operator chooses to admin-merge manually (outside the skill), on `resume REQ-XXX` the skill should detect the PR is merged and proceed to Phase 5.
523
+ - Timeout escalation: if the gate hasn't resolved in 24 hours (configurable via `sdlc-config.json:phase4.gate_timeout_hours`), escalate the sticky to "Phase 4 BLOCKED 24h — gate <name> still failing. Escalation required."
524
+
525
+ **Release Approval Gate retry (devaudit-installer#211 Gap 17).** If the Release Approval Gate check fails on the PR and the portal approval was already given (API sync delay, stale cache), retry logic: re-run the Release Approval Gate workflow up to 3 times with 30-second intervals (`gh workflow run check-release-approval.yml` or trigger via `workflow_dispatch`). If it still fails after 3 retries: halt — "Release Approval Gate still failing after 3 retries. Portal may show approval but the gate can't verify it. Operator action — check the portal release status manually, verify the API key is valid, and re-run the workflow from GitHub Actions if needed."
526
+
431
527
  ### Phase 5 — Finalise or change-request loop (SDLC stage 5)
432
528
 
433
529
  Invoked separately by the user after UAT activity on the portal. Trigger: "resume REQ-XXX", "REQ-XXX UAT done", or just re-firing the skill on the same issue.
434
530
 
435
- 1. **Read portal state.** `curl` `https://devaudit.metasession.co/api/projects/<slug>/releases/<version>` and inspect the approval status.
531
+ 1. **Read portal state.** `curl` `https://devaudit.metasession.co/api/projects/<slug>/releases/<version>` and inspect the approval status. Retry up to 3 times with exponential backoff (5s, 15s, 45s) on 5xx responses. If all retries fail, halt — "Portal unreachable at <URL>. Cannot determine UAT approval state. Operator action — verify portal availability + API key, then ping `resume REQ-XXX`." If the API returns 401/403, halt — "Portal API key rejected (HTTP 401/403). The `DEVAUDIT_API_KEY` secret may be expired or revoked. Operator action — rotate the API key, then ping `resume REQ-XXX`." Never guess the portal state.
436
532
 
437
533
  2. **Branch on state:**
438
534
  - **UAT approved** → run stage 5:
535
+ - **Enumerate all in-scope REQs** from `compliance/pending-releases/RELEASE-TICKET-REQ-*.md` and check each one's portal state before merging. If ANY are `uat_changes_requested`: halt — "REQ-YYY has changes requested on the portal. Cannot merge bundled PR until all in-scope REQs are approved." If ANY are still `uat_review`: halt — "REQ-YYY is still pending UAT review." If ALL are `uat_approved` (or `prod_review`/`prod_approved`/`released`): proceed with merge.
439
536
  - `gh pr merge <M> --merge` (merge commit; `--squash` and `--rebase` are blocked by branch protection on SDLC repos and would break the audit trail).
440
- - Watch `post-deploy-prod.yml` via `gh run watch` block until the workflow reaches a terminal state.
537
+ - **Before watching `post-deploy-prod.yml`**, verify the workflow exists and has triggered: `gh run list --workflow post-deploy-prod.yml --limit 1 --json status,conclusion`. If no run appears within 5 minutes of the merge, halt — "post-deploy-prod.yml did not trigger after merge. Check workflow configuration, secrets, and deployment platform. Operator action — run `gh workflow run post-deploy-prod.yml` manually, then ping `resume REQ-XXX`."
538
+ - Watch `post-deploy-prod.yml` via `gh run watch` — block until the workflow reaches a terminal state. Wrap in a timeout (30 minutes default, configurable via `sdlc-config.json:post_deploy.timeout_minutes`). If the timeout fires: halt — "post-deploy-prod.yml has not reached a terminal state in N minutes. The deployment may be stuck. Operator action — check hosting platform logs, decide whether to wait or rollback, then ping `resume REQ-XXX`."
441
539
  - Verify production smoke evidence uploaded (`--environment production`) at `https://devaudit.metasession.co/projects/<slug>/releases/<version>`.
442
- - Mark release as `Released` via portal API: `PATCH /releases/<version>` with `{"status": "released"}`.
443
- - Comment on the issue: "Released. Production smoke evidence: <link>."
540
+ - **Read `production_review.terminal_status` from `sdlc-config.json`** (default: `prod_review`). Branch:
541
+ - If `released` (Option B): PATCH directly to `released` — `PATCH /releases/<version>` with `{"status": "released"}`.
542
+ - If `prod_review` (Option A, default): after `post-deploy-prod.yml` completes, the release is at `prod_review`. Update the sticky to "Phase 5 — production deployed; awaiting prod approval on portal" and halt. The operator clicks "Approve Production" (`prod_review` → `prod_approved`) then "Mark as Released" (`prod_approved` → `released`) in the portal. On next `resume REQ-XXX`, the skill reads the portal state — if `released`, finalise (close issue, update sticky to terminal). If `prod_approved`, prompt the operator to click "Mark as Released". If still `prod_review`, report "awaiting production approval" and stop.
543
+ - Comment on the issue: "Released. Production smoke evidence: <link>." (only after release status is `released`)
444
544
  - **Update SDLC status sticky** to the terminal state: `bash scripts/update-sdlc-status.sh "$ISSUE_NUM" "Phase 5 complete — release marked Released; production smoke evidence uploaded" "Done — close issue + retire feature branch (sdlc-implementer halts)"`.
445
545
  - Close the issue.
446
- - If production smoke fails: do NOT mark as Released. File an `[INCIDENT]` defect issue, page the on-call per the project's incident playbook, follow the rollback plan from the implementation plan. **Update the sticky** to reflect the incident state: `… "Phase 5 BLOCKED — production smoke failed; INCIDENT issue #N filed" "Operator action — read INCIDENT #N + execute rollback per plan"`.
546
+ - If production smoke fails: do NOT mark as Released. **Delegate incident filing to `governance-doc-author` (devaudit-installer#210 §6c)** — invoke `Skill(name: "governance-doc-author", args: "Production smoke failure for REQ-XXX. File an incident issue with the `incident` label + `### Framework attribution` section (ISO29119.3.5.4 + SOC2.CC7.2), severity `blocker`. Include the production URL, git SHA, testCycleId (CI run ID), and smoke results in the issue body.")`. Page the on-call per the project's incident playbook, follow the rollback plan from the implementation plan. If the rollback also fails: update the sticky to "Phase 5 CRITICAL — production smoke failed AND rollback failed. Production is in a broken state. Operator action — page on-call immediately, engage hosting platform support, declare incident per the project's incident playbook. This is beyond the skill's scope." Do NOT attempt further automated remediation. **Update the sticky** to reflect the incident state: `… "Phase 5 BLOCKED — production smoke failed; INCIDENT issue #N filed" "Operator action — read INCIDENT #N + execute rollback per plan"`.
447
547
 
448
548
  - **Changes requested** → run change-request loop:
449
549
  - Fetch change-request comments from the PR (`gh pr view <M> --comments`) and from the portal release page.
550
+ - **Classify the change request (devaudit-installer#210 §6b):** Assess whether the change request is a **defect** (the implementation doesn't match the AC — the reviewer found a bug) or a **scope change** (the AC itself needs amendment — the reviewer wants new behaviour the AC didn't specify).
551
+ - **If it's a defect:** Delegate to `e2e-test-engineer` to file the incident with the `incident` label + `### Framework attribution` section before fixing. Invoke `Skill(name: "e2e-test-engineer", args: "Phase 6 triage — UAT reviewer found defect in REQ-XXX. Change-request comment: <summary>. Triage and file application defects with the `incident` label + `### Framework attribution` section per the standard convention.")`. `e2e-test-engineer` returns with a summary of filed incidents. Then fix the defect in the same Phase 2 re-run. This ensures UAT-discovered defects produce `incident_report` evidence, closing `ISO29119.3.5.4` for the defect — not just silently fixing it.
552
+ - **If it's a scope change:** Use the existing scope-expansion halt gate (REQ-SKILL-IMPLEMENTER-019) — file a separate issue or amend the REQ scope. Do NOT file an incident — the AC itself needs amendment, not a defect fix.
450
553
  - Add a new `## Change-request iteration N` section to `compliance/plans/REQ-XXX/implementation-plan.md` describing what changed and why.
554
+ - **Update the release ticket status** (`compliance/pending-releases/RELEASE-TICKET-REQ-XXX.md`): set `**Status:**` to `CHANGES REQUESTED — ITERATION N` when entering the iteration. Commit the ticket update alongside the delta-plan section in the same iteration commit.
451
555
  - Re-run Phase 2 (implement + test) for the requested changes — same delegation to `e2e-test-engineer` for any e2e work.
452
- - Re-run Phase 3 (evidence) for the new/changed artefacts only; existing evidence stays.
556
+ - Re-run Phase 3 (evidence) for the new/changed artefacts only; existing evidence stays. After evidence re-compile completes, update the release ticket status to `TESTED - PENDING SIGN-OFF (ITERATION N)`.
453
557
  - Push to the same branch (no force-push). The PR auto-updates.
454
558
  - Re-request UAT review on the portal: `POST /api/projects/<slug>/releases/<version>/approval-requests`.
455
559
  - Comment on the issue: "Change requests addressed in commits <SHAs>. UAT re-review requested."
456
560
  - **Update SDLC status sticky** for the re-review handoff: `bash scripts/update-sdlc-status.sh "$ISSUE_NUM" "Change-request iteration N applied; PR pushed; re-review requested" "Operator action — re-review on portal; sdlc-implementer halts until you ping resume REQ-XXX"`.
457
561
  - Hard stop again. The portal's release-approval state has reset; UAT must explicitly re-approve.
562
+ - **After UAT re-approval (Phase 5 happy path):** update the release ticket status to `APPROVED — READY FOR MERGE`. After merge + deploy + prod verification: update to `RELEASED`. If rejected: update to `REJECTED — AWAITING OPERATOR DIRECTION`.
563
+ - **Iteration cap (N=5).** If the change-request loop reaches 5 iterations without convergence, halt and ask the user to choose:
564
+ - **Abandon:** Close the PR (`gh pr close <M>`), close the issue with "REQ-XXX abandoned after N=5 change-request iterations without convergence", update RTM status to `ABANDONED`, update release ticket status to `ABANDONED — ITERATION CAP REACHED (N=5)`, update sticky to terminal. Skill halts.
565
+ - **Split:** Halt with "Recommend filing separate issues for: <inferred sub-scopes>. Close this PR after splitting. Each new issue gets its own `sdlc-implementer` invocation." The skill does not perform the split — it surfaces the recommendation and stops.
566
+ - **Restart from fresh plan:** Keep the same REQ-XXX number (the portal release already exists). Archive the current implementation plan (`mv compliance/plans/REQ-XXX/implementation-plan.md compliance/plans/REQ-XXX/implementation-plan-v1-archived.md`), re-run Phase 1 from step 5 (write new plan). The PR stays open — the new plan's implementation replaces the code on the feature branch. Reset the iteration counter to 0.
458
567
 
459
568
  - **Still pending UAT (no approval, no change-request)** → report "UAT review still pending on the portal at <link>" and stop. Do not act.
460
569
 
570
+ - **UAT rejected** → halt. Post on the issue: "UAT rejected this release. Options: (a) close PR and re-plan from scratch, (b) escalate to a different reviewer, (c) appeal the rejection with new context." See [`references/change-request-loop.md`](./references/change-request-loop.md) §"If the change-request is a rejection" for details. Do not attempt to address — the reviewer has marked the release fundamentally broken. Wait for the user to decide.
571
+
461
572
  ## Compliance constraints
462
573
 
463
574
  Hard rules — the skill's SKILL.md fails review if any of these are violated. Audited against ISO 29119, ISO 27001, SOC 2, GDPR, and the EU AI Act; details in [`references/compliance-constraints.md`](./references/compliance-constraints.md).
@@ -468,6 +579,7 @@ Hard rules — the skill's SKILL.md fails review if any of these are violated. A
468
579
  4. **Change-request loop triggers full UAT re-review.** The portal's release-approval state resets when new commits land on the PR — respect it. Surface a "UAT re-review needed" comment; never rely on prior approval covering subsequent changes.
469
580
  5. **AI involvement disclosed on every commit** via `Co-Authored-By: Claude`. (ISO 27001 disclosure norms + EU AI Act Art. 13 transparency.)
470
581
  6. **All portal mutations through audit-logged APIs.** Use `devaudit push` and the standard portal-API endpoints — never a private back-channel.
582
+ 7. **Never file incidents inline (devaudit-installer#210).** Incident filing (label application, framework attribution, severity classification) is delegated to `e2e-test-engineer` (for E2E-discovered defects) or `governance-doc-author` (for non-E2E incidents). The orchestrator's job is to detect the defect, delegate the filing, receive the return summary, and act on it (halt, rollback, re-review). This keeps the incident filing convention in one place — the sub-skills — and prevents silent evidence gaps where defects are found and fixed but never recorded on the portal.
471
583
 
472
584
  Plus one process risk surfaced explicitly in the principles below (rubber-stamping at UAT). Not enforceable architecturally — the UAT reviewer is the load-bearing human.
473
585
 
@@ -61,4 +61,4 @@ Three rules every orchestrator → sub-skill call should follow:
61
61
 
62
62
  1. **Pass the minimum context the sub-skill needs.** `e2e-test-engineer` needs the implementation plan + diff; it does not need the orchestrator's full state. Over-passing context bloats the sub-skill's working memory and slows it down.
63
63
  2. **Trust the sub-skill's return contract.** If `e2e-test-engineer` says it added 3 tests and they all pass, the orchestrator does not re-verify the assertion. The sub-skill is the authority for its slice.
64
- 3. **Surface sub-skill failures to the human at the orchestrator level.** If `e2e-test-engineer` halts because the project's test framework can't be detected, that's an orchestrator-level halt — the user sees one clear "I couldn't proceed because…" message, not a stack of nested skill output.
64
+ 3. **Surface sub-skill failures to the human at the orchestrator level.** If `e2e-test-engineer` halts because the project's test framework can't be detected, that's an orchestrator-level halt — the user sees one clear "I couldn't proceed because…" message, not a stack of nested skill output. For Phase 1 sub-skills (`requirements-aligner`, `adr-author`, `risk-register-keeper`), failure behavior is defined per-skill in [`SKILL.md` §Sub-skill failure behavior](../SKILL.md#sub-skill-failure-behavior-devaudit-installer211-gap-15): blocking sub-skills halt the orchestrator; advisory sub-skills warn and continue with a skipped marker in the plan.
@@ -40,12 +40,14 @@ For each requested change, mark it as one of:
40
40
  - **Must address** — the reviewer is asking for a specific code/test/doc change.
41
41
  - **Question** — the reviewer is asking for clarification, not asking for a code change.
42
42
  - **Out of scope** — the reviewer is asking for something this REQ doesn't intend to deliver.
43
+ - **Requirements gap** (devaudit-installer#212) — the reviewer identified behaviour that should have been an AC but wasn't. The plan was incomplete, not the implementation. This is distinct from "Out of scope" (the reviewer is asking for something this REQ genuinely shouldn't deliver) and from "Must address" (the reviewer is asking for a code change within existing ACs).
43
44
 
44
45
  For Questions: post a reply on the PR or portal explaining; do NOT change code.
45
46
  For Out-of-scope items: post a reply proposing a follow-up issue; do NOT silently expand REQ-XXX.
47
+ For Requirements gap items: trigger the [requirements gap flow](../SKILL.md#requirements-gap-flow-devaudit-installer212) in `sdlc-implementer` SKILL.md with the reviewer's feedback as the gap report. The operator decides: accept the gap (ship without the edge case), amend the ACs (add the edge case as a new AC, update plan + SRS, re-walk Phase 2-3), or file a follow-up REQ. If "amend" is chosen, the change-request iteration includes the new AC in the delta-plan section, `requirements-aligner` is re-invoked for the new AC, and the SRS is updated.
46
48
  For Must-address items: collect into a delta-plan section.
47
49
 
48
- ### Step 3 — Add a delta-plan section to the implementation plan
50
+ ### Step 3 — Add a delta-plan section to the implementation plan and update the release ticket
49
51
 
50
52
  Append a new section to `compliance/plans/REQ-XXX/implementation-plan.md`:
51
53
 
@@ -75,6 +77,8 @@ Append a new section to `compliance/plans/REQ-XXX/implementation-plan.md`:
75
77
 
76
78
  This section is append-only — never rewrite a prior iteration's delta-plan section.
77
79
 
80
+ **Update the release ticket status** (`compliance/pending-releases/RELEASE-TICKET-REQ-XXX.md`): set `**Status:**` to `CHANGES REQUESTED — ITERATION N`. Commit the ticket update alongside the delta-plan section in the same iteration commit. The status transitions mirror the portal's state machine but are recorded in the ticket file for audit trail completeness.
81
+
78
82
  ### Step 4 — Re-run Phase 2 (Implement and test)
79
83
 
80
84
  For the Must-address items only:
@@ -117,6 +121,8 @@ devaudit push <slug> REQ-XXX <type> <file> \
117
121
 
118
122
  **Existing evidence stays.** Iteration N adds; it does not replace. The audit trail wants to see what each iteration's evidence looked like.
119
123
 
124
+ **After evidence re-compile completes**, update the release ticket status to `TESTED - PENDING SIGN-OFF (ITERATION N)`.
125
+
120
126
  ### Step 6 — Push to the same branch
121
127
 
122
128
  `git push` — no `--force`. The PR auto-updates.
@@ -151,7 +157,10 @@ The skill halts. The human's next move is reviewing the iteration N changes on t
151
157
  A few rules that keep iteration N+1 distinguishable from iteration N:
152
158
 
153
159
  - **One iteration per UAT cycle**, not one per commit. If the reviewer asks for three changes, address all three in iteration N, push once, request re-review once. Don't fire iteration boundaries per commit.
154
- - **Cap at 5 iterations** as a heuristic. If a REQ goes through 5+ iterations without converging, the original plan was probably wrong — halt and ask the user whether to abandon the REQ, split it, or restart from a fresh plan.
160
+ - **Cap at 5 iterations** as a heuristic. If a REQ goes through 5+ iterations without converging, the original plan was probably wrong — halt and ask the user to choose one of three branches:
161
+ - **Abandon:** Close the PR (`gh pr close <M>`), close the issue with "REQ-XXX abandoned after N=5 change-request iterations without convergence", update RTM status to `ABANDONED`, update release ticket status to `ABANDONED — ITERATION CAP REACHED (N=5)`, update sticky to terminal. Skill halts.
162
+ - **Split:** Halt with "Recommend filing separate issues for: <inferred sub-scopes>. Close this PR after splitting. Each new issue gets its own `sdlc-implementer` invocation." The skill does not perform the split — it surfaces the recommendation and stops.
163
+ - **Restart from fresh plan:** Keep the same REQ-XXX number (the portal release already exists). Archive the current implementation plan (`mv compliance/plans/REQ-XXX/implementation-plan.md compliance/plans/REQ-XXX/implementation-plan-v1-archived.md`), re-run Phase 1 from step 5 (write new plan). The PR stays open — the new plan's implementation replaces the code on the feature branch. Reset the iteration counter to 0.
155
164
  - **Never rewrite the original plan's body** in response to iteration feedback. Use the delta-plan sections; the original plan stays as a historical record of what was first proposed.
156
165
  - **Iteration commits are still SDLC-conformant.** Conventional Commits format, `Ref: REQ-XXX`, `Co-Authored-By: Claude`, all gates green locally. No exceptions because "it's just a fix to the previous iteration."
157
166
 
@@ -179,6 +188,21 @@ If the portal state is `uat_rejected` (not `uat_changes_requested`), the reviewe
179
188
  2. Post on the issue: "UAT rejected this release. Awaiting user direction — options are to (a) close the PR and re-plan from scratch, (b) escalate the rejection to a different reviewer, or (c) appeal the rejection with new context."
180
189
  3. Wait for the user to decide. The skill does not unilaterally reopen, force-merge, or escalate.
181
190
 
191
+ ## Release ticket status transitions
192
+
193
+ The release ticket (`compliance/pending-releases/RELEASE-TICKET-REQ-XXX.md`) `**Status:**` field is updated at each transition in the change-request loop, mirroring the portal's state machine for audit trail completeness:
194
+
195
+ | Transition | Release ticket status |
196
+ |---|---|
197
+ | Entering iteration N | `CHANGES REQUESTED — ITERATION N` |
198
+ | After evidence re-compile (Step 5) | `TESTED - PENDING SIGN-OFF (ITERATION N)` |
199
+ | After UAT re-approval (Phase 5 happy path) | `APPROVED — READY FOR MERGE` |
200
+ | After merge + deploy + prod verification | `RELEASED` |
201
+ | If rejected | `REJECTED — AWAITING OPERATOR DIRECTION` |
202
+ | If iteration cap hit and abandoned | `ABANDONED — ITERATION CAP REACHED (N=5)` |
203
+
204
+ Commit each ticket update alongside the corresponding delta-plan or evidence commit in the same iteration commit.
205
+
182
206
  ## Summary
183
207
 
184
208
  The change-request loop preserves the controls a one-shot approval cycle would skip. Every iteration:
@@ -187,6 +211,7 @@ The change-request loop preserves the controls a one-shot approval cycle would s
187
211
  - Documents what was changed and why.
188
212
  - Captures fresh evidence tagged by iteration.
189
213
  - Triggers an explicit UAT re-review on the portal.
214
+ - Updates the release ticket status to reflect the current state.
190
215
  - Maintains the full audit trail.
191
216
 
192
217
  By doing so, even REQs that take five iterations to land have the same compliance posture as REQs that pass UAT on the first try.
@@ -294,7 +294,7 @@ jobs:
294
294
  chmod +x scripts/upload-evidence.sh 2>/dev/null || true
295
295
  # Create the release in DevAudit (no evidence yet — just registration)
296
296
  bash scripts/upload-evidence.sh \
297
- {{PROJECT_SLUG}} _compliance-docs compliance_document README.md \
297
+ {{PROJECT_SLUG}} _compliance-docs release_registration README.md \
298
298
  --release ${{ steps.version.outputs.version }} --create-release-if-missing \
299
299
  --environment uat --category planning \
300
300
  --git-sha ${{ github.sha }} --branch ${{ github.ref_name }} || true
@@ -413,6 +413,7 @@ jobs:
413
413
  FLAGS="${FLAGS} --release ${{ needs.register-release.outputs.version }} --create-release-if-missing"
414
414
  FLAGS="${FLAGS} --environment uat"
415
415
  FLAGS="${FLAGS} --sdlc-stage 2"
416
+ FLAGS="${FLAGS} --test-cycle ${{ github.run_id }}"
416
417
 
417
418
  # Track failures across all uploads so we can fail the job at the
418
419
  # end with the full picture. Previously each upload used
@@ -451,7 +452,7 @@ jobs:
451
452
  STATUS_DEPAUDIT=$(gate_status "$(jq -r '.dependency_audit // "skipped"' ci-evidence/gate-outcomes.json)")
452
453
  STATUS_BUILD=$(gate_status "$(jq -r '.build // "skipped"' ci-evidence/gate-outcomes.json)")
453
454
  upload gate-outcomes.json \
454
- {{PROJECT_SLUG}} _compliance-docs compliance_document ci-evidence/gate-outcomes.json \
455
+ {{PROJECT_SLUG}} _compliance-docs gate_outcome ci-evidence/gate-outcomes.json \
455
456
  --category ci_pipeline ${FLAGS}
456
457
  fi
457
458
 
@@ -488,28 +489,34 @@ jobs:
488
489
  --category security_scan --gate-status "$STATUS_DEPAUDIT" ${FLAGS}
489
490
  fi
490
491
 
491
- # Upload E2E test results (ci_pipeline category)
492
+ # Upload E2E test results (e2e_result category)
492
493
  if [ -f ci-evidence/e2e-results.json ]; then
493
494
  upload e2e-results.json \
494
495
  {{PROJECT_SLUG}} _compliance-docs e2e_result ci-evidence/e2e-results.json \
495
- --category ci_pipeline ${FLAGS}
496
+ --category e2e_result ${FLAGS}
496
497
  fi
497
498
 
498
- # Upload Playwright HTML report (test_report category)
499
+ # Upload Playwright HTML report (e2e_report — run context, NOT
500
+ # a test completion report). Uses evidence_type=e2e_report so
501
+ # the portal can distinguish it from per-REQ test-execution-
502
+ # summary.md (DevAudit-Installer#207).
499
503
  if [ -d ci-evidence/playwright-report ]; then
500
504
  (cd ci-evidence && zip -qr playwright-report.zip playwright-report/) 2>/dev/null || true
501
505
  if [ -f ci-evidence/playwright-report.zip ]; then
502
506
  upload playwright-report.zip \
503
- {{PROJECT_SLUG}} _compliance-docs test_report ci-evidence/playwright-report.zip \
504
- --category test_report ${FLAGS}
507
+ {{PROJECT_SLUG}} _compliance-docs e2e_report ci-evidence/playwright-report.zip \
508
+ --category e2e_report ${FLAGS}
505
509
  fi
506
510
  fi
507
511
 
508
- # Upload Jest coverage summary (test_report category)
512
+ # Upload Jest coverage summary (coverage_report category).
513
+ # Uses evidence_type=coverage_report (NOT test_report) so the
514
+ # portal can distinguish run-context coverage data from per-REQ
515
+ # test completion reports (DevAudit-Installer#207).
509
516
  if [ -f ci-evidence/coverage/coverage-summary.json ]; then
510
517
  upload coverage-summary.json \
511
- {{PROJECT_SLUG}} _compliance-docs test_report ci-evidence/coverage/coverage-summary.json \
512
- --category test_report ${FLAGS}
518
+ {{PROJECT_SLUG}} _compliance-docs coverage_report ci-evidence/coverage/coverage-summary.json \
519
+ --category coverage_report ${FLAGS}
513
520
  fi
514
521
 
515
522
  # Upload project-level Test Summary Report as a baseline
@@ -526,6 +533,56 @@ jobs:
526
533
  --category planning ${FLAGS}
527
534
  fi
528
535
 
536
+ # devaudit-installer#200 Fix 4 — Per-REQ gate evidence fan-out.
537
+ # The gate evidence above was uploaded to the single commit-derived
538
+ # release (FLAGS carries --release <derived>). When multiple REQs
539
+ # have pending release tickets, only that one REQ gets gate evidence
540
+ # on the portal — other in-scope REQs show "no gate evidence" and
541
+ # "no tests tagged with this REQ" even though the gates ran and
542
+ # passed. Fan out gate-outcomes.json + e2e-results.json to each
543
+ # in-scope REQ's release so the portal's per-REQ test parser can
544
+ # extract REQ-tagged tests from the e2e-results.json associated with
545
+ # that REQ's release. Heavy run-context artefacts (SAST, dep-audit,
546
+ # playwright zip, coverage) remain on the commit-derived release
547
+ # only — they are run-level, not per-REQ.
548
+ COMMIT_REQ="${{ needs.register-release.outputs.version }}"
549
+ FANOUT_REQS=()
550
+ if [ -d compliance/pending-releases ]; then
551
+ for TICKET in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
552
+ [ -f "$TICKET" ] || continue
553
+ REQ_ID=$(basename "$TICKET" .md | sed 's/^RELEASE-TICKET-//')
554
+ # #192 — skip REQs already in a terminal release directory.
555
+ if compgen -G "compliance/approved-releases/RELEASE-TICKET-${REQ_ID}.md" > /dev/null 2>&1 \
556
+ || compgen -G "compliance/superseded-releases/RELEASE-TICKET-${REQ_ID}.md" > /dev/null 2>&1; then
557
+ continue
558
+ fi
559
+ # Skip the commit-derived REQ — it already has the evidence.
560
+ if [ "$REQ_ID" = "$COMMIT_REQ" ]; then
561
+ continue
562
+ fi
563
+ FANOUT_REQS+=("$REQ_ID")
564
+ done
565
+ fi
566
+ if [ "${#FANOUT_REQS[@]}" -gt 0 ]; then
567
+ echo "Fanning out gate evidence to in-scope REQs: ${FANOUT_REQS[*]}"
568
+ for REQ_ID in "${FANOUT_REQS[@]}"; do
569
+ FANOUT_FLAGS="--git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }}"
570
+ FANOUT_FLAGS="${FANOUT_FLAGS} --release ${REQ_ID} --create-release-if-missing"
571
+ FANOUT_FLAGS="${FANOUT_FLAGS} --environment uat --sdlc-stage 2"
572
+ FANOUT_FLAGS="${FANOUT_FLAGS} --test-cycle ${{ github.run_id }}"
573
+ if [ -f ci-evidence/gate-outcomes.json ]; then
574
+ upload "gate-outcomes.json -> ${REQ_ID}" \
575
+ {{PROJECT_SLUG}} "${REQ_ID}" gate_outcome ci-evidence/gate-outcomes.json \
576
+ --category ci_pipeline ${FANOUT_FLAGS}
577
+ fi
578
+ if [ -f ci-evidence/e2e-results.json ]; then
579
+ upload "e2e-results.json -> ${REQ_ID}" \
580
+ {{PROJECT_SLUG}} "${REQ_ID}" e2e_result ci-evidence/e2e-results.json \
581
+ --category e2e_result ${FANOUT_FLAGS}
582
+ fi
583
+ done
584
+ fi
585
+
529
586
  # Upload per-AC e2e evidence screenshots, scoped to each in-scope
530
587
  # requirement so they render under "Evidence by requirement" in the
531
588
  # portal. These are the per-assertion `evidenceShot(page, REQ, 'ACn-…')`
@@ -627,7 +684,7 @@ jobs:
627
684
 
628
685
  if bash scripts/upload-evidence.sh \
629
686
  {{PROJECT_SLUG}} "$REQ" screenshot "$NAMED" \
630
- --category test_report ${FLAGS} --release "$REQ" \
687
+ --category screenshot ${FLAGS} --release "$REQ" \
631
688
  "${REQ_META[@]}" \
632
689
  "${ORIGIN_META[@]}"
633
690
  then
@@ -679,8 +736,21 @@ jobs:
679
736
  " 2>/dev/null || echo "0")
680
737
  fi
681
738
  if [ "$TAGGED_COUNT" -eq 0 ]; then
682
- echo "::error::Evidence completeness gate: ${REQ} has zero tagged tests AND zero per-AC screenshots. The release cannot proceed to UAT with no traceable evidence. Ensure specs carry @requirement ${REQ} annotations and evidenceShot() calls (skill Phase 5½, issue #170)."
683
- EVIDENCE_GAPS=$((EVIDENCE_GAPS + 1))
739
+ # devaudit-installer#195 before failing, scan spec files on
740
+ # disk for the REQ ID. CI runs --project=smoke (a fast subset),
741
+ # so specs outside e2e/smoke/ (e2e/realtime/, e2e/critical/,
742
+ # e2e/admin/, etc.) never appear in e2e-results.json. A REQ
743
+ # whose tests live there would be falsely flagged as having
744
+ # "zero traceable evidence" even though well-written, REQ-tagged
745
+ # specs exist on disk. If specs are found but weren't run in
746
+ # this tier, emit a non-blocking warning instead of an error.
747
+ SPECS_ON_DISK=$(grep -rl --include='*.spec.ts' -e "@requirement ${REQ}" -e "tagTest.*${REQ}" -e "'${REQ}'" e2e/ 2>/dev/null | wc -l)
748
+ if [ "$SPECS_ON_DISK" -gt 0 ]; then
749
+ echo "::warning::Evidence completeness gate: ${REQ} has zero tagged tests in this smoke run and zero per-AC screenshots, but ${SPECS_ON_DISK} spec file(s) on disk reference it (outside the smoke project). Evidence exists but was not executed in this CI tier. Consider running --project=critical or --grep \"${REQ}\" to capture evidence."
750
+ else
751
+ echo "::error::Evidence completeness gate: ${REQ} has zero tagged tests AND zero per-AC screenshots. The release cannot proceed to UAT with no traceable evidence. Ensure specs carry @requirement ${REQ} annotations and evidenceShot() calls (skill Phase 5½, issue #170)."
752
+ EVIDENCE_GAPS=$((EVIDENCE_GAPS + 1))
753
+ fi
684
754
  else
685
755
  echo "${REQ} has zero screenshots but ${TAGGED_COUNT} tagged test(s) — evidence-completeness gate passed (screenshot gap is non-blocking)"
686
756
  fi