@metasession.co/devaudit-cli 0.3.18 → 0.3.20

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 (36) hide show
  1. package/dist/index.js +35 -34
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -2
  4. package/scripts/upload-evidence.sh +23 -19
  5. package/sdlc/files/_common/governance/incident-report.md.template +6 -1
  6. package/sdlc/files/_common/governance/nil-incident-report.md.template +5 -2
  7. package/sdlc/files/_common/scripts/check-release-pr-scope.sh +8 -1
  8. package/sdlc/files/_common/scripts/check-release-pr-scope.test.sh +58 -10
  9. package/sdlc/files/_common/scripts/check-self-hosted-runner.sh +103 -0
  10. package/sdlc/files/_common/scripts/check-self-hosted-runner.test.sh +74 -0
  11. package/sdlc/files/_common/scripts/record-uat-execution.sh +195 -0
  12. package/sdlc/files/_common/scripts/record-uat-execution.test.sh +144 -0
  13. package/sdlc/files/_common/scripts/render-test-executions.sh +146 -0
  14. package/sdlc/files/_common/scripts/{render-test-cycles.test.sh → render-test-executions.test.sh} +12 -18
  15. package/sdlc/files/_common/scripts/{report-test-cycle.sh → report-test-execution.sh} +66 -74
  16. package/sdlc/files/_common/scripts/report-test-execution.test.sh +177 -0
  17. package/sdlc/files/_common/scripts/submit-for-uat-review.sh +21 -0
  18. package/sdlc/files/_common/scripts/upload-evidence.sh +23 -19
  19. package/sdlc/files/_common/scripts/validate-commits.sh +6 -4
  20. package/sdlc/files/_common/scripts/validate-commits.test.sh +15 -0
  21. package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +9 -7
  22. package/sdlc/files/_common/skills/e2e-test-engineer/references/e2e-regression-3-tier.yml +4 -1
  23. package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +16 -14
  24. package/sdlc/files/ci/ci.yml.template +54 -44
  25. package/sdlc/files/ci/compliance-evidence.yml.template +135 -46
  26. package/sdlc/files/ci/feature-e2e.yml.template +58 -18
  27. package/sdlc/files/ci/incident-export.yml.template +18 -4
  28. package/sdlc/files/ci/post-deploy-prod.yml.template +33 -30
  29. package/sdlc/files/ci/python/ci.yml.template +14 -14
  30. package/sdlc/files/ci/quality-gates-provenance.yml.template +3 -0
  31. package/sdlc/package.json +1 -1
  32. package/sdlc/src/blueprints/3-compile-evidence.raw.md +10 -10
  33. package/sdlc/src/blueprints/4-submit-for-review.raw.md +1 -1
  34. package/sdlc/src/blueprints/5-deploy-main.raw.md +1 -1
  35. package/sdlc/src/blueprints/implementing-an-sdlc-issue.raw.md +3 -3
  36. package/sdlc/files/_common/scripts/render-test-cycles.sh +0 -227
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env bash
2
+ # report-test-execution.test.sh - Focused regression tests for the test execution lifecycle helper.
3
+
4
+ set -euo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ HELPER="$SCRIPT_DIR/report-test-execution.sh"
8
+ [ -x "$HELPER" ] || chmod +x "$HELPER"
9
+
10
+ PASS=0
11
+ FAIL=0
12
+
13
+ ok() { echo " PASS: $1"; PASS=$((PASS + 1)); }
14
+ no() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); }
15
+
16
+ make_fixture() {
17
+ local dir="$1"
18
+ rm -rf "$dir"
19
+ mkdir -p "$dir/bin"
20
+ cat > "$dir/bin/curl" <<'EOF'
21
+ #!/usr/bin/env bash
22
+ set -euo pipefail
23
+ LOG_FILE="${EXECUTION_TEST_LOG:?}"
24
+ URL=""
25
+ OUT_FILE=""
26
+ WRITE_OUT=""
27
+ BODY=""
28
+ ARGS=("$@")
29
+ for ((i=0; i<${#ARGS[@]}; i++)); do
30
+ arg="${ARGS[$i]}"
31
+ case "$arg" in
32
+ -o) OUT_FILE="${ARGS[$((i+1))]}" ;;
33
+ -w) WRITE_OUT="${ARGS[$((i+1))]}" ;;
34
+ -d) BODY="${ARGS[$((i+1))]}" ;;
35
+ http://*|https://*) URL="$arg" ;;
36
+ esac
37
+ done
38
+ printf 'URL:%s\n' "$URL" >> "$LOG_FILE"
39
+ if [ -n "$BODY" ]; then
40
+ printf 'BODY:%s\n' "$BODY" >> "$LOG_FILE"
41
+ fi
42
+
43
+ status=500
44
+ payload='{"error":"unhandled"}'
45
+ case "$URL" in
46
+ *"/api/ci/releases/resolve"*)
47
+ status="${RESOLVE_STATUS:-200}"
48
+ payload="${RESOLVE_BODY:-{}}"
49
+ ;;
50
+ *"/cycles/start")
51
+ status="${START_STATUS:-201}"
52
+ payload="${START_BODY:-{}}"
53
+ ;;
54
+ *"/cycles/complete")
55
+ status="${COMPLETE_STATUS:-200}"
56
+ payload="${COMPLETE_BODY:-{}}"
57
+ ;;
58
+ *"/cycles/reconcile")
59
+ status="${RECONCILE_STATUS:-200}"
60
+ payload="${RECONCILE_BODY:-{}}"
61
+ ;;
62
+ esac
63
+
64
+ if [ -n "$OUT_FILE" ]; then
65
+ printf '%s' "$payload" > "$OUT_FILE"
66
+ fi
67
+ if [ -n "$WRITE_OUT" ]; then
68
+ printf '%s' "$status"
69
+ fi
70
+ EOF
71
+ chmod +x "$dir/bin/curl"
72
+ }
73
+
74
+ run_helper() {
75
+ PATH="$1/bin:$PATH" \
76
+ EXECUTION_TEST_LOG="$1/curl.log" \
77
+ DEVAUDIT_BASE_URL="https://devaudit.example.test" \
78
+ DEVAUDIT_API_KEY="mc_test_dummy" \
79
+ "$HELPER" "${@:2}"
80
+ }
81
+
82
+ assert_contains() {
83
+ local label="$1" needle="$2" file="$3"
84
+ if grep -Fq -- "$needle" "$file"; then ok "$label"; else no "$label"; fi
85
+ }
86
+
87
+ echo "=== report-test-execution.sh tests ==="
88
+
89
+ case_start_success() {
90
+ echo "case: start resolves exact release and records execution"
91
+ local dir out exit_code
92
+ dir=$(mktemp -d)
93
+ make_fixture "$dir"
94
+ out="$dir/out.env"
95
+ RESOLVE_STATUS=200 \
96
+ RESOLVE_BODY='{"latest":{"id":"release-1","version":"REQ-100"}}' \
97
+ START_STATUS=201 \
98
+ START_BODY='{"id":"execution-1"}' \
99
+ run_helper "$dir" start \
100
+ --project-slug fixture-app \
101
+ --release REQ-100 \
102
+ --sdlc-stage 2 \
103
+ --environment uat \
104
+ --suite-kind e2e \
105
+ --iteration-key rework-1 \
106
+ --iteration-ordinal 1 \
107
+ --idempotency-key github:fixture/repo:feature-e2e.e2e:12345:1:2:REQ-100 \
108
+ --output-file "$out" >"$dir/stdout.log" 2>"$dir/stderr.log" && exit_code=0 || exit_code=$?
109
+ if [ "$exit_code" -eq 0 ]; then ok "exit code 0"; else no "expected exit 0"; fi
110
+ assert_contains "outputs supported=true" "execution_supported=true" "$out"
111
+ assert_contains "outputs execution_record_id" "execution_record_id=execution-1" "$out"
112
+ assert_contains "called start endpoint" "/cycles/start" "$dir/curl.log"
113
+ assert_contains "payload has suite kind" '"suiteKind": "e2e"' "$dir/curl.log"
114
+ assert_contains "payload carries portal transport kind" '"cycleKind": "e2e"' "$dir/curl.log"
115
+ assert_contains "payload has iteration key" '"iterationKey": "rework-1"' "$dir/curl.log"
116
+ rm -rf "$dir"
117
+ }
118
+
119
+ case_complete_reconciles_terminal_conflict() {
120
+ echo "case: complete falls back to reconcile on terminal-outcome conflict"
121
+ local dir out exit_code
122
+ dir=$(mktemp -d)
123
+ make_fixture "$dir"
124
+ out="$dir/out.env"
125
+ RESOLVE_STATUS=200 \
126
+ RESOLVE_BODY='{"latest":{"id":"release-1","version":"REQ-100"}}' \
127
+ COMPLETE_STATUS=400 \
128
+ COMPLETE_BODY='{"error":"Test execution already has a different terminal outcome; use reconcile"}' \
129
+ RECONCILE_STATUS=200 \
130
+ RECONCILE_BODY='{"id":"execution-1"}' \
131
+ run_helper "$dir" complete \
132
+ --project-slug fixture-app \
133
+ --release REQ-100 \
134
+ --sdlc-stage 2 \
135
+ --environment uat \
136
+ --suite-kind e2e \
137
+ --idempotency-key github:fixture/repo:feature-e2e.e2e:12345:1:2:REQ-100 \
138
+ --started-at 2026-07-16T10:00:00Z \
139
+ --outcome failed \
140
+ --output-file "$out" >"$dir/stdout.log" 2>"$dir/stderr.log" && exit_code=0 || exit_code=$?
141
+ if [ "$exit_code" -eq 0 ]; then ok "exit code 0"; else no "expected exit 0"; fi
142
+ assert_contains "called complete endpoint" "/cycles/complete" "$dir/curl.log"
143
+ assert_contains "called reconcile endpoint" "/cycles/reconcile" "$dir/curl.log"
144
+ assert_contains "outputs reconcile endpoint" "execution_endpoint=reconcile" "$out"
145
+ rm -rf "$dir"
146
+ }
147
+
148
+ case_missing_release_fails() {
149
+ echo "case: release resolve failure is not treated as successful fallback"
150
+ local dir out exit_code
151
+ dir=$(mktemp -d)
152
+ make_fixture "$dir"
153
+ out="$dir/out.env"
154
+ RESOLVE_STATUS=404 \
155
+ RESOLVE_BODY='{"error":"missing"}' \
156
+ run_helper "$dir" start \
157
+ --project-slug fixture-app \
158
+ --release REQ-100 \
159
+ --sdlc-stage 2 \
160
+ --environment uat \
161
+ --suite-kind e2e \
162
+ --idempotency-key key \
163
+ --output-file "$out" >"$dir/stdout.log" 2>"$dir/stderr.log" && exit_code=0 || exit_code=$?
164
+ if [ "$exit_code" -ne 0 ]; then ok "exit code non-zero"; else no "expected non-zero"; fi
165
+ if grep -q '/cycles/start' "$dir/curl.log"; then no "unexpected start endpoint call"; else ok "no start endpoint call"; fi
166
+ rm -rf "$dir"
167
+ }
168
+
169
+ case_start_success
170
+ case_complete_reconciles_terminal_conflict
171
+ case_missing_release_fails
172
+
173
+ echo ""
174
+ echo "=== report-test-execution.test.sh: $PASS passed, $FAIL failed ==="
175
+ if [ "$FAIL" -gt 0 ]; then
176
+ exit 1
177
+ fi
@@ -27,6 +27,8 @@
27
27
  # If status is uat_review → idempotent no-op (exit 0, "already submitted").
