@metasession.co/devaudit-cli 0.3.12 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +53 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +55 -1
- package/sdlc/files/_common/0-project-setup.md +10 -4
- package/sdlc/files/_common/Periodic_Security_Review_Schedule.md +5 -5
- package/sdlc/files/_common/README_TEMPLATE.md +8 -8
- package/sdlc/files/_common/Test_Architecture.md +15 -15
- package/sdlc/files/_common/Test_Plan_TEMPLATE.md +13 -13
- package/sdlc/files/_common/Test_Policy.md +2 -2
- package/sdlc/files/_common/Test_Strategy.md +10 -10
- package/sdlc/files/_common/governance/ai-disclosure.md.template +3 -3
- package/sdlc/files/_common/governance/dpia.md.template +3 -3
- package/sdlc/files/_common/governance/incident-report.md.template +3 -3
- package/sdlc/files/_common/governance/nil-incident-report.md.template +1 -1
- package/sdlc/files/_common/governance/periodic-review.md.template +3 -3
- package/sdlc/files/_common/governance/risk-register.md.template +1 -1
- package/sdlc/files/_common/governance/ropa.md.template +2 -2
- package/sdlc/files/_common/joining-an-existing-project.md +4 -4
- package/sdlc/files/_common/scripts/check-host-deployment.sh +107 -0
- package/sdlc/files/_common/scripts/check-host-deployment.test.sh +119 -0
- package/sdlc/files/_common/scripts/check-release-pr-scope.sh +95 -0
- package/sdlc/files/_common/scripts/check-release-pr-scope.test.sh +137 -0
- package/sdlc/files/_common/scripts/close-out-release.sh +175 -54
- package/sdlc/files/_common/scripts/close-out-release.test.sh +90 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.sh +27 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.test.sh +13 -1
- package/sdlc/files/_common/scripts/generate-bundled-changes.sh +302 -64
- package/sdlc/files/_common/scripts/generate-bundled-changes.test.sh +128 -82
- package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.sh +18 -9
- package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.test.sh +66 -0
- package/sdlc/files/_common/scripts/generate-security-summary.sh +14 -5
- package/sdlc/files/_common/scripts/generate-security-summary.test.sh +57 -0
- package/sdlc/files/_common/scripts/prepare-release-pr.sh +44 -4
- package/sdlc/files/_common/scripts/render-test-cycles.sh +227 -0
- package/sdlc/files/_common/scripts/render-test-cycles.test.sh +107 -0
- package/sdlc/files/_common/scripts/report-test-cycle.sh +344 -0
- package/sdlc/files/_common/scripts/submit-bundle-manifest.sh +100 -0
- package/sdlc/files/_common/scripts/submit-bundle-manifest.test.sh +138 -0
- package/sdlc/files/_common/scripts/upload-evidence.sh +24 -0
- package/sdlc/files/_common/scripts/validate-compliance-artifacts.sh +16 -0
- package/sdlc/files/_common/scripts/validate-compliance-artifacts.test.sh +14 -0
- package/sdlc/files/_common/scripts/validate-test-summary.test.sh +4 -4
- package/sdlc/files/_common/skills/adr-author/SKILL.md +2 -2
- package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +2 -2
- package/sdlc/files/_common/skills/e2e-test-engineer/references/bootstrap.md +5 -5
- package/sdlc/files/_common/skills/governance-doc-author/references/incident-classification.md +1 -1
- package/sdlc/files/_common/skills/requirements-aligner/SKILL.md +4 -4
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +21 -2
- package/sdlc/files/_common/skills/sdlc-implementer/references/change-request-loop.md +1 -1
- package/sdlc/files/_common/skills/sdlc-implementer/references/compliance-constraints.md +7 -14
- package/sdlc/files/ci/ci.yml.template +82 -28
- package/sdlc/files/ci/close-out-release.yml.template +7 -4
- package/sdlc/files/ci/compliance-evidence.yml.template +204 -20
- package/sdlc/files/ci/feature-e2e.yml.template +84 -1
- package/sdlc/files/ci/post-deploy-prod.yml.template +59 -4
- package/sdlc/files/ci/python/ci.yml.template +60 -12
- package/sdlc/files/ci/quality-gates-provenance.yml.template +123 -28
- package/sdlc/package.json +1 -1
- package/sdlc/src/bin/devaudit-sdlc.js +44 -2
- package/sdlc/src/blueprints/1-plan-requirement.raw.md +6 -1
- package/sdlc/src/blueprints/2-implement-and-test.raw.md +1 -1
- package/sdlc/src/blueprints/3-compile-evidence.raw.md +34 -20
- package/sdlc/src/blueprints/4-submit-for-review.raw.md +2 -2
- package/sdlc/src/blueprints/5-deploy-main.raw.md +2 -2
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# submit-bundle-manifest.sh — resolve a release row, then POST its bundle manifest.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# ./scripts/submit-bundle-manifest.sh <project-slug> <release-version> <manifest-path> [--reconcile-existing-ownership]
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
PROJECT_SLUG="${1:-}"
|
|
10
|
+
RELEASE_VERSION="${2:-}"
|
|
11
|
+
MANIFEST_PATH="${3:-}"
|
|
12
|
+
shift $(( $# >= 3 ? 3 : $# ))
|
|
13
|
+
|
|
14
|
+
RECONCILE_EXISTING_OWNERSHIP=false
|
|
15
|
+
while [ "$#" -gt 0 ]; do
|
|
16
|
+
case "$1" in
|
|
17
|
+
--reconcile-existing-ownership)
|
|
18
|
+
RECONCILE_EXISTING_OWNERSHIP=true
|
|
19
|
+
shift
|
|
20
|
+
;;
|
|
21
|
+
*)
|
|
22
|
+
echo "Unknown argument: $1" >&2
|
|
23
|
+
exit 2
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
if [ -z "$PROJECT_SLUG" ] || [ -z "$RELEASE_VERSION" ] || [ -z "$MANIFEST_PATH" ]; then
|
|
29
|
+
echo "Usage: $0 <project-slug> <release-version> <manifest-path> [--reconcile-existing-ownership]" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
: "${DEVAUDIT_BASE_URL:?DEVAUDIT_BASE_URL must be set}"
|
|
34
|
+
: "${DEVAUDIT_API_KEY:?DEVAUDIT_API_KEY must be set}"
|
|
35
|
+
|
|
36
|
+
if [ ! -f "$MANIFEST_PATH" ]; then
|
|
37
|
+
echo "Error: manifest file not found: $MANIFEST_PATH" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
42
|
+
echo "Error: jq is required." >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
BASE_URL="${DEVAUDIT_BASE_URL%/}"
|
|
47
|
+
|
|
48
|
+
SCHEMA_VERSION="$(jq -r '.schemaVersion // empty' "$MANIFEST_PATH")"
|
|
49
|
+
if [ "$SCHEMA_VERSION" != "1" ]; then
|
|
50
|
+
echo "Error: manifest schemaVersion must be 1." >&2
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
MEMBER_COUNT="$(jq -r '(.members // []) | length' "$MANIFEST_PATH")"
|
|
55
|
+
WORK_ITEM_COUNT="$(jq -r '(.nonReleaseWorkItems // []) | length' "$MANIFEST_PATH")"
|
|
56
|
+
if [ "$MEMBER_COUNT" = "0" ] && [ "$WORK_ITEM_COUNT" = "0" ]; then
|
|
57
|
+
echo "Bundle manifest has no members or non-release work items; skipping submission."
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
RESOLVE_URL="${BASE_URL}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${RELEASE_VERSION}"
|
|
62
|
+
RESOLVE_RESPONSE="$(curl -sS -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" "$RESOLVE_URL")"
|
|
63
|
+
RELEASE_ID="$(printf '%s' "$RESOLVE_RESPONSE" | jq -r '.latest.id // empty')"
|
|
64
|
+
RESOLVED_VERSION="$(printf '%s' "$RESOLVE_RESPONSE" | jq -r '.latest.version // empty')"
|
|
65
|
+
|
|
66
|
+
if [ -z "$RELEASE_ID" ]; then
|
|
67
|
+
echo "Error: could not resolve release ${RELEASE_VERSION} for project ${PROJECT_SLUG}." >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
if [ "$RESOLVED_VERSION" != "$RELEASE_VERSION" ]; then
|
|
71
|
+
echo "Error: resolve endpoint returned ${RESOLVED_VERSION} instead of exact release ${RELEASE_VERSION}." >&2
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
PAYLOAD="$(cat "$MANIFEST_PATH")"
|
|
76
|
+
if [ "$RECONCILE_EXISTING_OWNERSHIP" = "true" ]; then
|
|
77
|
+
PAYLOAD="$(jq '. + { reconcileExistingOwnership: true }' "$MANIFEST_PATH")"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
POST_URL="${BASE_URL}/api/ci/releases/${RELEASE_ID}/bundle-manifest"
|
|
81
|
+
HTTP_BODY_FILE="$(mktemp)"
|
|
82
|
+
trap 'rm -f "$HTTP_BODY_FILE"' EXIT
|
|
83
|
+
HTTP_CODE="$(
|
|
84
|
+
curl -sS -o "$HTTP_BODY_FILE" -w "%{http_code}" \
|
|
85
|
+
-X POST \
|
|
86
|
+
-H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
|
|
87
|
+
-H "Content-Type: application/json" \
|
|
88
|
+
--data "$PAYLOAD" \
|
|
89
|
+
"$POST_URL"
|
|
90
|
+
)"
|
|
91
|
+
|
|
92
|
+
if [ "$HTTP_CODE" != "201" ]; then
|
|
93
|
+
echo "Error: bundle manifest submission failed with HTTP ${HTTP_CODE}." >&2
|
|
94
|
+
sed 's/^/ /' "$HTTP_BODY_FILE" >&2
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
echo "Submitted bundle manifest for ${RELEASE_VERSION} to release ${RELEASE_ID}."
|
|
99
|
+
echo " members: ${MEMBER_COUNT}"
|
|
100
|
+
echo " non-release work items: ${WORK_ITEM_COUNT}"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# submit-bundle-manifest.test.sh — Focused tests for bundle manifest submission.
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
HELPER="$SCRIPT_DIR/submit-bundle-manifest.sh"
|
|
8
|
+
[ -x "$HELPER" ] || chmod +x "$HELPER"
|
|
9
|
+
|
|
10
|
+
PASS=0
|
|
11
|
+
FAIL=0
|
|
12
|
+
WORK="$(mktemp -d)"
|
|
13
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
14
|
+
|
|
15
|
+
assert_contains() {
|
|
16
|
+
local desc="$1" needle="$2" file="$3"
|
|
17
|
+
if grep -Fq "$needle" "$file"; then
|
|
18
|
+
echo " PASS: $desc"
|
|
19
|
+
PASS=$((PASS + 1))
|
|
20
|
+
else
|
|
21
|
+
echo " FAIL: $desc"
|
|
22
|
+
echo " missing: $needle"
|
|
23
|
+
echo " file:"
|
|
24
|
+
sed 's/^/ /' "$file"
|
|
25
|
+
FAIL=$((FAIL + 1))
|
|
26
|
+
fi
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
make_fixture() {
|
|
30
|
+
local dir="$1"
|
|
31
|
+
rm -rf "$dir"
|
|
32
|
+
mkdir -p "$dir/bin"
|
|
33
|
+
cd "$dir"
|
|
34
|
+
cat > bin/curl <<'EOF'
|
|
35
|
+
#!/usr/bin/env bash
|
|
36
|
+
set -euo pipefail
|
|
37
|
+
LOG_FILE="${SUBMIT_TEST_LOG:?}"
|
|
38
|
+
URL=""
|
|
39
|
+
BODY_FILE=""
|
|
40
|
+
WRITE_OUT=""
|
|
41
|
+
METHOD="GET"
|
|
42
|
+
BODY=""
|
|
43
|
+
ARGS=("$@")
|
|
44
|
+
for ((i=0; i<${#ARGS[@]}; i++)); do
|
|
45
|
+
arg="${ARGS[$i]}"
|
|
46
|
+
case "$arg" in
|
|
47
|
+
-X) METHOD="${ARGS[$((i+1))]}" ;;
|
|
48
|
+
-o) BODY_FILE="${ARGS[$((i+1))]}" ;;
|
|
49
|
+
-w) WRITE_OUT="${ARGS[$((i+1))]}" ;;
|
|
50
|
+
--data) BODY="${ARGS[$((i+1))]}" ;;
|
|
51
|
+
http://*|https://*) URL="$arg" ;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
echo "METHOD:${METHOD}" >> "$LOG_FILE"
|
|
55
|
+
echo "URL:${URL}" >> "$LOG_FILE"
|
|
56
|
+
if [ -n "$BODY" ]; then
|
|
57
|
+
echo "BODY:${BODY}" >> "$LOG_FILE"
|
|
58
|
+
fi
|
|
59
|
+
if [[ "$URL" == *"/api/ci/releases/resolve"* ]]; then
|
|
60
|
+
printf '{"latest":{"id":"rel_123","version":"REQ-042","status":"draft"}}'
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
if [[ "$URL" == *"/api/ci/releases/rel_123/bundle-manifest" ]]; then
|
|
64
|
+
if [ -n "$BODY_FILE" ]; then
|
|
65
|
+
printf '{"ok":true}' > "$BODY_FILE"
|
|
66
|
+
fi
|
|
67
|
+
if [ -n "$WRITE_OUT" ]; then
|
|
68
|
+
printf '201'
|
|
69
|
+
fi
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
printf '{"error":"unexpected url"}'
|
|
73
|
+
exit 1
|
|
74
|
+
EOF
|
|
75
|
+
chmod +x bin/curl
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
echo "=== submit-bundle-manifest.sh tests ==="
|
|
79
|
+
|
|
80
|
+
D1="$WORK/case1"
|
|
81
|
+
make_fixture "$D1"
|
|
82
|
+
cat > "$D1/manifest.json" <<'EOF'
|
|
83
|
+
{
|
|
84
|
+
"schemaVersion": 1,
|
|
85
|
+
"approvalRelease": { "version": "REQ-042" },
|
|
86
|
+
"coreRelease": { "version": "REQ-042" },
|
|
87
|
+
"members": [
|
|
88
|
+
{
|
|
89
|
+
"version": "REQ-041",
|
|
90
|
+
"role": "predecessor",
|
|
91
|
+
"relationship": "superseded"
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"nonReleaseWorkItems": [
|
|
95
|
+
{
|
|
96
|
+
"kind": "housekeeping_commit",
|
|
97
|
+
"title": "docs: refresh release notes"
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"manifestHash": "sha256:test"
|
|
101
|
+
}
|
|
102
|
+
EOF
|
|
103
|
+
LOG_FILE="$D1/submit.log"
|
|
104
|
+
touch "$LOG_FILE"
|
|
105
|
+
export PATH="$D1/bin:$PATH"
|
|
106
|
+
export SUBMIT_TEST_LOG="$LOG_FILE"
|
|
107
|
+
export DEVAUDIT_BASE_URL="https://devaudit.example.test"
|
|
108
|
+
export DEVAUDIT_API_KEY="mc_test_dummy"
|
|
109
|
+
|
|
110
|
+
bash "$HELPER" fixture-project REQ-042 "$D1/manifest.json" > "$D1/stdout.log" 2>&1
|
|
111
|
+
assert_contains "resolve endpoint called" "/api/ci/releases/resolve?projectSlug=fixture-project&versionPrefix=REQ-042" "$LOG_FILE"
|
|
112
|
+
assert_contains "bundle endpoint called" "/api/ci/releases/rel_123/bundle-manifest" "$LOG_FILE"
|
|
113
|
+
assert_contains "member payload submitted" "\"version\": \"REQ-041\"" "$LOG_FILE"
|
|
114
|
+
|
|
115
|
+
D2="$WORK/case2"
|
|
116
|
+
make_fixture "$D2"
|
|
117
|
+
cat > "$D2/manifest.json" <<'EOF'
|
|
118
|
+
{
|
|
119
|
+
"schemaVersion": 1,
|
|
120
|
+
"approvalRelease": { "version": "REQ-042" },
|
|
121
|
+
"coreRelease": { "version": "REQ-042" },
|
|
122
|
+
"members": [],
|
|
123
|
+
"nonReleaseWorkItems": []
|
|
124
|
+
}
|
|
125
|
+
EOF
|
|
126
|
+
LOG_FILE="$D2/submit.log"
|
|
127
|
+
touch "$LOG_FILE"
|
|
128
|
+
export PATH="$D2/bin:$PATH"
|
|
129
|
+
export SUBMIT_TEST_LOG="$LOG_FILE"
|
|
130
|
+
|
|
131
|
+
bash "$HELPER" fixture-project REQ-042 "$D2/manifest.json" > "$D2/stdout.log" 2>&1
|
|
132
|
+
assert_contains "empty manifest skips submission" "Bundle manifest has no members or non-release work items; skipping submission." "$D2/stdout.log"
|
|
133
|
+
|
|
134
|
+
echo ""
|
|
135
|
+
echo "=== submit-bundle-manifest.test.sh: $PASS passed, $FAIL failed ==="
|
|
136
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
@@ -47,6 +47,14 @@
|
|
|
47
47
|
# portal group evidence by test cycle per
|
|
48
48
|
# ISO/IEC/IEEE 29119-3. Optional — older
|
|
49
49
|
# portals ignore the field (no error).
|
|
50
|
+
# --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
58
|
#
|
|
51
59
|
# Required environment variables:
|
|
52
60
|
# DEVAUDIT_BASE_URL e.g. https://meta-comply-production.up.railway.app
|
|
@@ -98,6 +106,8 @@ CHANGE_TYPE=""
|
|
|
98
106
|
GATE_STATUS=""
|
|
99
107
|
SDLC_STAGE=""
|
|
100
108
|
TEST_CYCLE=""
|
|
109
|
+
EVIDENCE_SCOPE=""
|
|
110
|
+
TEST_CYCLE_RECORD_ID=""
|
|
101
111
|
SENTINEL_CONTENT=""
|
|
102
112
|
COMMIT_TIMESTAMP=""
|
|
103
113
|
# Repeatable `--meta-key key=value` accumulator. Each pair gets merged
|
|
@@ -127,6 +137,8 @@ while [ "$#" -gt 0 ]; do
|
|
|
127
137
|
--gate-status) GATE_STATUS="$2"; shift 2 ;;
|
|
128
138
|
--sdlc-stage) SDLC_STAGE="$2"; shift 2 ;;
|
|
129
139
|
--test-cycle) TEST_CYCLE="$2"; shift 2 ;;
|
|
140
|
+
--evidence-scope) EVIDENCE_SCOPE="$2"; shift 2 ;;
|
|
141
|
+
--test-cycle-record-id) TEST_CYCLE_RECORD_ID="$2"; shift 2 ;;
|
|
130
142
|
# --meta-key key=value (repeatable). Merged into the metadata JSON
|
|
131
143
|
# before posting. Validates the `key=value` shape; rejects bare
|
|
132
144
|
# keys without `=`.
|
|
@@ -154,6 +166,14 @@ if [ -n "$SDLC_STAGE" ] && ! [[ "$SDLC_STAGE" =~ ^[1-5]$ ]]; then
|
|
|
154
166
|
echo "Error: --sdlc-stage must be an integer 1-5 (got: $SDLC_STAGE)"
|
|
155
167
|
exit 1
|
|
156
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"
|
|
171
|
+
exit 1
|
|
172
|
+
fi
|
|
173
|
+
if [ -n "$TEST_CYCLE_RECORD_ID" ] && [ "$EVIDENCE_SCOPE" != "cycle" ]; then
|
|
174
|
+
echo "Error: --test-cycle-record-id requires --evidence-scope cycle"
|
|
175
|
+
exit 1
|
|
176
|
+
fi
|
|
157
177
|
|
|
158
178
|
if [ -z "${DEVAUDIT_BASE_URL:-}" ]; then
|
|
159
179
|
echo "Error: DEVAUDIT_BASE_URL environment variable is required"
|
|
@@ -365,6 +385,8 @@ upload_presigned() {
|
|
|
365
385
|
\"changeType\": \"${CHANGE_TYPE}\",
|
|
366
386
|
\"sdlcStage\": \"${SDLC_STAGE}\",
|
|
367
387
|
\"testCycleId\": \"${TEST_CYCLE}\",
|
|
388
|
+
\"evidenceScope\": \"${EVIDENCE_SCOPE}\",
|
|
389
|
+
\"testCycleRecordId\": \"${TEST_CYCLE_RECORD_ID}\",
|
|
368
390
|
\"sentinelContent\": $(jq -Rn --arg v "$SENTINEL_CONTENT" '$v'),
|
|
369
391
|
\"commitTimestamp\": \"${COMMIT_TIMESTAMP}\"
|
|
370
392
|
}") || curl_exit=$?
|
|
@@ -531,6 +553,8 @@ for FILE in "${FILES[@]}"; do
|
|
|
531
553
|
[ -n "$GATE_STATUS" ] && CURL_ARGS+=(-F "gateStatus=${GATE_STATUS}")
|
|
532
554
|
[ -n "$SDLC_STAGE" ] && CURL_ARGS+=(-F "sdlcStage=${SDLC_STAGE}")
|
|
533
555
|
[ -n "$TEST_CYCLE" ] && CURL_ARGS+=(-F "testCycleId=${TEST_CYCLE}")
|
|
556
|
+
[ -n "$EVIDENCE_SCOPE" ] && CURL_ARGS+=(-F "evidenceScope=${EVIDENCE_SCOPE}")
|
|
557
|
+
[ -n "$TEST_CYCLE_RECORD_ID" ] && CURL_ARGS+=(-F "testCycleRecordId=${TEST_CYCLE_RECORD_ID}")
|
|
534
558
|
[ -n "$SENTINEL_CONTENT" ] && CURL_ARGS+=(-F "sentinelContent=${SENTINEL_CONTENT}")
|
|
535
559
|
[ -n "$COMMIT_TIMESTAMP" ] && CURL_ARGS+=(-F "commitTimestamp=${COMMIT_TIMESTAMP}")
|
|
536
560
|
|
|
@@ -243,6 +243,20 @@ for REQ in $REQUIREMENTS; do
|
|
|
243
243
|
done
|
|
244
244
|
if [ -n "$BUNDLED_FILE" ]; then
|
|
245
245
|
echo " OK: Bundled release evidence present: ${BUNDLED_FILE}"
|
|
246
|
+
BUNDLED_MANIFEST=""
|
|
247
|
+
for MANIFEST_PATTERN in \
|
|
248
|
+
"compliance/pending-releases/BUNDLED-CHANGES-${REQ}.json" \
|
|
249
|
+
"compliance/approved-releases/BUNDLED-CHANGES-${REQ}.json" \
|
|
250
|
+
"compliance/superseded-releases/BUNDLED-CHANGES-${REQ}.json"; do
|
|
251
|
+
BUNDLED_MANIFEST=$(find_first_match "$MANIFEST_PATTERN" || true)
|
|
252
|
+
[ -n "$BUNDLED_MANIFEST" ] && break
|
|
253
|
+
done
|
|
254
|
+
if [ -n "$BUNDLED_MANIFEST" ]; then
|
|
255
|
+
echo " OK: Bundled release manifest present: ${BUNDLED_MANIFEST}"
|
|
256
|
+
else
|
|
257
|
+
echo " ERROR: Bundled release evidence exists but BUNDLED-CHANGES-${REQ}.json is missing"
|
|
258
|
+
EXIT_CODE=1
|
|
259
|
+
fi
|
|
246
260
|
if grep -q '^## Bundled Changes' "$BUNDLED_FILE"; then
|
|
247
261
|
echo " OK: Bundled release evidence has canonical heading"
|
|
248
262
|
else
|
|
@@ -253,6 +267,8 @@ for REQ in $REQUIREMENTS; do
|
|
|
253
267
|
"$BUNDLED_FILE" \
|
|
254
268
|
"Bundled release evidence" \
|
|
255
269
|
"Core tracked release" \
|
|
270
|
+
"Bundle manifest" \
|
|
271
|
+
"Manifest hash" \
|
|
256
272
|
"Absorbed predecessor releases" \
|
|
257
273
|
"Absorbed non-release work" \
|
|
258
274
|
"Why bundled here" \
|
|
@@ -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. |
|
package/sdlc/files/_common/skills/governance-doc-author/references/incident-classification.md
CHANGED
|
@@ -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` |
|