@metasession.co/devaudit-cli 0.3.12 → 0.3.14

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 (67) hide show
  1. package/dist/index.js +53 -4
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -2
  4. package/scripts/upload-evidence.sh +55 -1
  5. package/sdlc/files/_common/0-project-setup.md +10 -4
  6. package/sdlc/files/_common/Periodic_Security_Review_Schedule.md +5 -5
  7. package/sdlc/files/_common/README_TEMPLATE.md +8 -8
  8. package/sdlc/files/_common/Test_Architecture.md +15 -15
  9. package/sdlc/files/_common/Test_Plan_TEMPLATE.md +13 -13
  10. package/sdlc/files/_common/Test_Policy.md +2 -2
  11. package/sdlc/files/_common/Test_Strategy.md +10 -10
  12. package/sdlc/files/_common/governance/ai-disclosure.md.template +3 -3
  13. package/sdlc/files/_common/governance/dpia.md.template +3 -3
  14. package/sdlc/files/_common/governance/incident-report.md.template +3 -3
  15. package/sdlc/files/_common/governance/nil-incident-report.md.template +1 -1
  16. package/sdlc/files/_common/governance/periodic-review.md.template +3 -3
  17. package/sdlc/files/_common/governance/risk-register.md.template +1 -1
  18. package/sdlc/files/_common/governance/ropa.md.template +2 -2
  19. package/sdlc/files/_common/joining-an-existing-project.md +4 -4
  20. package/sdlc/files/_common/scripts/check-host-deployment.sh +107 -0
  21. package/sdlc/files/_common/scripts/check-host-deployment.test.sh +119 -0
  22. package/sdlc/files/_common/scripts/check-release-pr-scope.sh +95 -0
  23. package/sdlc/files/_common/scripts/check-release-pr-scope.test.sh +137 -0
  24. package/sdlc/files/_common/scripts/close-out-release.sh +175 -54
  25. package/sdlc/files/_common/scripts/close-out-release.test.sh +90 -0
  26. package/sdlc/files/_common/scripts/extract-release-metadata.sh +27 -0
  27. package/sdlc/files/_common/scripts/extract-release-metadata.test.sh +13 -1
  28. package/sdlc/files/_common/scripts/generate-bundled-changes.sh +311 -64
  29. package/sdlc/files/_common/scripts/generate-bundled-changes.test.sh +133 -82
  30. package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.sh +18 -9
  31. package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.test.sh +66 -0
  32. package/sdlc/files/_common/scripts/generate-security-summary.sh +14 -5
  33. package/sdlc/files/_common/scripts/generate-security-summary.test.sh +57 -0
  34. package/sdlc/files/_common/scripts/prepare-release-pr.sh +44 -4
  35. package/sdlc/files/_common/scripts/render-test-cycles.sh +227 -0
  36. package/sdlc/files/_common/scripts/render-test-cycles.test.sh +107 -0
  37. package/sdlc/files/_common/scripts/report-release-check.sh +86 -0
  38. package/sdlc/files/_common/scripts/report-release-check.test.sh +97 -0
  39. package/sdlc/files/_common/scripts/report-test-cycle.sh +344 -0
  40. package/sdlc/files/_common/scripts/submit-bundle-manifest.sh +100 -0
  41. package/sdlc/files/_common/scripts/submit-bundle-manifest.test.sh +138 -0
  42. package/sdlc/files/_common/scripts/upload-evidence.sh +24 -0
  43. package/sdlc/files/_common/scripts/validate-compliance-artifacts.sh +16 -0
  44. package/sdlc/files/_common/scripts/validate-compliance-artifacts.test.sh +14 -0
  45. package/sdlc/files/_common/scripts/validate-test-summary.test.sh +4 -4
  46. package/sdlc/files/_common/skills/adr-author/SKILL.md +2 -2
  47. package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +2 -2
  48. package/sdlc/files/_common/skills/e2e-test-engineer/references/bootstrap.md +5 -5
  49. package/sdlc/files/_common/skills/governance-doc-author/references/incident-classification.md +1 -1
  50. package/sdlc/files/_common/skills/requirements-aligner/SKILL.md +4 -4
  51. package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +21 -2
  52. package/sdlc/files/_common/skills/sdlc-implementer/references/change-request-loop.md +1 -1
  53. package/sdlc/files/_common/skills/sdlc-implementer/references/compliance-constraints.md +7 -14
  54. package/sdlc/files/ci/ci.yml.template +119 -28
  55. package/sdlc/files/ci/close-out-release.yml.template +7 -4
  56. package/sdlc/files/ci/compliance-evidence.yml.template +230 -20
  57. package/sdlc/files/ci/feature-e2e.yml.template +84 -1
  58. package/sdlc/files/ci/post-deploy-prod.yml.template +189 -6
  59. package/sdlc/files/ci/python/ci.yml.template +60 -12
  60. package/sdlc/files/ci/quality-gates-provenance.yml.template +123 -28
  61. package/sdlc/package.json +1 -1
  62. package/sdlc/src/bin/devaudit-sdlc.js +44 -2
  63. package/sdlc/src/blueprints/1-plan-requirement.raw.md +6 -1
  64. package/sdlc/src/blueprints/2-implement-and-test.raw.md +1 -1
  65. package/sdlc/src/blueprints/3-compile-evidence.raw.md +34 -20
  66. package/sdlc/src/blueprints/4-submit-for-review.raw.md +2 -2
  67. package/sdlc/src/blueprints/5-deploy-main.raw.md +2 -2
@@ -306,6 +306,7 @@ EOF
306
306
  echo "## Bundled Changes" > compliance/pending-releases/BUNDLED-CHANGES-REQ-344.md
307
307
  git add . && git commit -q --amend --no-edit
308
308
  run_validator
309
+ assert_grep "missing bundled manifest is reported" "ERROR: Bundled release evidence exists but BUNDLED-CHANGES-REQ-344.json is missing" 1
309
310
  assert_grep "missing structured fields on bundled evidence are reported" "ERROR: Bundled release evidence is missing bundled field '\\*\\*Core tracked release:\\*\\*'" 1
310
311
  assert_grep "missing structured fields on ticket are reported" "ERROR: Release ticket is missing bundled field '\\*\\*Core tracked release:\\*\\*'" 1
311
312
  assert_grep "missing bundled section on ticket is reported" "ERROR: Bundled release evidence exists but the release ticket is missing" 1
@@ -387,6 +388,8 @@ cat > compliance/pending-releases/BUNDLED-CHANGES-REQ-345.md <<'EOF'
387
388
  ## Bundled Changes
388
389
 
389
390
  - **Core tracked release:** `REQ-345`