28
28
  # If status is uat_approved → idempotent no-op (exit 0, "already approved").
29
29
  # Any other status → refuse with an explanatory message.
30
+ # 6. Print the explicit Stage 4 UAT execution command to run after the
31
+ # reviewer has completed UAT. Submission is not UAT execution.
30
32
 
31
33
  set -euo pipefail
32
34
 
@@ -50,6 +52,23 @@ note() { printf ' - %s\n' "$*"; }
50
52
  fail() { printf ' ✗ %s\n' "$*"; FAILED=$((FAILED + 1)); }
51
53
  ok() { printf ' ✓ %s\n' "$*"; }
52
54
 
55
+ print_uat_execution_next_step() {
56
+ local tested_sha="${1:-}"
57
+ [ -n "$tested_sha" ] || tested_sha="$(git rev-parse HEAD 2>/dev/null || true)"
58
+ echo ""
59
+ echo "Next required control: after the reviewer performs UAT, record the Stage 4 execution explicitly."
60
+ echo "Submission only moves the release into review; it does not claim UAT passed."
61
+ echo ""
62
+ echo " ./scripts/record-uat-execution.sh \\"
63
+ echo " --project-slug ${PROJECT_SLUG} \\"
64
+ echo " --release ${RELEASE_VERSION:-$VERSION_PREFIX} \\"
65
+ echo " --outcome passed \\"
66
+ echo " --executor \"<reviewer identity>\" \\"
67
+ echo " --tested-sha ${tested_sha:-<tested sha>} \\"
68
+ echo " --checklist-ref \"<UAT checklist or portal note>\" \\"
69
+ echo " --evidence-ref \"<supporting evidence path or URL>\""
70
+ }
71
+
53
72
  echo "Readiness checks for ${PROJECT_SLUG} ${VERSION_PREFIX}"
