@metasession.co/devaudit-cli 0.3.7 → 0.3.9
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/README.md +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +33 -3
- package/sdlc/files/_common/scripts/close-out-contract.sh +34 -0
- package/sdlc/files/_common/scripts/derive-release-version.sh +18 -0
- package/sdlc/files/_common/scripts/derive-release-version.test.sh +57 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.sh +119 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.test.sh +363 -0
- package/sdlc/files/_common/scripts/upload-evidence.sh +593 -0
- package/sdlc/files/_common/scripts/validate-test-summary.sh +37 -0
- package/sdlc/files/_common/scripts/validate-test-summary.test.sh +87 -0
- package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +16 -0
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +7 -4
- package/sdlc/files/ci/check-release-approval.yml.template +80 -21
- package/sdlc/files/ci/ci-status-fallback.yml.template +3 -0
- package/sdlc/files/ci/ci.yml.template +24 -33
- package/sdlc/files/ci/close-out-release.yml.template +67 -11
- package/sdlc/files/ci/compliance-evidence.yml.template +32 -16
- package/sdlc/files/ci/quality-gates-provenance.yml.template +62 -0
- package/sdlc/files/stacks/node/hooks/pre-push +58 -8
- package/sdlc/package.json +1 -1
- package/sdlc/src/bin/devaudit-sdlc.js +22 -2
- package/sdlc/src/blueprints/3-compile-evidence.raw.md +20 -6
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Quality Gates Provenance — verifies prior Quality Gates success on PRs to main
|
|
2
|
+
#
|
|
3
|
+
# Generated by `devaudit install` / `devaudit update` from sdlc-config.json.
|
|
4
|
+
# Do not edit manually — re-run the CLI (`devaudit update`) to regenerate.
|
|
5
|
+
#
|
|
6
|
+
# Why this exists (devaudit-installer#280):
|
|
7
|
+
# ci.yml and ci-status-fallback.yml only run on pushes/PRs to develop.
|
|
8
|
+
# A PR to main can be opened for a head SHA that never received a develop-side
|
|
9
|
+
# Quality Gates result. When that happens, neither existing workflow fires on
|
|
10
|
+
# the PR to main, so the release PR sits with missing/ambiguous protection.
|
|
11
|
+
#
|
|
12
|
+
# This workflow fires on PRs to main, queries the GitHub API for prior
|
|
13
|
+
# successful `Quality Gates` check runs on the PR head SHA, and fails closed
|
|
14
|
+
# if none are found. It does NOT rerun heavy CI — it is a same-SHA provenance
|
|
15
|
+
# check only.
|
|
16
|
+
#
|
|
17
|
+
# The job name is exactly `Quality Gates` to satisfy existing branch-protection
|
|
18
|
+
# rules without introducing a new required-check name.
|
|
19
|
+
|
|
20
|
+
name: CI Pipeline
|
|
21
|
+
|
|
22
|
+
on:
|
|
23
|
+
workflow_dispatch:
|
|
24
|
+
pull_request:
|
|
25
|
+
branches: [main]
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
quality-gates:
|
|
29
|
+
name: Quality Gates
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- name: Verify prior Quality Gates success on head SHA
|
|
33
|
+
env:
|
|
34
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
35
|
+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
36
|
+
CURRENT_RUN_ID: ${{ github.run_id }}
|
|
37
|
+
run: |
|
|
38
|
+
echo "Checking for prior successful 'Quality Gates' on SHA: $HEAD_SHA"
|
|
39
|
+
echo "Excluding current workflow run: $CURRENT_RUN_ID"
|
|
40
|
+
|
|
41
|
+
# Query GitHub API for check runs on this SHA with name "Quality Gates"
|
|
42
|
+
CURRENT_RUN_URL="/actions/runs/${CURRENT_RUN_ID}"
|
|
43
|
+
RESPONSE=$(gh api \
|
|
44
|
+
"/repos/${{ github.repository }}/commits/${HEAD_SHA}/check-runs?name=Quality%20Gates" \
|
|
45
|
+
--jq ".check_runs[]
|
|
46
|
+
| select(.status == \"completed\" and .conclusion == \"success\")
|
|
47
|
+
| select((.details_url // \"\") | contains(\"${CURRENT_RUN_URL}\") | not)")
|
|
48
|
+
|
|
49
|
+
if [ -n "$RESPONSE" ]; then
|
|
50
|
+
echo "Found prior successful Quality Gates result:"
|
|
51
|
+
echo "$RESPONSE" | head -5
|
|
52
|
+
echo ""
|
|
53
|
+
echo "Provenance verified — this SHA passed develop-side Quality Gates."
|
|
54
|
+
exit 0
|
|
55
|
+
else
|
|
56
|
+
echo "::error::This SHA did not pass develop-side Quality Gates."
|
|
57
|
+
echo "::error::Push this exact commit through the normal develop flow first."
|
|
58
|
+
echo ""
|
|
59
|
+
echo "No prior successful 'Quality Gates' check run found for SHA $HEAD_SHA"
|
|
60
|
+
echo "(excluding current workflow run $CURRENT_RUN_ID)."
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
@@ -34,16 +34,22 @@ if [ $? -ne 0 ]; then
|
|
|
34
34
|
fi
|
|
35
35
|
echo "Pre-push: TypeScript check passed."
|
|
36
36
|
|
|
37
|
+
# Capture stdin once — all subsequent checks iterate over the captured refs.
|
|
38
|
+
# Previous code had two `while read` loops; the second consumed nothing
|
|
39
|
+
# because stdin was already exhausted by the first (devaudit-installer#278).
|
|
40
|
+
PUSH_REFS="$(cat)"
|
|
41
|
+
|
|
42
|
+
INTEGRATION_BRANCH=$(jq -r '.integration_branch // "develop"' sdlc-config.json 2>/dev/null || echo "develop")
|
|
43
|
+
|
|
37
44
|
# ── 2. E2E evidence check (devaudit-installer#226) ───────────────────
|
|
38
45
|
# Only fires if UI-facing files are in the push. Checks for either
|
|
39
46
|
# playwright-report/ directory or .e2e-gate-passed sentinel (written by
|
|
40
47
|
# e2e-test-engineer after a successful run).
|
|
41
48
|
# Also checks .e2e-evidence-wired sentinel if e2e spec files changed
|
|
42
49
|
# (written by e2e-test-engineer Phase 5½ after validating tagTest/evidenceShot).
|
|
43
|
-
|
|
50
|
+
# Scoped to integration-branch pushes (correct for the normal workflow).
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
while read -r local_ref local_sha remote_ref remote_sha; do
|
|
52
|
+
echo "$PUSH_REFS" | while read -r local_ref local_sha remote_ref remote_sha; do
|
|
47
53
|
# Check if pushing to the integration branch
|
|
48
54
|
case "$remote_ref" in
|
|
49
55
|
*"refs/heads/$INTEGRATION_BRANCH") ;;
|
|
@@ -103,14 +109,17 @@ done
|
|
|
103
109
|
# If feat/fix/refactor/perf commits are in the push, verify the
|
|
104
110
|
# sdlc-implementer skill was invoked (writes .sdlc-implementer-invoked).
|
|
105
111
|
# Housekeeping types (docs/chore/ci/build/test/revert) are exempt.
|
|
112
|
+
# Fires on ANY push containing tracked commits, regardless of target branch
|
|
113
|
+
# (devaudit-installer#278: previously only checked integration-branch pushes,
|
|
114
|
+
# but feature-branch pushes also need the sentinel).
|
|
106
115
|
TRACKED_TYPES='^(feat|fix|refactor|perf)(\(.+\))?!?:'
|
|
107
116
|
HAS_TRACKED=false
|
|
108
117
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
# Parse stdin refs in the current shell (not a subshell) so HAS_TRACKED propagates.
|
|
119
|
+
for ref_line in $PUSH_REFS; do
|
|
120
|
+
set -- $ref_line
|
|
121
|
+
local_sha="$2"
|
|
122
|
+
remote_sha="$4"
|
|
114
123
|
if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then
|
|
115
124
|
RANGE="$local_sha"
|
|
116
125
|
else
|
|
@@ -133,6 +142,47 @@ if [ "$HAS_TRACKED" = "true" ]; then
|
|
|
133
142
|
exit 1
|
|
134
143
|
fi
|
|
135
144
|
echo "Pre-push: skill-invocation sentinel check passed."
|
|
145
|
+
|
|
146
|
+
# ── 3b. Phase-progression validation (devaudit-installer#278) ──────
|
|
147
|
+
# If the commit touches Phase 3 artifacts (compliance/evidence/REQ-XXX/),
|
|
148
|
+
# the sentinel must contain a phase "3" record. If it touches Phase 5
|
|
149
|
+
# close-out (approved-releases/ or RTM RELEASED), sentinel must have phase "5".
|
|
150
|
+
for ref_line in $PUSH_REFS; do
|
|
151
|
+
set -- $ref_line
|
|
152
|
+
local_sha="$2"
|
|
153
|
+
remote_sha="$4"
|
|
154
|
+
if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then
|
|
155
|
+
RANGE="$local_sha"
|
|
156
|
+
else
|
|
157
|
+
RANGE="${remote_sha}..${local_sha}"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
PHASE3_FILES=$(git diff --name-only "$RANGE" -- 'compliance/evidence/REQ-*/' 2>/dev/null || true)
|
|
161
|
+
PHASE5_FILES=$(git diff --name-only "$RANGE" -- 'compliance/approved-releases/' 'compliance/RTM.md' 2>/dev/null || true)
|
|
162
|
+
|
|
163
|
+
if [ -n "$PHASE3_FILES" ]; then
|
|
164
|
+
PHASES_IN_SENTINEL=$(jq -r '.[].currentPhase // .[].phase // empty' .sdlc-implementer-invoked 2>/dev/null || true)
|
|
165
|
+
if ! echo "$PHASES_IN_SENTINEL" | grep -q '"3"\|3'; then
|
|
166
|
+
echo ""
|
|
167
|
+
echo "ERROR: Commit touches Phase 3 artifacts (compliance/evidence/) but"
|
|
168
|
+
echo " the sentinel has no phase 3 record. Invoke sdlc-implementer"
|
|
169
|
+
echo " at Phase 3 before pushing these changes."
|
|
170
|
+
exit 1
|
|
171
|
+
fi
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
if [ -n "$PHASE5_FILES" ]; then
|
|
175
|
+
PHASES_IN_SENTINEL=$(jq -r '.[].currentPhase // .[].phase // empty' .sdlc-implementer-invoked 2>/dev/null || true)
|
|
176
|
+
if ! echo "$PHASES_IN_SENTINEL" | grep -q '"5"\|5'; then
|
|
177
|
+
echo ""
|
|
178
|
+
echo "ERROR: Commit touches Phase 5 close-out artifacts (approved-releases/ or RTM)"
|
|
179
|
+
echo " but the sentinel has no phase 5 record. Invoke sdlc-implementer"
|
|
180
|
+
echo " at Phase 5 before pushing these changes."
|
|
181
|
+
exit 1
|
|
182
|
+
fi
|
|
183
|
+
fi
|
|
184
|
+
done
|
|
185
|
+
echo "Pre-push: phase-progression check passed."
|
|
136
186
|
fi
|
|
137
187
|
|
|
138
188
|
# ── 4. Compliance artifact validation (devaudit-installer#226) ──────
|
package/sdlc/package.json
CHANGED
|
@@ -50,11 +50,30 @@ if (viewOnly) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
|
+
// Detect the invoking agent type (devaudit-installer#278):
|
|
54
|
+
// - 'skill' when invoked via a skill mechanism (Claude Code Skill, etc.)
|
|
55
|
+
// - 'native-agent' when invoked directly by the native agent (Cursor, Windsurf, etc.)
|
|
56
|
+
// - 'cli' when invoked manually from the terminal
|
|
57
|
+
const invokedBySkill = !!(process.env.DEVAUDIT_SKILL_NAME || process.env.DEVAUDIT_SKILL_INVOKED);
|
|
58
|
+
const invokedByAgent = !!(process.env.DEVAUDIT_AGENT || process.env.AI_AGENT_ID);
|
|
59
|
+
const initializedBy = invokedBySkill ? 'skill' : invokedByAgent ? 'native-agent' : 'cli';
|
|
60
|
+
|
|
61
|
+
// Extract REQ ID from args or environment (devaudit-installer#278)
|
|
62
|
+
let reqId = null;
|
|
63
|
+
const reqArg = args.find(arg => arg.startsWith('--req='));
|
|
64
|
+
if (reqArg) {
|
|
65
|
+
reqId = reqArg.split('=')[1];
|
|
66
|
+
} else if (process.env.DEVAUDIT_REQ_ID) {
|
|
67
|
+
reqId = process.env.DEVAUDIT_REQ_ID;
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
const newRecord = {
|
|
54
71
|
activatedAt: new Date().toISOString(),
|
|
55
72
|
currentPhase: phase,
|
|
56
|
-
initializedBy:
|
|
57
|
-
status: 'active'
|
|
73
|
+
initializedBy: initializedBy,
|
|
74
|
+
status: 'active',
|
|
75
|
+
reqId: reqId,
|
|
76
|
+
agentType: invokedBySkill ? (process.env.DEVAUDIT_SKILL_NAME || 'skill') : (invokedByAgent ? (process.env.DEVAUDIT_AGENT || 'native-agent') : 'manual'),
|
|
58
77
|
};
|
|
59
78
|
|
|
60
79
|
// Read existing sentinel and append, or create new array
|
|
@@ -83,6 +102,7 @@ try {
|
|
|
83
102
|
console.log(`\n✅ SDLC Gateway Initialized: Appended phase ${phase} record to sentinel at ${sentinelPath}`);
|
|
84
103
|
console.log(`🚀 Phase ${phase} orchestration active. Your local git commit gates are now open.`);
|
|
85
104
|
console.log(`📋 Phase history: ${phaseHistory.map(r => r.currentPhase).join(' → ')}`);
|
|
105
|
+
console.log(`👤 Initialized by: ${initializedBy}${reqId ? ` (REQ-${reqId})` : ''}`);
|
|
86
106
|
|
|
87
107
|
if (fs.existsSync(blueprintPath)) {
|
|
88
108
|
console.log(`\n--- PHASE EXECUTION MANIFEST ---`);
|
|
@@ -149,17 +149,17 @@ cat > compliance/evidence/REQ-XXX/test-execution-summary.md << 'EOF'
|
|
|
149
149
|
|
|
150
150
|
## Test design (devaudit#50)
|
|
151
151
|
|
|
152
|
-
Records the design-time decisions before listing run results — what was tested, what was deliberately
|
|
152
|
+
Records the design-time decisions before listing run results — what was tested, what was deliberately exempted, who/what decided. Auditors (and future maintainers) can see the scope decision was *made*, not implicit.
|
|
153
153
|
|
|
154
154
|
**Layers planned:** [unit | integration | e2e | visual | manual — pick the ones that apply to this REQ]
|
|
155
155
|
|
|
156
|
-
**Layers covered:** [same list, marked ✓ for shipped layers / `
|
|
156
|
+
**Layers covered:** [same list, marked ✓ for shipped layers / `NOT_NEEDED` for skipped ones with reason]
|
|
157
157
|
|
|
158
|
-
**
|
|
158
|
+
**Exemptions (if any):**
|
|
159
159
|
|
|
160
|
-
- [e.g. "e2e
|
|
161
|
-
- [e.g. "visual regression
|
|
162
|
-
-
|
|
160
|
+
- [e.g. "e2e NOT_NEEDED — schema-only change, no UI surface reads the new fields yet; will be covered in REQ-NNN when the admin form lands"]
|
|
161
|
+
- [e.g. "visual regression NOT_NEEDED — backend service change, no UI affected"]
|
|
162
|
+
- An exemption without a stated rationale is a gap, not an exemption. Either name *why* it was skipped or do the work.
|
|
163
163
|
|
|
164
164
|
**Skill invocation:** [`e2e-test-engineer` invoked on turn N during Phase 2 — verifiable from the chat transcript] / [`manual scope decision` — operator chose layers directly because <reason>]
|
|
165
165
|
|
|
@@ -175,6 +175,20 @@ Records the design-time decisions before listing run results — what was tested
|
|
|
175
175
|
| E2E Tests | PASS | [N]/[N] passed |
|
|
176
176
|
| Build | PASS | Production build succeeded |
|
|
177
177
|
|
|
178
|
+
## Accepted skips
|
|
179
|
+
|
|
180
|
+
Required when E2E gate result is `SKIPPED`. Optional when no accepted skips exist.
|
|
181
|
+
|
|
182
|
+
| Spec | Test | REQ/AC | Classification | Resolution attempted | Approved by | Rationale |
|
|
183
|
+
|------|------|--------|---------------|---------------------|-------------|----------|
|
|
184
|
+
| e2e/foo.spec.ts | "AC3: bar dialog" | REQ-XXX/AC3 | intentional_non_applicability | N/A — API-only change | [operator] | API-only change, no UI surface |
|
|
185
|
+
|
|
186
|
+
Rules:
|
|
187
|
+
- Each row must have a non-empty `Approved by` value.
|
|
188
|
+
- Classification must be one of: `environment_gap`, `precondition_gap`, `intentional_non_applicability`, `obsolete_or_quarantined`.
|
|
189
|
+
- `environment_gap` and `precondition_gap` skips must be resolved before listing — if they appear here, the gate is not truly `SKIPPED` with operator approval.
|
|
190
|
+
- A skipped test never proves an acceptance criterion.
|
|
191
|
+
|
|
178
192
|
## Test Cycles
|
|
179
193
|
|
|
180
194
|
| Cycle | CI Run | Gate Status | E2E Result | Coverage | Date |
|