391
+ - **Bundle manifest:** `BUNDLED-CHANGES-REQ-345.json`
392
+ - **Manifest hash:** `sha256:test-bundle-hash`
390
393
  - **Absorbed predecessor releases:** None
391
394
  - **Absorbed non-release work:** housekeeping syncs only
392
395
  - **Why bundled here:** housekeeping consolidation
@@ -399,8 +402,19 @@ cat > compliance/pending-releases/BUNDLED-CHANGES-REQ-345.md <<'EOF'
399
402
 
400
403
  - `abc123` chore: sync templates
401
404
  EOF
405
+ cat > compliance/pending-releases/BUNDLED-CHANGES-REQ-345.json <<'EOF'
406
+ {
407
+ "schemaVersion": 1,
408
+ "approvalRelease": { "version": "REQ-345" },
409
+ "coreRelease": { "version": "REQ-345" },
410
+ "members": [],
411
+ "nonReleaseWorkItems": [],
412
+ "manifestHash": "sha256:test-bundle-hash"
413
+ }
414
+ EOF
402
415
  git add . && git commit -q --amend --no-edit
403
416
  run_validator
417
+ assert_grep "bundled manifest accepted" "OK: Bundled release manifest present: compliance/pending-releases/BUNDLED-CHANGES-REQ-345.json" 1
404
418
  assert_grep "bundled ticket section accepted" "OK: Release ticket documents bundled release context" 1
405
419
  assert_grep "bundled test summary section accepted" "OK: test-execution-summary.md documents bundled release context" 1
406
420
  assert_grep "bundled security summary section accepted" "OK: security-summary.md documents bundled release context" 1
@@ -182,7 +182,7 @@ make_fixture "$D6" "REQ-006" << 'EOF'
182
182
  ## Accepted skips
183
183
 
184
184
  | Spec | Test | REQ/AC | Classification | Resolution attempted | Approved by | Rationale |
185
- |------|------|--------|---------------|---------------------|-------------|----------|
185
+ | ------ | ------ | -------- | --------------- | --------------------- | ------------- | ---------- |
186
186
  | e2e/foo.spec.ts | "AC1: bar dialog" | REQ-006/AC1 | intentional_non_applicability | N/A — API-only change | operator | API-only change, no UI surface |
187
187
 
188
188
  **Final assessment:** All gates passed. E2E skipped — API-only change.
@@ -279,7 +279,7 @@ make_fixture "$D12" "REQ-012" << 'EOF'
279
279
  ## Accepted skips
280
280
 
281
281
  | Spec | Test | REQ/AC | Classification | Resolution attempted | Approved by | Rationale |
282
- |------|------|--------|---------------|---------------------|-------------|----------|
282
+ | ------ | ------ | -------- | --------------- | --------------------- | ------------- | ---------- |
283
283
  | e2e/foo.spec.ts | "AC3: bar dialog" | REQ-012/AC3 | intentional_non_applicability | N/A — API-only change | operator | API-only change, no UI surface |
284
284
 
285
285
  **Final assessment:** All gates passed. E2E skipped — API-only change.
@@ -317,7 +317,7 @@ make_fixture "$D14" "REQ-014" << 'EOF'
317
317
  ## Accepted skips
318
318
 
319
319
  | Spec | Test | REQ/AC | Classification | Resolution attempted | Approved by | Rationale |
320
- |------|------|--------|---------------|---------------------|-------------|----------|
320
+ | ------ | ------ | -------- | --------------- | --------------------- | ------------- | ---------- |
321
321
 
322
322
  **Final assessment:** All gates passed. E2E skipped — API-only change.
323
323
  EOF
@@ -338,7 +338,7 @@ make_fixture "$D15" "REQ-015" << 'EOF'
338
338
  ## Accepted skips
339
339
 
340
340
  | Spec | Test | REQ/AC | Classification | Resolution attempted | Approved by | Rationale |
341
- |------|------|--------|---------------|---------------------|-------------|----------|
341
+ | ------ | ------ | -------- | --------------- | --------------------- | ------------- | ---------- |
342
342
  | e2e/foo.spec.ts | "AC3: bar dialog" | REQ-015/AC3 | intentional_non_applicability | N/A — API-only change | | API-only change, no UI surface |
343
343
 
344
344
  **Final assessment:** All gates passed. E2E skipped — API-only change.