54
73
 
55
74
  # 1. Working tree clean
@@ -134,6 +153,7 @@ case "$RELEASE_STATUS" in
134
153
  NEW_STATUS=$(echo "$BODY" | jq -r '.status')
135
154
  echo " Status: draft → ${NEW_STATUS}"
136
155
  echo " Review page: ${BASE_URL}/projects/${PROJECT_SLUG}/releases/${RELEASE_ID}?env=uat"
156
+ print_uat_execution_next_step "$REMOTE_SHA"
137
157
  else
138
158
  echo " Submission failed (HTTP ${HTTP_CODE}): $(echo "$BODY" | jq -r '.error // .')" >&2
139
159
  exit 1
@@ -143,6 +163,7 @@ case "$RELEASE_STATUS" in
143
163
  echo ""
144
164
  echo "Release already submitted for UAT review (status: uat_review). No action taken."
145
165
  echo "Review page: ${BASE_URL}/projects/${PROJECT_SLUG}/releases/${RELEASE_ID}?env=uat"
166
+ print_uat_execution_next_step "$REMOTE_SHA"
146
167
  ;;
147
168
  uat_approved | prod_review | prod_approved | released)
148
169
  echo ""
@@ -42,19 +42,19 @@
42
42
  # 4 submit-for-review, 5 deploy. Forwarded as
43
43
  # `sdlcStage`; unknown to older portals (ignored
44
44
  # server-side, no error).
45
- # --test-cycle <id> Test cycle identifier (typically the CI run
46
- # ID). Forwarded as `testCycleId`; lets the
47
- # portal group evidence by test cycle per
48
- # ISO/IEC/IEEE 29119-3. Optional — older
49
- # portals ignore the field (no error).
45
+ # --test-execution <id> Test execution identifier (typically the CI run
46
+ # ID). Forwarded to the portal transport as
47
+ # `testCycleId` until the upload API field is
48
+ # renamed.
50
49
  # --evidence-scope <scope> Evidence ownership scope: release | stage |
51
- # cycle | approval. Forwarded as
52
- # `evidenceScope`; optional and ignored by
53
- # older portals.
54
- # --test-cycle-record-id <id> First-class portal cycle UUID. Requires
55
- # `--evidence-scope cycle`. Forwarded as
56
- # `testCycleRecordId`; preserved alongside
57
- # legacy `--test-cycle` during dual-write.
50
+ # execution | approval. `execution` is mapped
51
+ # to the portal transport value `cycle` until
52
+ # the upload API field is renamed.
53
+ # --test-execution-record-id <id>
54
+ # First-class portal test execution UUID.
55
+ # Requires `--evidence-scope execution`.
56
+ # Forwarded to the portal transport as
57
+ # `testCycleRecordId`.
58
58
  #
59
59
  # Required environment variables:
60
60
  # DEVAUDIT_BASE_URL e.g. https://meta-comply-production.up.railway.app
@@ -136,9 +136,9 @@ while [ "$#" -gt 0 ]; do
136
136
  # DevAudit-Installer#96.
137
137
  --gate-status) GATE_STATUS="$2"; shift 2 ;;
138
138
  --sdlc-stage) SDLC_STAGE="$2"; shift 2 ;;
139
- --test-cycle) TEST_CYCLE="$2"; shift 2 ;;
139
+ --test-execution) TEST_CYCLE="$2"; shift 2 ;;
140
140
  --evidence-scope) EVIDENCE_SCOPE="$2"; shift 2 ;;
141
- --test-cycle-record-id) TEST_CYCLE_RECORD_ID="$2"; shift 2 ;;
141
+ --test-execution-record-id) TEST_CYCLE_RECORD_ID="$2"; shift 2 ;;
142
142
  # --meta-key key=value (repeatable). Merged into the metadata JSON
143
143
  # before posting. Validates the `key=value` shape; rejects bare
144
144
  # keys without `=`.
@@ -166,14 +166,18 @@ if [ -n "$SDLC_STAGE" ] && ! [[ "$SDLC_STAGE" =~ ^[1-5]$ ]]; then
166
166
  echo "Error: --sdlc-stage must be an integer 1-5 (got: $SDLC_STAGE)"
167
167
  exit 1
168
168
  fi
169
- if [ -n "$EVIDENCE_SCOPE" ] && ! [[ "$EVIDENCE_SCOPE" =~ ^(release|stage|cycle|approval)$ ]]; then
170
- echo "Error: --evidence-scope must be one of: release, stage, cycle, approval"
169
+ if [ -n "$EVIDENCE_SCOPE" ] && ! [[ "$EVIDENCE_SCOPE" =~ ^(release|stage|execution|approval)$ ]]; then
170
+ echo "Error: --evidence-scope must be one of: release, stage, execution, approval"
171
171
  exit 1
172
172
  fi
173
- if [ -n "$TEST_CYCLE_RECORD_ID" ] && [ "$EVIDENCE_SCOPE" != "cycle" ]; then
174
- echo "Error: --test-cycle-record-id requires --evidence-scope cycle"
173
+ if [ -n "$TEST_CYCLE_RECORD_ID" ] && [ "$EVIDENCE_SCOPE" != "execution" ]; then
174
+ echo "Error: --test-execution-record-id requires --evidence-scope execution"
175
175
  exit 1
176
176
  fi
177
+ TRANSPORT_EVIDENCE_SCOPE="$EVIDENCE_SCOPE"
178
+ if [ "$TRANSPORT_EVIDENCE_SCOPE" = "execution" ]; then
179
+ TRANSPORT_EVIDENCE_SCOPE="cycle"
180
+ fi
177
181
 
178
182
  if [ -z "${DEVAUDIT_BASE_URL:-}" ]; then
179
183
  echo "Error: DEVAUDIT_BASE_URL environment variable is required"
@@ -553,7 +557,7 @@ for FILE in "${FILES[@]}"; do
553
557
  [ -n "$GATE_STATUS" ] && CURL_ARGS+=(-F "gateStatus=${GATE_STATUS}")
554
558
  [ -n "$SDLC_STAGE" ] && CURL_ARGS+=(-F "sdlcStage=${SDLC_STAGE}")
555
559
  [ -n "$TEST_CYCLE" ] && CURL_ARGS+=(-F "testCycleId=${TEST_CYCLE}")
556
- [ -n "$EVIDENCE_SCOPE" ] && CURL_ARGS+=(-F "evidenceScope=${EVIDENCE_SCOPE}")
560
+ [ -n "$TRANSPORT_EVIDENCE_SCOPE" ] && CURL_ARGS+=(-F "evidenceScope=${TRANSPORT_EVIDENCE_SCOPE}")
557
561
  [ -n "$TEST_CYCLE_RECORD_ID" ] && CURL_ARGS+=(-F "testCycleRecordId=${TEST_CYCLE_RECORD_ID}")
558
562
  [ -n "$SENTINEL_CONTENT" ] && CURL_ARGS+=(-F "sentinelContent=${SENTINEL_CONTENT}")
559
563
  [ -n "$COMMIT_TIMESTAMP" ] && CURL_ARGS+=(-F "commitTimestamp=${COMMIT_TIMESTAMP}")
@@ -21,11 +21,12 @@ echo "=== Commit Convention Validation ==="
21
21
  echo "Comparing: $BASE_BRANCH...HEAD"
22
22
  echo ""
23
23
 
24
- # Conventional Commit regex: type(optional-scope): description
24
+ # Conventional Commit regex: optional [REQ-XXX] prefix, then
25
+ # type(optional-scope): description.
25
26
  # Scope accepts anything except `)` so multi-scope subjects like