@@ -12,7 +12,7 @@ The skill is **Phase A scope** (per [DevAudit-Installer#120](https://github.com/
12
12
  ## What this skill owns
13
13
 
14
14
  | Artefact | Lives at | Tier |
15
- |---|---|---|
15
+ | --- | --- | --- |
16
16
  | `docs/ADR/ADR-NNN-<slug>.md` (the SoT, project-spanning) | Top-level project docs | 2 (project strategy) |
17
17
  | `compliance/evidence/REQ-XXX/architecture-decision.md` (per-REQ Tier 3 evidence) | Per-REQ evidence directory | 3 (per-REQ) |
18
18
 
@@ -57,7 +57,7 @@ Input: the REQ's `compliance/plans/REQ-XXX/implementation-plan.md` plus the work
57
57
  **Step 2 — Apply the ADR-worthiness decision tree.** The skill judges *architectural significance* via these signals — any one matching ⇒ ADR warranted:
58
58
 
59
59
  | Signal | Examples | Verdict |
60
- |---|---|---|
60
+ | --- | --- | --- |
61
61
  | New third-party runtime dependency | adding `redis`, `bull`, a new ORM, a new auth provider package | ADR |
62
62
  | New external service | introducing Stripe, Twilio, a new SaaS integration | ADR |
63
63
  | New database / cache / queue tier | adding Redis alongside Postgres, swapping MongoDB for Postgres | ADR |
@@ -136,7 +136,7 @@ For each scenario, write a one-line description. Present the full grouped list t
136
136
  When designing each scenario, also pick the tier it'll live in. Three tiers map to MoSCoW priority + gating point (see `Test_Strategy.md` § *E2E gating model*):
137
137
 
138
138
  | Tier | File location | Picks this when… |
139
- |---|---|---|
139
+ | --- | --- | --- |
140
140
  | **smoke** | `e2e/smoke/*.spec.ts` | Cross-cutting sanity that proves the app is up: login, basic nav, one canonical CRUD per main domain. Runs on every push to the integration branch. Keep small — total smoke wall-clock target is ~3–5 min. |
141
141
  | **critical** | `e2e/critical/*.spec.ts` | Must-priority SRS item that breaks a headline flow if it regresses. Examples: payment authorisation, order completion, admin permission editing, RBAC enforcement on financial surfaces. Runs on PR-to-release-branch. Total critical wall-clock target ~10–15 min (includes smoke). |
142
142
  | **regression** | `e2e/<area>/*.spec.ts` | Should/Could-priority SRS item, edge cases, less-load-bearing flows. Runs nightly + post-merge + dispatch. Total full pack can be 30+ min; that's the point of the tier. |
@@ -365,7 +365,7 @@ Every defect filed from Phase 6 becomes `incident_report` evidence when (a) the
365
365
  Classify the defect against this table when filing — the canonical version lives at `governance-doc-author/references/incident-classification.md`, mirrored here for the e2e workflow:
366
366
 
367
367
  | Defect characteristic | Frameworks/clauses attributed |
368
- |---|---|
368
+ | --- | --- |
369
369
  | **Any test failure / defect** (baseline — always) | `ISO29119.3.5.4` Test incident report |
370
370
  | **Ops impact** (downtime, persistent errors, perf regression, data corruption) | + `SOC2.CC7.2` System monitoring and incident response |
371
371
  | **Security vulnerability** (auth bypass, injection, data exposure) | + `SOC2.CC7.2` + relevant ISO 27001 controls |
@@ -20,7 +20,7 @@ Recommend a primary, mention the runner-up, get user confirmation before install
20
20
  ### Web apps — JavaScript / TypeScript
21
21
 
22
22
  | Stack | Primary | Runner-up | Rationale |
23
- |---|---|---|---|
23
+ | --- | --- | --- | --- |
24
24
  | React / Next.js / Remix | **Playwright** | Cypress | Fast, parallel by default, multi-browser, first-party TS, built-in visual regression, excellent trace viewer. |
25
25
  | Vue / Nuxt | **Playwright** | Cypress | Same reasons. |
26
26
  | Angular | **Playwright** | Cypress | Playwright has overtaken Cypress as the Angular community default; Cypress still common in established Angular shops. |
@@ -32,7 +32,7 @@ Recommend a primary, mention the runner-up, get user confirmation before install
32
32
  ### Mobile
33
33
 
34
34
  | Stack | Primary | Runner-up | Rationale |
35
- |---|---|---|---|
35
+ | --- | --- | --- | --- |
36
36
  | React Native | **Detox** | Appium | Native, fast, integrates with RN test infra. |
37
37
  | Native iOS / Android cross-platform | **Appium** | Maestro | Industry standard; Maestro is gaining popularity for simpler flows. |
38
38
  | Mobile web | **Playwright** (mobile emulation) | Appium (for real-device need) | Playwright covers viewport + UA emulation; Appium for real-device touch interactions. |
@@ -40,14 +40,14 @@ Recommend a primary, mention the runner-up, get user confirmation before install
40
40
  ### Desktop
41
41
 
42
42
  | Stack | Primary | Runner-up | Rationale |
43
- |---|---|---|---|
43
+ | --- | --- | --- | --- |
44
44
  | Electron | **Playwright** | WebdriverIO + Electron service | Playwright has first-party Electron support. |
45
45
  | Tauri | **WebdriverIO** + Tauri driver | — | Official path. |
46
46
 
47
47
  ### Backend-rendered / non-JS web apps
48
48
 
49
49
  | Stack | Primary | Runner-up | Rationale |
50
- |---|---|---|---|
50
+ | --- | --- | --- | --- |
51
51
  | Python (Django, Flask, FastAPI with templates) | **pytest-playwright** | Selenium + pytest | Same Playwright engine, idiomatic for Python test suites. |
52
52
  | Ruby on Rails | **Capybara + Cuprite** | Capybara + Selenium | Cuprite uses CDP, faster than Selenium. |
53
53
  | Java (Spring etc.) | **Playwright for Java** | Selenium + JUnit/TestNG | Playwright Java is mature; Selenium still dominant in enterprise Java. |
@@ -67,7 +67,7 @@ Recommend a primary, mention the runner-up, get user confirmation before install
67
67
  Only add visual regression if the user asked for it or the originating issue is visually significant. Default off for greenfield bootstrap unless explicitly requested.
68
68
 
69
69
  | Need | Recommendation | Notes |
70
- |---|---|---|
70
+ | --- | --- | --- |
71
71
  | Default for Playwright | **`toHaveScreenshot()` built-in** | Zero extra deps; baselines stored in repo. Configure `threshold` and `maxDiffPixels`. |
72
72
  | Default for Cypress | **`cypress-image-snapshot`** | Or `cypress-visual-regression`. Both store baselines in repo. |
73
73
  | Default for WebdriverIO | **`@wdio/visual-service`** | Maintained, image-comparison-based. |
@@ -7,7 +7,7 @@ Used by both `e2e-test-engineer` (when filing defects) and `governance-doc-autho
7
7
  Every `incident_report` evidence row closes `ISO29119.3.5.4` (baseline). Additional clauses depend on the incident's scope.
8
8
 
9
9
  | Defect / incident characteristic | Frameworks/clauses attributed |
10
- |---|---|
10
+ | --- | --- |
11
11
  | **Any test failure / defect** (baseline — always) | `ISO29119.3.5.4` Test incident report |
12
12
  | **Ops impact** (downtime, persistent errors, perf regression, data corruption) | + `SOC2.CC7.2` System monitoring and incident response |
13
13
  | **Security vulnerability** (auth bypass, injection, data exposure beyond GDPR scope) | + `SOC2.CC7.2` + relevant ISO 27001 controls |
@@ -12,7 +12,7 @@ The skill is **Phase A scope** (per [DevAudit-Installer#119](https://github.com/
12
12
  ## What this skill owns
13
13
 
14
14
  | Artefact | Lives at | Tier |
15
- |---|---|---|
15
+ | --- | --- | --- |
16
16
  | `docs/SRS.md` (the SoT, project-spanning) | Top-level project docs | 2 (project strategy) |
17
17
  | `compliance/evidence/REQ-XXX/srs-alignment.md` (per-REQ Tier 3 evidence) | Per-REQ evidence directory | 3 (per-REQ) |
18
18
 
@@ -65,7 +65,7 @@ Input: the REQ's `compliance/plans/REQ-XXX/implementation-plan.md` plus the work
65
65
  **Step 3 — Categorise each AC:**
66
66
 
67
67
  | AC ⇒ SRS state | Action |
68
- |---|---|
68
+ | --- | --- |
69
69
  | **Exact match** — AC traces 1:1 to an existing SRS item, no behavioural delta | Record the mapping; no SRS edit needed |
70
70
  | **Match + drift** — existing SRS item covers the AC's surface but the behaviour has shifted (e.g. new field, new edge case, new error path) | Flag the item as *potentially stale*; the plan must mark it for update in this cycle OR justify why the SRS prose still covers it |
71
71
  | **No match** — AC introduces behaviour the SRS doesn't yet describe | Propose new `REQ-AREA-NNN` (next free ID per area — see Step 4) with a Given/When/Then stub the operator edits |
@@ -79,7 +79,7 @@ Input: the REQ's `compliance/plans/REQ-XXX/implementation-plan.md` plus the work
79
79
  ## SRS items proposed/touched
80
80
 
81
81
  | AC | SRS item | Status | Notes |
82
- |---|---|---|---|
82
+ | --- | --- | --- | --- |
83
83
  | AC1 | REQ-ORDER-005 (existing) | unchanged | Trace-only |
84
84
  | AC2 | REQ-INV-010 (new — proposed) | stub | <one-line behaviour description> |
85
85
  | AC3 | REQ-INV-011 (new — proposed) | stub | <one-line> |
@@ -113,7 +113,7 @@ generated_at: <ISO timestamp>
113
113
  ## ACs traced
114
114
 
115
115
  | AC | SRS item | Action this cycle |
116
- |---|---|---|
116
+ | --- | --- | --- |
117
117
  | AC1 | REQ-ORDER-005 | unchanged |
118
118
  | AC2 | REQ-INV-010 | added (new — see Phase 1 stub) |
119
119
  | AC3 | REQ-INV-011 | added (new) |
@@ -385,7 +385,7 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
385
385
  - **`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.
386
386
  - **`adr-author` fails**: Warn and continue — ADR is advisory by default. Mark the plan's "Architecture decisions" section as "ADR assessment skipped — <error>".
387
387
  - **`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>".
388
- 9. **Update `compliance/RTM.md`** with the new entry: REQ-XXX, title, risk class, linked issue, linked test cases (placeholder), and a provenance marker `sdlc-implementer@<version>` (devaudit-installer#226). The provenance column is the last column in the RTM row — `validate-commits.sh` in CI checks for its presence when `feat`/`fix`/`refactor`/`perf` commits cite a REQ-XXX. Without the stamp, CI fails with "no sdlc-implementer provenance in RTM.md." If the RTM table doesn't have a provenance column, add one with header `Provenance`.
388
+ 9. **Update `compliance/RTM.md`** with the new entry: REQ-XXX, title, risk class, linked issue, linked test cases (placeholder), and a provenance marker `sdlc-implementer@<version>` (devaudit-installer#226). The provenance column is the last column in the RTM row — `validate-commits.sh` in CI checks for its presence when `feat`/`fix`/`refactor`/`perf` commits cite a REQ-XXX. Without the stamp, CI fails with "no sdlc-implementer provenance in RTM.md." If the RTM table doesn't have a provenance column, add one with header `Provenance`. If any RTM cell text needs literal pipe characters (regexes, enum examples, status notes), escape them as `\|` rather than writing bare `|`, otherwise markdownlint will misparse the row as extra columns.
389
389
  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.
390
390
  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.
391
391
  12. **Update SDLC status sticky** before exiting Phase 1: `bash scripts/update-sdlc-status.sh "$ISSUE_NUM" "Phase 1 complete — plan written to compliance/plans/REQ-XXX/implementation-plan.md (risk class <CLASS>)" "Phase 2 — sdlc-implementer auto-continuing"` (or "Operator action — review plan + ping resume" if the HIGH/CRITICAL checkpoint paused).
@@ -511,6 +511,23 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
511
511
 
512
512
  If the validator fails, fix the summary before proceeding. E2E gate results must be one of: `PASS`, `FAIL`, `NOT_NEEDED` (with reason), or `SKIPPED` (with operator-approved rationale). The word "deferred" must never appear in `test-execution-summary.md` — not as a gate state, not in prose, not in final assessment. "Deferred to CI" and "Playwright browsers not installed locally" are environment issues, not gate states. The CI validator (`validate-test-summary.sh`) will reject any summary containing "deferred" or "browsers not installed" on PRs to main.
513
513
 
514
+ 5bb. **Render the Test Cycles table from the portal's cycle read model when available (devaudit-installer#394).** Do not hand-build cycle rows from uploaded artefacts if the portal exposes first-class cycle records. Query the release-journey / cycle API, save the JSON, and render the markdown table with:
515
+
516
+ ```bash
517
+ bash scripts/render-test-cycles.sh /tmp/release-journey.json
518
+ ```
519
+
520
+ First-class rows must preserve:
521
+ - source release
522
+ - SDLC stage
523
+ - cycle ordinal within that source release + stage
524
+ - cycle kind / outcome
525
+ - workflow or run link
526
+ - related evidence
527
+ - incident or remediation reference
528
+
529
+ If the portal does not yet expose first-class cycles, the same helper emits the legacy `testCycleId` grouping fallback and labels it as such. Keep the fallback note in the final `test-execution-summary.md`; it is part of the audit trail during rollout.
530
+
514
531
  5c. **Run the Phase 3 completion guard before any PR/release-review step (devaudit-installer#341).** The tracked-release evidence pack is incomplete until the required Git artefacts exist. Before proceeding past Phase 3, verify:
515
532
 
516
533
  - `compliance/evidence/REQ-XXX/test-scope.md`
@@ -520,6 +537,7 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
520
537
  - `compliance/pending-releases/RELEASE-TICKET-REQ-XXX.md`
521
538
  - `compliance/evidence/REQ-XXX/implementation-plan.md` when the REQ risk class requires it
522
539
  - any already-mandatory AI artefacts required by the risk/usage rules
540
+ - if `compliance/pending-releases/BUNDLED-CHANGES-REQ-XXX.md` exists, regenerate the paired JSON manifest too and keep both aligned with the current approval scope
523
541
  - if `compliance/pending-releases/BUNDLED-CHANGES-REQ-XXX.md` exists, the canonical artefacts must carry the bundle narrative too:
524
542
  - release ticket includes `## Bundled Changes` or `## Absorbed Predecessor Releases`
525
543
  - `test-execution-summary.md` includes `## Bundled Release Context`
@@ -636,7 +654,7 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
636
654
  --base-url <devaudit-base-url>
637
655
  ```
638
656
 
639
- The watcher persists retry state in `.sdlc-pr-watch.json`, polls `gh pr view` + `gh pr checks`, re-runs likely flaky workflows automatically, and re-runs the Release Approval Gate when the portal is already approved but GitHub has not converged yet. Use `--once` when you only need a single classification pass.
657
+ The watcher persists retry state in `.sdlc-pr-watch.json`, polls `gh pr view` + `gh pr checks`, re-runs likely flaky workflows automatically, and re-runs the Release Approval Gate when the portal is already approved but GitHub has not converged yet. For a release PR (`$INTEGRATION_BRANCH -> $RELEASE_BRANCH`), it must not call the PR green until the full required release check set has reached terminal success: `Quality Gates`, `Release Scope Integrity`, `Compliance Validation`, `DevAudit Release Approval`, and `E2E Regression Suite`. Use `--once` when you only need a single classification pass.
640
658
 
641
659
  6. **Hard stop only after blocker classification.** Phase 4 ends here. Do not proceed to merge; the human's next action is reviewing on the portal or clearing the categorized blocker above.
642
660
  7. **Update SDLC status sticky** before halting: `bash scripts/update-sdlc-status.sh "$ISSUE_NUM" "Phase 4 — truthful release PR #<N> prepared against $RELEASE_BRANCH; blockers classified" "Operator action — review PR #<N> + approve UAT release on the portal; sdlc-implementer halts until you ping resume REQ-XXX"`. This is a critical handoff — the sticky must reflect that the agent has stopped + the operator is on the hook.
@@ -675,6 +693,7 @@ Invoked separately by the user after UAT activity on the portal. Trigger: "resum
675
693
  - `gh pr merge <M> --merge` (merge commit; `--squash` and `--rebase` are blocked by branch protection on SDLC repos and would break the audit trail).
676
694
  - **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`."
677
695
  - 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`."
696
+ - Treat `post-deploy-prod.yml` success as necessary but not sufficient. The workflow must also confirm the hosting platform's GitHub deployment status for the merged SHA reached terminal `success`. If the host deployment is still `queued` / `in_progress`, remain blocked. If it reaches `failure`, halt — "The host deployment for <sha> failed after merge. Do not approve Production or mark Released. Operator action — inspect hosting logs / rollback / resume once the production state is understood."
678
697
  - Verify production smoke evidence uploaded (`--environment production`) at `https://devaudit.metasession.co/projects/<slug>/releases/<version>`.
679
698
  - **Read `production_review.terminal_status` from `sdlc-config.json`** (default: `prod_review`). Branch:
680
699
  - If `released` (Option B): PATCH directly to `released` — `PATCH /releases/<version>` with `{"status": "released"}`.
@@ -193,7 +193,7 @@ If the portal state is `uat_rejected` (not `uat_changes_requested`), the reviewe
193
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
194
 
195
195
  | Transition | Release ticket status |
196
- |---|---|
196
+ | --- | --- |
197
197
  | Entering iteration N | `CHANGES REQUESTED — ITERATION N` |
198
198
  | After evidence re-compile (Step 5) | `TESTED - PENDING SIGN-OFF (ITERATION N)` |
199
199
  | After UAT re-approval (Phase 5 happy path) | `APPROVED — READY FOR MERGE` |
@@ -8,8 +8,7 @@ The constraints are enforced in `SKILL.md` and in the smoke pass. Where applicab
8
8
 
9
9
  ### 1. Never skip the UAT review gate
10
10
 
11
- | | |
12
- |---|---|
11
+ | | | | --- | --- |
13
12
  | **What** | The skill must not attempt to merge a PR while the `DevAudit Release Approval` check is red. |
14
13
  | **Why (framework)** | ISO 27001 A.8.32 (Change management); SOC 2 CC8 (Change management); EU AI Act Art. 14 (Human oversight). The UAT gate is the load-bearing human-oversight checkpoint. |
15
14
  | **Enforcement** | Portal-side via `check-release-approval.yml` workflow status check + branch protection. Skill-side: never call `gh pr merge` until the check is green. |
@@ -17,8 +16,7 @@ The constraints are enforced in `SKILL.md` and in the smoke pass. Where applicab
17
16
 
18
17
  ### 2. For HIGH/CRITICAL, never act as the UAT approver
19
18
 
20
- | | |
21
- |---|---|
19
+ | | | | --- | --- |
22
20
  | **What** | The skill must verify at Phase 4 that the configured UAT reviewer (in `compliance/plans/REQ-XXX/implementation-plan.md` §Four-eyes attestation slot) differs from the skill-trigger user. If they match, halt. |
23
21
  | **Why (framework)** | SOC 2 CC8.1 — segregation of duties; ISO 27001 A.5.15 (Access control — duty segregation). The change-author cannot also be the change-approver for higher-risk work. |
24
22
  | **Enforcement** | Portal-side: the release-approval API rejects self-approval for MEDIUM/HIGH/CRITICAL risk classes. Skill-side: refuses to open the PR if the configured reviewer matches the trigger user. |
@@ -26,8 +24,7 @@ The constraints are enforced in `SKILL.md` and in the smoke pass. Where applicab
26
24
 
27
25
  ### 3. Plan checkpoint mandatory for HIGH/CRITICAL
28
26
 
29
- | | |
30
- |---|---|
27
+ | | | | --- | --- |
31
28
  | **What** | At Phase 1 step 8, the skill pauses for human approval of the plan **iff** risk class is HIGH or CRITICAL. LOW/MEDIUM pass through automatically. Can be forced on for all classes via `--require-plan-approval` flag or `DEVAUDIT_REQUIRE_PLAN_APPROVAL=1` env var. |
32
29
  | **Why (framework)** | EU AI Act Art. 14 (Human oversight for high-risk AI systems); SOC 2 CC3 (Risk assessment requires human judgement); ISO 29119 §6 (Test management — planning is a documented human step). For HIGH/CRITICAL work the plan IS the design — human review must precede code. |
33
30
  | **Enforcement** | Skill-side. Phase 1 explicitly halts and waits for the human's "go" before Phase 2. |
@@ -35,8 +32,7 @@ The constraints are enforced in `SKILL.md` and in the smoke pass. Where applicab
35
32
 
36
33
  ### 4. Change-request loop triggers full UAT re-review
37
34
 
38
- | | |
39
- |---|---|
35
+ | | | | --- | --- |
40
36
  | **What** | When new commits land on a PR after a UAT change-request, the portal's release-approval state resets. The skill respects the reset, surfaces a "UAT re-review needed" comment, and never assumes the prior approval still covers the new changes. |
41
37
  | **Why (framework)** | ISO 27001 A.14.2.4 (Restrictions on changes to software packages — each change goes through controls); SOC 2 CC8.1 (Each change requires approval). Treating "approval given once, all subsequent commits inherit it" would make UAT a one-shot rubber-stamp. |
42
38
  | **Enforcement** | Portal-side: the release-approval state machine resets on new commits to the linked branch. Skill-side: re-requests review explicitly via the portal API after each change-request iteration. |
@@ -44,8 +40,7 @@ The constraints are enforced in `SKILL.md` and in the smoke pass. Where applicab
44
40
 
45
41
  ### 5. AI involvement disclosed on every commit
46
42
 
47
- | | |
48
- |---|---|
43
+ | | | | --- | --- |
49
44
  | **What** | Every commit the skill creates carries a `Co-Authored-By: Claude <noreply@anthropic.com>` trailer (or equivalent for the specific Claude model in use). |
50
45
  | **Why (framework)** | ISO 27001 — transparency norms; EU AI Act Art. 13 (Transparency and provision of information to deployers); GDPR Art. 22 (data subjects' right to know about automated decisions, where applicable). When an auditor traces a commit, the AI involvement must be visible. |
51
46
  | **Enforcement** | Skill-side. Every `git commit` call includes the trailer. CI's `compliance-validation.yml` workflow can be configured to grep for `Co-Authored-By` on PRs labelled with the AI-touched marker; this is optional but recommended. |
@@ -53,8 +48,7 @@ The constraints are enforced in `SKILL.md` and in the smoke pass. Where applicab
53
48
 
54
49
  ### 6. All portal mutations through audit-logged APIs
55
50
 
56
- | | |
57
- |---|---|
51
+ | | | | --- | --- |
58
52
  | **What** | The skill calls `devaudit push` and the standard portal HTTP APIs — never a database back-channel, never a service-role-key-equivalent that bypasses the audit log. |
59
53
  | **Why (framework)** | ISO 27001 A.12.4 (Logging and monitoring); SOC 2 CC4 + CC7 (Monitoring activities + system operations). Every action that affects compliance state must produce an audit-log entry attributable to a human identity (the user whose PAT is in `DEVAUDIT_USER_TOKEN`). |
60
54
  | **Enforcement** | Skill-side. All portal interaction goes through `devaudit push` or `curl`/`gh api` against the documented REST endpoints. |
@@ -64,8 +58,7 @@ The constraints are enforced in `SKILL.md` and in the smoke pass. Where applicab
64
58
 
65
59
  ### 7. Rubber-stamping at UAT
66
60
 
67
- | | |
68
- |---|---|
61
+ | | | | --- | --- |
69
62
  | **What** | If the UAT reviewer approves without actually reading the change, the controls are formally satisfied but substantively hollow. |
70
63
  | **Why (framework)** | SOC 2 CC4.1 (Monitoring activities — the entity should detect this via its own QA sampling). Not enforced by any control mechanism; relies on human discipline. |
71
64
  | **Enforcement** | Not architectural. Mitigations: the SKILL.md's Principles section names the UAT reviewer as "the load-bearing human"; the portal's UAT-review UI intentionally adds friction (no one-click approve from email); org policy should random-sample approvals for QA. |
@@ -6,8 +6,9 @@
6
6
  # Single consolidated job — on a self-hosted runner, parallel jobs run
7
7
  # sequentially anyway. One checkout + one cached npm ci = fast.
8
8
  #
9
- # PRs to develop run Quality Gates before integration. PRs to main are verified
10
- # by quality-gates-provenance.yml (same-SHA provenance check, not heavy CI rerun).
9
+ # PRs to {{INTEGRATION_BRANCH}} run Quality Gates before integration. PRs to
10
+ # {{RELEASE_BRANCH}} are handled by quality-gates-provenance.yml: provenance-only
11
+ # for release PRs, full CI rerouted for hotfix/* PRs.
11
12
  # Compliance validation runs separately on PRs (compliance-validation.yml).
12
13
 
13
14
  name: CI Pipeline
@@ -15,9 +16,9 @@ name: CI Pipeline
15
16
  on:
16
17
  workflow_dispatch:
17
18
  pull_request:
18
- branches: [develop]
19
+ branches: [{{INTEGRATION_BRANCH}}]
19
20
  push:
20
- branches: [develop]
21
+ branches: [{{INTEGRATION_BRANCH}}]
21
22
  paths-ignore:
22
23
  {{PATHS_IGNORE}} - 'sdlc-config.json'
23
24
 
@@ -31,6 +32,8 @@ jobs:
31
32
  # ──────────────────────────────────────────────
32
33
  quality-gates:
33
34
  name: Quality Gates
35
+ needs: [register-release]
36
+ if: ${{ always() && (github.event_name != 'pull_request' || !startsWith(github.head_ref, 'chore/close-out-')) && (github.event_name == 'pull_request' || needs.register-release.result == 'success') }}
34
37
  runs-on: {{RUNNER}}
35
38
 
36
39
  services:
@@ -42,6 +45,8 @@ jobs:
42
45
  env:
43
46
  {{DATABASE_ENV}}
44
47
  {{APP_ENV}}
48
+ DEVAUDIT_BASE_URL_VAR: ${{ vars.DEVAUDIT_BASE_URL }}
49
+ DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
45
50
 
46
51
  steps:
47
52
  - uses: actions/checkout@v6
@@ -52,6 +57,29 @@ jobs:
52
57
  # tag each captured screenshot as feature vs regression.
53
58
  fetch-depth: 0
54
59
 
60
+ - name: Start authoritative quality-gate cycle
61
+ if: github.event_name != 'pull_request' && github.ref_name == '{{INTEGRATION_BRANCH}}' && needs.register-release.outputs.version != 'skip'
62
+ run: |
63
+ BASE=""
64
+ if [ -f sdlc-config.json ]; then
65
+ BASE=$(jq -r '.devaudit.base_url // empty' sdlc-config.json 2>/dev/null || true)
66
+ fi
67
+ [ -n "$BASE" ] || BASE="$DEVAUDIT_BASE_URL_VAR"
68
+ [ -n "$BASE" ] || { echo "::warning::No DevAudit URL; cycle lifecycle disabled"; exit 0; }
69
+ echo "DEVAUDIT_BASE_URL=${BASE%/}" >> "$GITHUB_ENV"
70
+ chmod +x scripts/report-test-cycle.sh scripts/report-release-check.sh 2>/dev/null || true
71
+ bash scripts/report-test-cycle.sh start \
72
+ --project-slug {{PROJECT_SLUG}} \
73
+ --release "${{ needs.register-release.outputs.version }}" \
74
+ --sdlc-stage 2 --environment ci --cycle-kind quality_gate \
75
+ --provider github_actions --external-run-id "${{ github.run_id }}" \
76
+ --external-run-attempt "${{ github.run_attempt }}" \
77
+ --external-job-id "quality-gates" \
78
+ --idempotency-key "github:${{ github.repository }}:quality-gates.quality_gate:${{ github.run_id }}:${{ github.run_attempt }}:2:${{ needs.register-release.outputs.version }}" \
79
+ --commit-sha "${{ github.sha }}" --branch "${{ github.ref_name }}" \
80
+ --workflow-name "Quality Gates" \
81
+ --workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
82
+
55
83
  # ── Cached installs (skip if already present on self-hosted runner) ──
56
84
 
57
85
  - uses: actions/setup-node@v6
@@ -238,7 +266,7 @@ jobs:
238
266
  # PRs to develop should report Quality Gates without mutating DevAudit
239
267
  # releases or evidence. Release registration remains a develop-push /
240
268
  # manual-dispatch side effect.
241
- if: ${{ github.event_name != 'pull_request' }}
269
+ if: ${{ github.event_name != 'pull_request' && github.ref_name == '{{INTEGRATION_BRANCH}}' }}
242
270
  outputs:
243
271
  version: ${{ steps.version.outputs.version }}
244
272
  env:
@@ -295,18 +323,16 @@ jobs:
295
323
  echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
296
324
  echo "Release version: ${VERSION}"
297
325
 
298
- - name: Check release exists (no creation — deferred to upload-evidence)
326
+ - name: Ensure release exists for gate-cycle lineage
299
327
  if: env.DEVAUDIT_BASE_URL != '' && steps.version.outputs.version != 'skip'
300
328
  run: |
301
- # Release creation is deferred to the upload-evidence job (which
302
- # runs after gates pass) so the portal never shows a release from
303
- # a push where gates weren't verified (devaudit-installer#226).
304
- # This step only checks whether the release already exists (e.g.
305
- # created earlier by sdlc-implementer Phase 1 step 3).
329
+ # First-class cycle APIs need an exact release row before the
330
+ # stage-2 cycle can start. This keeps failed gate runs visible even
331
+ # when later evidence generation fails.
306
332
  chmod +x scripts/upload-evidence.sh 2>/dev/null || true
307
333
  bash scripts/upload-evidence.sh \
308
334
  {{PROJECT_SLUG}} _compliance-docs release_registration README.md \
309
- --release ${{ steps.version.outputs.version }} \
335
+ --release ${{ steps.version.outputs.version }} --create-release-if-missing \
310
336
  --environment uat --category planning \
311
337
  --git-sha ${{ github.sha }} --branch ${{ github.ref_name }} || true
312
338
 
@@ -338,8 +364,19 @@ jobs:
338
364
  fi
339
365
  echo "Scanning bundled changes since ${SINCE_REF} for ${VERSION}"
340
366
  BUNDLED_FILE="compliance/pending-releases/BUNDLED-CHANGES-${VERSION}.md"
367
+ BUNDLED_MANIFEST="compliance/pending-releases/BUNDLED-CHANGES-${VERSION}.json"
341
368
  mkdir -p compliance/pending-releases
342
- bash scripts/generate-bundled-changes.sh "$SINCE_REF" "$VERSION" > "$BUNDLED_FILE"
369
+ chmod +x scripts/submit-bundle-manifest.sh 2>/dev/null || true
370
+ bash scripts/generate-bundled-changes.sh \
371
+ "$SINCE_REF" "$VERSION" --json-out "$BUNDLED_MANIFEST" > "$BUNDLED_FILE"
372
+ MEMBER_COUNT=$(jq -r '(.members // []) | length' "$BUNDLED_MANIFEST" 2>/dev/null || echo 0)
373
+ WORK_ITEM_COUNT=$(jq -r '(.nonReleaseWorkItems // []) | length' "$BUNDLED_MANIFEST" 2>/dev/null || echo 0)
374
+ if [ "$MEMBER_COUNT" = "0" ] && [ "$WORK_ITEM_COUNT" = "0" ]; then
375
+ echo "No bundled predecessor releases or non-release work detected for ${VERSION}."
376
+ rm -f "$BUNDLED_FILE" "$BUNDLED_MANIFEST"
377
+ exit 0
378
+ fi
379
+ bash scripts/submit-bundle-manifest.sh {{PROJECT_SLUG}} "$VERSION" "$BUNDLED_MANIFEST"
343
380
  # Upload as bundled_changes evidence against the REQ release.
344
381
  bash scripts/upload-evidence.sh \
345
382
  {{PROJECT_SLUG}} _compliance-docs bundled_changes "$BUNDLED_FILE" \
@@ -407,7 +444,7 @@ jobs:
407
444
  # evidence — `status=failed` is itself the audit trail. `!cancelled()`
408
445
  # still guards against partial state on operator-cancel.
409
446
  # DevAudit-Installer#96.
410
- if: ${{ always() && !cancelled() && needs.register-release.result == 'success' }}
447
+ if: ${{ always() && github.ref_name == '{{INTEGRATION_BRANCH}}' && needs.register-release.result == 'success' }}
411
448
  env:
412
449
  DEVAUDIT_BASE_URL_VAR: ${{ vars.DEVAUDIT_BASE_URL }}
413
450
  DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
@@ -444,17 +481,30 @@ jobs:
444
481
  - name: Generate and upload gate evidence
445
482
  if: env.DEVAUDIT_BASE_URL != ''
446
483
  run: |
447
- chmod +x scripts/upload-evidence.sh 2>/dev/null || true
448
- # --create-release-if-missing is intentionally here (post-gate) rather
449
- # than in register-release. This ensures the portal release record only
450
- # appears after gates have run — even if gates failed, the release
451
- # record is created here so failure evidence attaches to it.
452
- # (devaudit-installer#226)
484
+ chmod +x scripts/upload-evidence.sh scripts/report-test-cycle.sh 2>/dev/null || true
453
485
  FLAGS="--git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }}"
454
486
  FLAGS="${FLAGS} --release ${{ needs.register-release.outputs.version }} --create-release-if-missing"
455
487
  FLAGS="${FLAGS} --environment uat"
456
488
  FLAGS="${FLAGS} --sdlc-stage 2"
457
- FLAGS="${FLAGS} --test-cycle ${{ github.run_id }}"
489
+ PRIMARY_LINEAGE_FLAGS=(--test-cycle "${{ github.run_id }}")
490
+ CYCLE_OUT="$(mktemp)"
491
+ bash scripts/report-test-cycle.sh start \
492
+ --project-slug {{PROJECT_SLUG}} \
493
+ --release "${{ needs.register-release.outputs.version }}" \
494
+ --sdlc-stage 2 --environment ci --cycle-kind quality_gate \
495
+ --provider github_actions --external-run-id "${{ github.run_id }}" \
496
+ --external-run-attempt "${{ github.run_attempt }}" \
497
+ --external-job-id "quality-gates" \
498
+ --idempotency-key "github:${{ github.repository }}:quality-gates.quality_gate:${{ github.run_id }}:${{ github.run_attempt }}:2:${{ needs.register-release.outputs.version }}" \
499
+ --commit-sha "${{ github.sha }}" --branch "${{ github.ref_name }}" \
500
+ --workflow-name "Quality Gates" \
501
+ --workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
502
+ --output-file "$CYCLE_OUT"
503
+ . "$CYCLE_OUT"
504
+ rm -f "$CYCLE_OUT"
505
+ if [ "${cycle_supported:-false}" = "true" ] && [ -n "${cycle_record_id:-}" ]; then
506
+ PRIMARY_LINEAGE_FLAGS+=(--evidence-scope cycle --test-cycle-record-id "${cycle_record_id}")
507
+ fi
458
508
 
459
509
  # Track failures across all uploads so we can fail the job at the
460
510
  # end with the full picture. Previously each upload used
@@ -494,7 +544,7 @@ jobs:
494
544
  STATUS_BUILD=$(gate_status "$(jq -r '.build // "skipped"' ci-evidence/gate-outcomes.json)")
495
545
  upload gate-outcomes.json \
496
546
  {{PROJECT_SLUG}} _compliance-docs gate_outcome ci-evidence/gate-outcomes.json \
497
- --category ci_pipeline ${FLAGS}
547
+ --category ci_pipeline ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
498
548
  fi
499
549
 
500
550
  # Re-generate gate evidence as fallback if artifact download failed
@@ -519,7 +569,7 @@ jobs:
519
569
  if [ -f ci-evidence/sast-results.json ]; then
520
570
  upload sast-results.json \
521
571
  {{PROJECT_SLUG}} _compliance-docs sast_report ci-evidence/sast-results.json \
522
- --category security_scan --gate-status "$STATUS_SAST" ${FLAGS}
572
+ --category security_scan --gate-status "$STATUS_SAST" ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
523
573
  fi
524
574
 
525
575
  # Upload dependency audit — precise evidence_type=dependency_audit
@@ -527,14 +577,14 @@ jobs:
527
577
  if [ -f ci-evidence/dependency-audit.json ]; then
528
578
  upload dependency-audit.json \
529
579
  {{PROJECT_SLUG}} _compliance-docs dependency_audit ci-evidence/dependency-audit.json \
530
- --category security_scan --gate-status "$STATUS_DEPAUDIT" ${FLAGS}
580
+ --category security_scan --gate-status "$STATUS_DEPAUDIT" ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
531
581
  fi
532
582
 
533
583
  # Upload E2E test results (e2e_result category)
534
584
  if [ -f ci-evidence/e2e-results.json ]; then
535
585
  upload e2e-results.json \
536
586
  {{PROJECT_SLUG}} _compliance-docs e2e_result ci-evidence/e2e-results.json \
537
- --category e2e_result ${FLAGS}
587
+ --category e2e_result ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
538
588
  fi
539
589
 
540
590
  # Upload Playwright HTML report (e2e_report — run context, NOT
@@ -546,7 +596,7 @@ jobs:
546
596
  if [ -f ci-evidence/playwright-report.zip ]; then
547
597
  upload playwright-report.zip \
548
598
  {{PROJECT_SLUG}} _compliance-docs e2e_report ci-evidence/playwright-report.zip \
549
- --category e2e_report ${FLAGS}
599
+ --category e2e_report ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
550
600
  fi
551
601
  fi
552
602
 
@@ -557,7 +607,7 @@ jobs:
557
607
  if [ -f ci-evidence/coverage/coverage-summary.json ]; then
558
608
  upload coverage-summary.json \
559
609
  {{PROJECT_SLUG}} _compliance-docs coverage_report ci-evidence/coverage/coverage-summary.json \
560
- --category coverage_report ${FLAGS}
610
+ --category coverage_report ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
561
611
  fi
562
612
 
563
613
  # Upload project-level Test Summary Report as a baseline
@@ -571,7 +621,9 @@ jobs:
571
621
  if [ -f "compliance/test-summary-report.md" ]; then
572
622
  upload test-summary-report.md \
573
623
  {{PROJECT_SLUG}} _compliance-docs compliance_document compliance/test-summary-report.md \
574
- --category planning ${FLAGS}
624
+ --category planning --git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }} \
625
+ --release ${{ needs.register-release.outputs.version }} --create-release-if-missing \
626
+ --environment uat --sdlc-stage 2
575
627
  fi
576
628
 
577
629
  # devaudit-installer#200 Fix 4 — Per-REQ gate evidence fan-out.
@@ -850,5 +902,44 @@ jobs:
850
902
  exit 1
851
903
  fi
852
904
 
905
+ - name: Complete primary quality-gate cycle
906
+ if: always() && env.DEVAUDIT_BASE_URL != ''
907
+ run: |
908
+ case "${{ needs.quality-gates.result }}" in
909
+ success) OUTCOME=passed ;;
910
+ cancelled) OUTCOME=cancelled ;;
911
+ *) OUTCOME=failed ;;
912
+ esac
913
+ chmod +x scripts/report-test-cycle.sh scripts/report-release-check.sh 2>/dev/null || true
914
+ bash scripts/report-test-cycle.sh complete \
915
+ --project-slug {{PROJECT_SLUG}} \
916
+ --release "${{ needs.register-release.outputs.version }}" \
917
+ --sdlc-stage 2 \
918
+ --environment ci \
919
+ --cycle-kind quality_gate \
920
+ --provider github_actions \
921
+ --external-run-id "${{ github.run_id }}" \
922
+ --external-run-attempt "${{ github.run_attempt }}" \
923
+ --external-job-id "quality-gates" \
924
+ --idempotency-key "github:${{ github.repository }}:quality-gates.quality_gate:${{ github.run_id }}:${{ github.run_attempt }}:2:${{ needs.register-release.outputs.version }}" \
925
+ --commit-sha "${{ github.sha }}" \
926
+ --branch "${{ github.ref_name }}" \
927
+ --workflow-name "Quality Gates" \
928
+ --workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
929
+ --outcome "$OUTCOME"
930
+ case "$OUTCOME" in
931
+ passed) CHECK_STATUS=successful ;;
932
+ *) CHECK_STATUS="$OUTCOME" ;;
933
+ esac
934
+ bash scripts/report-release-check.sh \
935
+ --project-slug {{PROJECT_SLUG}} \
936
+ --release "${{ needs.register-release.outputs.version }}" \
937
+ --check-key "quality-gates:${{ github.run_id }}:${{ github.run_attempt }}" \
938
+ --label "Quality Gates" --provider github_actions --status "$CHECK_STATUS" \
939
+ --external-run-id "${{ github.run_id }}" \
940
+ --external-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
941
+ --commit-sha "${{ github.sha }}" --branch "${{ github.ref_name }}" \
942
+ --details-json '{"executionSource":"needs.quality-gates.result","evidenceUploadJob":"${{ job.status }}"}'
943
+
853
944
  - name: Summary
854
945
  run: echo "Evidence uploaded for ${{ needs.register-release.outputs.version }} (UAT)"