26
27
  # `feat(auth,profile):` and `fix(rewards/expiry):` validate. The closing-paren
27
- # guard prevents pathological inputs. DevAudit-Installer#93.
28
- CC_REGEX='^(feat|fix|docs|test|refactor|chore|compliance|security|perf|ci|build|revert)(\([^)]+\))?!?: .+'
28
+ # guard prevents pathological inputs. DevAudit-Installer#93/#440.
29
+ CC_REGEX='^(\[REQ-[0-9]{3,}\][[:space:]]+)?(feat|fix|docs|test|refactor|chore|compliance|security|perf|ci|build|revert)(\([^)]+\))?!?: .+'
29
30
 
30
31
  COMMITS=$(git log "$BASE_BRANCH"..HEAD --format='%H' || true)
31
32
 
@@ -78,7 +79,8 @@ while IFS= read -r sha; do
78
79
  # are exempt. Mirrors the commitlint rule; this is the PR-CI half that
79
80
  # `--no-verify` can't skip. Work starts from a requirement (which starts
80
81
  # from an issue) — use the sdlc-implementer skill to assign one.
81
- TYPE=$(echo "$SUBJECT" | grep -oE '^[a-z]+' || true)
82
+ SUBJECT_FOR_TYPE=$(echo "$SUBJECT" | sed -E 's/^\[REQ-[0-9]{3,}\][[:space:]]+//')
83
+ TYPE=$(echo "$SUBJECT_FOR_TYPE" | grep -oE '^[a-z]+' || true)
82
84
  case "$TYPE" in
83
85
  feat|fix|refactor|perf)
84
86
  if ! echo "$SUBJECT" | grep -qP '\[REQ-\d{3,}\]' \
@@ -138,6 +138,21 @@ run_validator
138
138
  assert_exit "no active context exits 1" 1
139
139
  assert_grep "hard error emitted without any active release context" "ERROR .*implementation commit but cites no requirement" 1
140
140
 
141
+ # Case 5: leading REQ prefix is accepted before the Conventional Commit type.
142
+ echo "Case 5: leading REQ prefix before type is accepted"
143
+ make_fixture "$WORKDIR/case5" "[REQ-123] fix(reports): map dynamic category reports" "Co-Authored-By: Test <test@example.com>"
144
+ run_validator
145
+ assert_exit "leading REQ prefix exits 0" 0
146
+ assert_grep "no conventional-commit error for leading REQ prefix" "Not Conventional Commits format" 0
147
+ assert_grep "no missing-requirement error for leading REQ prefix" "implementation commit but cites no requirement" 0
148
+
149
+ # Case 6: malformed leading REQ prefixes remain invalid.
150
+ echo "Case 6: malformed leading REQ prefix is rejected"
151
+ make_fixture "$WORKDIR/case6" "[REQ-12] fix: invalid short req prefix" "Co-Authored-By: Test <test@example.com>"
152
+ run_validator
153
+ assert_exit "malformed leading REQ prefix exits 1" 1
154
+ assert_grep "malformed prefix is not treated as conventional" "Not Conventional Commits format" 1
155
+
141
156
  echo
142
157
  echo "Result: $PASS passed, $FAIL failed"
143
158
  [ "$FAIL" = "0" ]
@@ -229,9 +229,9 @@ Before running the suite (Phase 6), verify that the evidence traceability wiring
229
229
 
230
230
  Also verify the import is present: `import { tagTest } from './helpers/test-tags'` (or the correct relative path from the spec's location to `e2e/helpers/test-tags.ts`).
231
231
 
232
- 4. **Check release and cycle provenance.** Before handing the work back to `sdlc-implementer`, identify the tracked `REQ-XXX`, the source branch SHA, and the expected SDLC stage/cycle for the run. The CI evidence upload must retain those values so the portal can show the result in the correct numbered cycle for the release that produced it.
232
+ 4. **Check release and execution provenance.** Before handing the work back to `sdlc-implementer`, identify the tracked `REQ-XXX`, the source branch SHA, and the expected SDLC stage/test execution for the run. The CI evidence upload must retain those values so the portal can show the result in the correct numbered execution for the release that produced it.
233
233
  - A passing local run is not portal evidence by itself. Report it as local verification and let the configured CI workflow upload the resulting report and screenshots.
234
- - For a bundled release, evidence remains attributed to its originating REQ and cycle. The absorbing release may reference that source release through bundle lineage, but must not relabel the source E2E run as newly produced by the absorbing REQ.
234
+ - For a bundled release, evidence remains attributed to its originating REQ and execution. The absorbing release may reference that source release through bundle lineage, but must not relabel the source E2E run as newly produced by the absorbing REQ.
235
235
  - Confirm the CI invocation has an unambiguous release context (`REQ-XXX` or the documented standalone-housekeeping declaration). A bare-date integration housekeeping run is historical CI only; it must not create an approval-ready E2E evidence set.
236
236
  - If the project workflow cannot provide this context, halt and report a workflow-contract gap. Do not claim that screenshots or E2E results will appear correctly on the portal.
237
237
 
@@ -247,7 +247,7 @@ Halt and report the gap to the user:
247
247
 
248
248
  Do **not** proceed to Phase 6 until all gaps are resolved. The user may choose to skip an AC (e.g. API-only, transport-only) — that's valid, but it must be an explicit decision recorded in the test-execution-summary, not an omission.
249
249
 
250
- **Write the evidence-wiring sentinel (devaudit-installer#226).** After all Phase 5½ checks pass (all `@requirement`, `evidenceShot()`, `tagTest()`, and release/cycle provenance checks verified), write a `.e2e-evidence-wired` sentinel file in the repo root so the pre-push hook and `sdlc-implementer` Phase 2 step 5b can verify evidence wiring was validated:
250
+ **Write the evidence-wiring sentinel (devaudit-installer#226).** After all Phase 5½ checks pass (all `@requirement`, `evidenceShot()`, `tagTest()`, and release/execution provenance checks verified), write a `.e2e-evidence-wired` sentinel file in the repo root so the pre-push hook and `sdlc-implementer` Phase 2 step 5b can verify evidence wiring was validated:
251
251
 
252
252
  ```bash
253
253
  echo "WIRED $(date -u +%Y-%m-%dT%H:%M:%SZ) REQ-XXX" > .e2e-evidence-wired
@@ -366,7 +366,7 @@ Each filed issue needs:
366
366
 
367
367
  #### Framework classification + the `incident` label
368
368
 
369
- Every defect filed from Phase 6 becomes `incident_report` evidence when (a) the issue is labelled `incident` and (b) the issue is closed. The flow: closed-with-label → `incident-export.yml` exports the body to `compliance/governance/incident-report-<N>.md` → `compliance-evidence.yml` uploads as `incident_report` evidence → portal flips the attributed clauses MISSING → COVERED.
369
+ Every defect filed from Phase 6 becomes `incident_report` evidence when (a) the issue is labelled `incident` and (b) the issue is closed. The flow: closed-with-label → `incident-export.yml` exports the body to `compliance/governance/incident-report-<N>.md` with `incident_kind`, `source_release`, `source_issue`, and `semantic_id` frontmatter → `compliance-evidence.yml` uploads it as source-owned `incident_report` evidence → portal flips the attributed clauses MISSING → COVERED for that source release.
370
370
 
371
371
  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:
372
372
 
@@ -411,6 +411,8 @@ Once closed, the `incident-export.yml` workflow exports this issue's body to `co
411
411
  - **Path B (any of SOC2/GDPR/EUAIA ticked):** auto-files a PR with the GDPR triage + sign-off sections to fill in. Merge that PR → `compliance-evidence.yml` uploads as `incident_report`.
412
412
  ```
413
413
 
414
+ The issue body must identify the owning `REQ-XXX` or standalone housekeeping release so the exported report's `source_release` is correct. Do not let a later bundled release claim the incident as newly produced evidence; bundles inherit by explicit lineage.
415
+
414
416
  Pre-tick boxes you're confident about. Leave the operator-judgement ones (GDPR triage, AI-failure classification) for the operator to confirm in the export PR.
415
417
 
416
418
  #### Worked examples
@@ -448,7 +450,7 @@ Wrap up with a summary the user can drop into the PR or ticket:
448
450
  - Defects filed — count, with links.
449
451
  - Requirements gap reports — count, with details (devaudit-installer#212 Gap 4). These are ACs classified as "impossible to test" or "missing AC" — returned to `sdlc-implementer` for the requirements gap flow, not filed as defects.
450
452
  - Missed requirements — count, with links.
451
- - **Release provenance** — originating `REQ-XXX`, local run result, expected CI workflow/run, and expected portal stage/cycle. For bundled work, state the source REQ explicitly and that the absorbing release will reference it through lineage rather than overwrite its attribution.
453
+ - **Release provenance** — originating `REQ-XXX`, local run result, expected CI workflow/run, and expected portal stage/execution. For bundled work, state the source REQ explicitly and that the absorbing release will reference it through lineage rather than overwrite its attribution.
452
454
 
453
455
  **Then feed the test-design record (devaudit#50).** The Stage 3 `test-execution-summary.md` (generated per `3-compile-evidence.md` Step 1a) carries a `## Test design` section at the top. Before Stage 3 finalises the file, populate that section with the design-time decisions this skill made, so the SDLC has a recorded trace that scope was *decided*, not implicit:
454
456
 
@@ -496,8 +498,8 @@ The canonical helper source lives at `references/evidence.ts` in this skill.
496
498
 
497
499
  The number of `evidenceShot` calls per spec should scale to the spec's role:
498
500
 
499
- - **While the spec is a feature artefact** (newly authored on the branch, before merge to develop): capture multiple stages every meaningful transition or state the AC documents. The dense evidence is what reviewers use to verify the AC was met end-to-end during the feature cycle.
500
- - **Once the spec joins the regression pack** (post-merge, `git diff --diff-filter=A` no longer matches it): capture only the canonical "this still works" anchor per AC. Re-running the dense journey on every regression cycle is noise and inflates CI artefact storage with little signal.
501
+ - **While the spec is a feature artefact** (newly authored on the branch, before merge to develop): capture multiple stages - every meaningful transition or state the AC documents. The dense evidence is what reviewers use to verify the AC was met end-to-end during the feature iteration.
502
+ - **Once the spec joins the regression pack** (post-merge, `git diff --diff-filter=A` no longer matches it): capture only the canonical "this still works" anchor per AC. Re-running the dense journey on every regression execution is noise and inflates CI artefact storage with little signal.
501
503
 
502
504
  The `EvidenceShotOrigin` signal (`'feature' | 'regression'`) auto-detects from `E2E_NEW_SPECS`. Mark stage screenshots with `{ tier: 'feature' }`; the helper auto-suppresses them on regression runs. The canonical anchor uses the default tier (`'always'`).
503
505
 
@@ -49,7 +49,10 @@ jobs:
49
49
  if: >-
50
50
  github.event_name != 'deployment_status' ||
51
51
  (github.event.deployment_status.state == 'success' &&
52
- (github.event.deployment.environment == 'production' || github.event.deployment.environment == 'prod'))
52
+ (github.event.deployment.environment == 'production' ||
53
+ github.event.deployment.environment == 'prod' ||
54
+ endsWith(github.event.deployment.environment, '/ production') ||
55
+ endsWith(github.event.deployment.environment, '/production')))
53
56
  runs-on: ubuntu-latest # adapt to your runner; e.g. self-hosted, ubuntu-24.04
54
57
  # The full regression target is about 35 minutes. Leave time to archive
55
58
  # partial output before GitHub terminates the job.
@@ -1,12 +1,12 @@
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", "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.
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 execution; 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 execution — 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
 
7
7
  # SDLC implementer
8
8
 
9
- Take a single GitHub issue end-to-end through the Metasession SDLC. The skill **triages first** (Phase 0): it classifies the change, announces the path it will take, and routes — only a **tracked** change continues into the full cycle, while housekeeping, trivial, and compliance-doc-only work is driven down its lighter path **to completion** (the skill still guides every step to merge; it just skips the tracked ceremony). For a tracked change, one command runs Phase 1 through Phase 4 unattended (with a plan-approval pause for HIGH/CRITICAL risk); the human enters the loop at the UAT review gate on the portal. On resume, the skill runs Phase 5 — merge, post-deploy smoke evidence, mark the release Released, or address change-requests and re-submit for UAT re-review.
9
+ Take a single GitHub issue end-to-end through the Metasession SDLC. The skill **triages first** (Phase 0): it classifies the change, announces the path it will take, and routes — only a **tracked** change continues into the full execution, while housekeeping, trivial, and compliance-doc-only work is driven down its lighter path **to completion** (the skill still guides every step to merge; it just skips the tracked ceremony). For a tracked change, one command runs Phase 1 through Phase 4 unattended (with a plan-approval pause for HIGH/CRITICAL risk); the human enters the loop at the UAT review gate on the portal. On resume, the skill runs Phase 5 — merge, post-deploy smoke evidence, mark the release Released, or address change-requests and re-submit for UAT re-review.
10
10
 
11
11
  This skill is a single entry point that **routes**, not one that always runs heavy. The change-type taxonomy it routes against is the canonical table in [`change-workflows.md`](https://github.com/metasession-dev/DevAudit-Installer/blob/main/docs/change-workflows.md) (six change-types → commit-type → requirement? → path).
12
12
 
@@ -105,7 +105,7 @@ This gate is distinct from Phase 2 step 4's "any deviation from the plan must be
105
105
 
106
106
  ## Requirements gap flow (devaudit-installer#212)
107
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:
108
+ A **requirements gap** is "the ACs themselves were wrong, incomplete, or missing" — discovered by the skill or sub-skills mid-execution, not by a user request. This is distinct from:
109
109
 
110
110
  - **Scope expansion** (user asks for something beyond the ACs → scope-expansion halt gate above)
111
111
  - **Defect** (implementation doesn't match the ACs → incident filing)
@@ -146,7 +146,7 @@ Amendment steps:
146
146
  8. If Phase 3+: re-compile affected evidence
147
147
  9. Continue from current phase
148
148
 
149
- **(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
+ **(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 execution. Continue with the current REQ unchanged.
150
150
 
151
151
  ### Distinction from scope-expansion halt gate
152
152
 
@@ -324,7 +324,7 @@ _Workflow tweak (CI artifact upload, gate timeout bump, etc.)_
324
324
 
325
325
  ### Lightweight path (housekeeping / trivial / compliance-doc-only)
326
326
 
327
- Reached from Phase 0 for non-tracked change-types. The skill drives this end-to-end; the only difference from the tracked cycle is the absence of _ceremony_, not the absence of _guidance_. It pauses only where a human is genuinely required (PR review, merge).
327
+ Reached from Phase 0 for non-tracked change-types. The skill drives this end-to-end; the only difference from the tracked execution is the absence of _ceremony_, not the absence of _guidance_. It pauses only where a human is genuinely required (PR review, merge).
328
328
 
329
329
  **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.
330
330
 
@@ -499,9 +499,10 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
499
499
 
500
500
  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:
501
501
  - Create `compliance/governance/nil-incident-report-<version>.md` from the `nil-incident-report.md.template` template.
502
+ - Set frontmatter `incident_kind: "no_incidents_attestation"`, `source_release: "<version>"`, and `semantic_id: "NIL-<version>"`. The `source_release` must be the REQ or standalone housekeeping release that actually owns the attestation.
502
503
  - Fill in the scope section (test cases executed/passed/failed, defects filed, incidents reported).
503
504
  - Leave the sign-off section with REPLACE markers — the operator fills it in.
504
- - 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.
505
+ - The nil report uploads as source-owned `incident_report` evidence via `compliance-evidence.yml`'s `upload_incident_report` function, flipping `ISO29119.3.5.4` to COVERED for clean releases. Historical nil reports are not re-uploaded to later REQs unless an explicit bundle manifest references the source release; even then ownership remains on the source release.
505
506
  - If incidents WERE closed, skip nil report generation — the populated incident report(s) from `incident-export.yml` serve as the evidence.
506
507
 
507
508
  5b. **Validate test-execution-summary.md gate states (devaudit-installer#240).** Before uploading evidence, verify that `compliance/evidence/REQ-XXX/test-execution-summary.md` does not contain invalid gate states. Run:
@@ -512,22 +513,22 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
512
513
 
513
514
  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.
514
515
 
515
- 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:
516
+ 5b.1. **Render the Test Executions table from the portal's execution read model when available (devaudit-installer#394).** Do not hand-build execution rows from uploaded artefacts if the portal exposes first-class test execution records. Query the release-journey / execution API, save the JSON, and render the markdown table with:
516
517
 
517
518
  ```bash
518
- bash scripts/render-test-cycles.sh /tmp/release-journey.json
519
+ bash scripts/render-test-executions.sh /tmp/release-journey.json
519
520
  ```
520
521
 
521
522
  First-class rows must preserve:
522
523
  - source release
523
524
  - SDLC stage
524
- - cycle ordinal within that source release + stage
525
- - cycle kind / outcome
525
+ - execution ordinal within that source release + stage
526
+ - execution kind / outcome
526
527
  - workflow or run link
527
528
  - related evidence
528
529
  - incident or remediation reference
529
530
 
530
- 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.
531
+ If the portal does not expose first-class test executions, do not infer executions from uploaded artefact groups. Record the gap in the summary and keep the release unresolved until the execution read model is available.
531
532
 
532
533
  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:
533
534
 
@@ -634,7 +635,7 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
634
635
  **Solo-operator teams.** On a one-person team, the literal "reviewer ≠ submitter" check is structurally unsatisfiable. The supported interpretation is _actor type, not human identity_ — AI tooling (the skill-trigger) and the human operator (the portal-approver) are distinct actors. Document this on the release ticket under `## Sign-off (dual-actor)` with the explicit interpretation, and ensure the human operator has independently reviewed the diff before clicking _Approve Production_ in the portal. Without this attestation the four-eyes claim is performative.
635
636
 
636
637
  3. **Apply labels** — `awaiting-uat-review`, `risk:<class>`.
637
- 4. **Comment on the issue**: "Implementation complete. PR #M opened. Evidence on portal: <link>. UAT review requested. Resume with `resume REQ-XXX` once UAT approval is granted on the portal."
638
+ 4. **Comment on the issue**: "Implementation complete. PR #M opened. Evidence on portal: <link>. UAT review requested. Reviewer action — perform UAT, record it with `scripts/record-uat-execution.sh`, then approve or request changes on the portal. Resume with `resume REQ-XXX` once UAT approval is granted on the portal."
638
639
  5. **Inspect PR blockers before handoff (devaudit-installer#304).** Run `gh pr view <N> --json mergeStateStatus,reviewDecision,statusCheckRollup,url` and categorize the state:
639
640
  - `reviewDecision` not approved → human approval blocker; hand off to the reviewer.
640
641
  - Release Approval Gate failing while portal state is not `uat_approved` → portal approval blocker; hand off to portal UAT reviewer.
@@ -658,7 +659,8 @@ Reached only on the **tracked** route from Phase 0 (the issue is already fetched
658
659
  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.
659
660
 
660
661
  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.
661
- 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.
662
+ 7. **Require a separate UAT execution record.** `scripts/submit-for-uat-review.sh` is a queue transition only. The reviewer must run `scripts/record-uat-execution.sh --project-slug <slug> --release REQ-XXX --outcome passed|failed --executor "<identity>" --tested-sha <sha> --checklist-ref "<ref>" --evidence-ref "<ref>"` after performing UAT and before portal approval. A `failed` execution must include `--remediation-ref <issue-or-pr>` and returns to the change-request loop. The skill must not create a passing UAT execution on behalf of the reviewer.
663
+ 8. **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>, perform UAT, record Stage 4 UAT execution, then approve or request changes 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.
662
664
 
663
665
  **When an external gate hangs or fails for unrelated reasons.** A required gate may fail for reasons outside the change's scope — flaky infra, an unrelated regression test that hangs at hour-plus runtime with no log activity, a known-failing suite. When this happens:
664
666
 
@@ -706,7 +708,7 @@ Invoked separately by the user after UAT activity on the portal. Trigger: "resum
706
708
  These steps are mandatory — "PR merged" or "release marked Released" is not complete until the automated close-out PR has merged or a documented fallback reconciliation is complete. Without it, CI can keep uploading evidence against a completed REQ and pollute the audit trail.
707
709
  - **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; automated close-out reconciled RTM and release ticket" "Done — close issue + retire feature branch (sdlc-implementer halts)"`.
708
710
  - Close the issue only after close-out reconciliation completes.
709
- - 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"`.
711
+ - 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, testExecutionId (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"`.
710
712
 
711
713
  - **Changes requested** → run change-request loop:
712
714
  - Fetch change-request comments from the PR (`gh pr view <M> --comments`) and from the portal release page.