@metasession.co/devaudit-cli 0.1.61 → 0.1.62
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 +16 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +24 -0
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +33 -0
- package/sdlc/files/ci/ci.yml.template +54 -0
- package/sdlc/files/ci/feature-e2e.yml.template +56 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metasession.co/devaudit-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.62",
|
|
4
4
|
"description": "DevAudit CLI — installs, syncs, and operates the Metasession SDLC across consumer projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@clack/prompts": "^0.8.2",
|
|
36
|
-
"@metasession.co/devaudit-plugin-sdk": "^0.1.
|
|
36
|
+
"@metasession.co/devaudit-plugin-sdk": "^0.1.62",
|
|
37
37
|
"ajv": "^8.20.0",
|
|
38
38
|
"commander": "^12.1.0",
|
|
39
39
|
"consola": "^3.2.3",
|
|
@@ -189,6 +189,30 @@ For **visual regression** specifically:
|
|
|
189
189
|
|
|
190
190
|
Do additions, updates, and (approved) deletions in the same change so the suite stays internally consistent.
|
|
191
191
|
|
|
192
|
+
### Phase 5½ — Evidence wiring validation
|
|
193
|
+
|
|
194
|
+
Before running the suite (Phase 6), verify that the evidence traceability wiring is in place. A spec can pass Phase 6's "AC covered" check by having a correct assertion without ever calling `evidenceShot()` or tagging `@requirement` — producing zero portal evidence and letting the release reach UAT with no traceable screenshots (DevAudit-Installer #170, #169).
|
|
195
|
+
|
|
196
|
+
**For each in-scope REQ and its ACs from Phase 2's scenario table:**
|
|
197
|
+
|
|
198
|
+
1. **Check `@requirement` annotation.** Grep the authored/modified spec files for `@requirement REQ-XXX` tags. Every spec file that covers an in-scope REQ must carry at least one `@requirement REQ-XXX` annotation so the CI's `detect-req` step and the portal's evidence-by-requirement view can find it.
|
|
199
|
+
|
|
200
|
+
2. **Check `evidenceShot()` calls.** For each UI spec covering an in-scope REQ, grep for at least one `evidenceShot(page, 'REQ-XXX', <ac>, ...)` call per AC. The call must be placed **at the assertion that proves the AC**, before any further interaction or navigation. API-only specs that don't have a visual surface are exempt — note the exemption in the test-execution-summary.
|
|
201
|
+
|
|
202
|
+
3. **Check test title annotations.** Each test for an in-scope REQ must carry a `[REQ-XXX]` tag in the test title or `test.info().annotations` so the Playwright JSON reporter emits the REQ association. The portal uses this to map test results to requirements.
|
|
203
|
+
|
|
204
|
+
**If any check fails:**
|
|
205
|
+
|
|
206
|
+
Halt and report the gap to the user:
|
|
207
|
+
> Evidence wiring incomplete for REQ-XXX:
|
|
208
|
+
> - Missing `@requirement REQ-XXX` annotation in `e2e/<area>/foo.spec.ts`
|
|
209
|
+
> - Missing `evidenceShot()` call for AC2 in `e2e/<area>/bar.spec.ts`
|
|
210
|
+
> - Missing `[REQ-XXX]` tag in test title for "should filter by status"
|
|
211
|
+
>
|
|
212
|
+
> These must be fixed before running the suite — without them the portal will show zero screenshots and zero tagged tests for this REQ, and the CI gate (#169) will block the release.
|
|
213
|
+
|
|
214
|
+
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.
|
|
215
|
+
|
|
192
216
|
### Phase 6 — Execute and report
|
|
193
217
|
|
|
194
218
|
Run the suite. Strategy:
|
|
@@ -59,6 +59,39 @@ The only pauses in the whole workflow are the explicitly-named checkpoints:
|
|
|
59
59
|
|
|
60
60
|
Everything else is silent continuation. The rule is **opt-in-to-pause, not opt-out-of-pause**. If you find yourself stopping after a sub-skill's "Return to the running `sdlc-implementer` context" line and waiting for the operator to ask _"is anything happening?"_ — that is the bug this section exists to prevent. Keep going.
|
|
61
61
|
|
|
62
|
+
## Scope-expansion halt gate (devaudit-installer#171)
|
|
63
|
+
|
|
64
|
+
The change-request loop's scope-expansion halt rule (see [`references/change-request-loop.md`](./references/change-request-loop.md) §"If the change-request is fundamentally a different REQ") only fires during Phase 5 — after a UAT reviewer clicks "Request Changes" and the portal state is `uat_changes_requested`. In practice, scope-expanding requests also arrive from the user at any point after Phase 1:
|
|
65
|
+
|
|
66
|
+
- During Phase 2 (implementation) — user spots a related improvement and asks the agent to add it
|
|
67
|
+
- During Phase 3 (evidence compile) — user asks for an additional evidence artefact not in the plan
|
|
68
|
+
- During Phase 4 (UAT submission) — user exercises the deployed app and asks for a behaviour change
|
|
69
|
+
- Between UAT submission and approval — same scenario
|
|
70
|
+
- After approval but before merge — user makes a last-minute request
|
|
71
|
+
|
|
72
|
+
In all these cases, the portal state is NOT `uat_changes_requested`, so the change-request loop's halt rule never fires. The agent implements the out-of-scope change without flagging it.
|
|
73
|
+
|
|
74
|
+
**This gate fires on every user work request while a REQ is active, in any phase.**
|
|
75
|
+
|
|
76
|
+
When the user asks for a change that goes beyond the current REQ's acceptance criteria:
|
|
77
|
+
|
|
78
|
+
1. **Read the current REQ's `test-scope.md`** (or `implementation-plan.md` § Acceptance Criteria if no test-scope exists yet) to identify the defined ACs.
|
|
79
|
+
|
|
80
|
+
2. **Check whether the user's request maps to an existing AC.** If yes — proceed (or enter the change-request loop if in Phase 5).
|
|
81
|
+
|
|
82
|
+
3. **If no — halt with the scope-expansion message:**
|
|
83
|
+
|
|
84
|
+
> This request adds behaviour outside REQ-XXX's acceptance criteria (AC1–ACn defined in test-scope.md). Recommend filing a separate issue for \<subject\> and shipping REQ-XXX as originally scoped.
|
|
85
|
+
|
|
86
|
+
4. **Wait for the user to confirm one of:**
|
|
87
|
+
- **(a) File a separate issue** (new REQ) — ship the current REQ as-is. The agent continues with the original scope.
|
|
88
|
+
- **(b) Amend REQ-XXX's scope** — explicitly expand `test-scope.md` / `implementation-plan.md`, update the plan, invalidate existing evidence, and re-walk Stage 3. This option carries a warning: *"Amending scope after evidence is compiled (Stage 3+) invalidates the existing test-execution-summary, screenshots, and UAT verification. All Stage 3 evidence must be re-compiled."*
|
|
89
|
+
- **(c) Abandon the request** — do nothing, continue with the original scope.
|
|
90
|
+
|
|
91
|
+
**Do not implement the out-of-scope change before the user picks (a), (b), or (c).** The inertia trap is real: the agent is mid-flow, the codebase is open, and the request sounds reasonable. The gate exists to interrupt that inertia — STOP, surface the scope gap, and let the user decide.
|
|
92
|
+
|
|
93
|
+
This gate is distinct from Phase 2 step 4's "any deviation from the plan must be noted" rule: that covers implementation *approach* deviations (how to build something the plan says to build), not *scope* expansions (building something the plan doesn't say to build at all).
|
|
94
|
+
|
|
62
95
|
## SDLC navigability — LAST/NEXT status sticky (devaudit#131)
|
|
63
96
|
|
|
64
97
|
Long-running SDLC issues accumulate dozens of comments across multiple Claude Code sessions. The operator returning to the thread should be able to answer two questions in under five seconds:
|
|
@@ -549,6 +549,8 @@ jobs:
|
|
|
549
549
|
# compliance/pending-releases/. Failures now bump UPLOAD_FAILURES
|
|
550
550
|
# so the step exits non-zero and the release PR turns red.
|
|
551
551
|
SHOT_REQS=()
|
|
552
|
+
ZERO_Screenshot_REQS=()
|
|
553
|
+
EVIDENCE_GAPS=0
|
|
552
554
|
if [ -d compliance/pending-releases ]; then
|
|
553
555
|
for TICKET in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
|
|
554
556
|
[ -f "$TICKET" ] || continue
|
|
@@ -580,6 +582,12 @@ jobs:
|
|
|
580
582
|
REQ_SHOTS=(ci-evidence/compliance/evidence/"$REQ"/screenshots/*.png)
|
|
581
583
|
if [ "${#REQ_SHOTS[@]}" -eq 0 ]; then
|
|
582
584
|
echo "No per-AC screenshots for ${REQ} (none captured by evidenceShot this run)"
|
|
585
|
+
# devaudit-installer#169 — track REQs with zero screenshots
|
|
586
|
+
# so the evidence-completeness gate below can fail the job
|
|
587
|
+
# if the REQ also has zero tagged tests in the Playwright
|
|
588
|
+
# report. Previously this was a silent continue — the release
|
|
589
|
+
# proceeded to UAT with no traceable evidence.
|
|
590
|
+
ZERO_Screenshot_REQS+=("$REQ")
|
|
583
591
|
continue
|
|
584
592
|
fi
|
|
585
593
|
echo "Uploading ${#REQ_SHOTS[@]} screenshot(s) for ${REQ}"
|
|
@@ -626,6 +634,52 @@ jobs:
|
|
|
626
634
|
fi
|
|
627
635
|
shopt -u nullglob
|
|
628
636
|
|
|
637
|
+
# devaudit-installer#169 — Evidence-completeness gate.
|
|
638
|
+
# For each in-scope REQ that had zero screenshots, check whether
|
|
639
|
+
# the Playwright JSON report contains at least one test tagged
|
|
640
|
+
# with that REQ. If a REQ has zero screenshots AND zero tagged
|
|
641
|
+
# tests, the release has no traceable evidence and must not
|
|
642
|
+
# proceed to UAT. Previously this was a silent pass — the release
|
|
643
|
+
# arrived in UAT review with "SCREENSHOTS: none" and "TESTS: no
|
|
644
|
+
# tests in report tagged with this REQ" on the portal.
|
|
645
|
+
if [ "${#ZERO_Screenshot_REQS[@]}" -gt 0 ]; then
|
|
646
|
+
for REQ in "${ZERO_Screenshot_REQS[@]}"; do
|
|
647
|
+
TAGGED_COUNT=0
|
|
648
|
+
if [ -f ci-evidence/e2e-results.json ]; then
|
|
649
|
+
TAGGED_COUNT=$(python3 -c "
|
|
650
|
+
import json, sys
|
|
651
|
+
with open('ci-evidence/e2e-results.json') as f:
|
|
652
|
+
data = json.load(f)
|
|
653
|
+
def count(specs):
|
|
654
|
+
n = 0
|
|
655
|
+
for s in specs:
|
|
656
|
+
tags = []
|
|
657
|
+
for a in s.get('annotations', []):
|
|
658
|
+
tags.append(a.get('type','') + ':' + a.get('description',''))
|
|
659
|
+
for t in s.get('tags', []):
|
|
660
|
+
tags.append(t)
|
|
661
|
+
title = s.get('title', '')
|
|
662
|
+
if '${REQ}' in title or any('${REQ}' in t for t in tags):
|
|
663
|
+
n += 1
|
|
664
|
+
n += count(s.get('suites', []))
|
|
665
|
+
return n
|
|
666
|
+
print(count(data.get('suites', [])))
|
|
667
|
+
" 2>/dev/null || echo "0")
|
|
668
|
+
fi
|
|
669
|
+
if [ "$TAGGED_COUNT" -eq 0 ]; then
|
|
670
|
+
echo "::error::Evidence completeness gate: ${REQ} has zero tagged tests AND zero per-AC screenshots. The release cannot proceed to UAT with no traceable evidence. Ensure specs carry @requirement ${REQ} annotations and evidenceShot() calls (skill Phase 5½, issue #170)."
|
|
671
|
+
EVIDENCE_GAPS=$((EVIDENCE_GAPS + 1))
|
|
672
|
+
else
|
|
673
|
+
echo "${REQ} has zero screenshots but ${TAGGED_COUNT} tagged test(s) — evidence-completeness gate passed (screenshot gap is non-blocking)"
|
|
674
|
+
fi
|
|
675
|
+
done
|
|
676
|
+
fi
|
|
677
|
+
|
|
678
|
+
if [ "$EVIDENCE_GAPS" -gt 0 ]; then
|
|
679
|
+
echo "::error::${EVIDENCE_GAPS} in-scope REQ(s) have zero traceable evidence (no tagged tests, no screenshots) — release cannot pass UAT review"
|
|
680
|
+
exit 1
|
|
681
|
+
fi
|
|
682
|
+
|
|
629
683
|
# NOTE: committed compliance docs (planning category: RTM/test-plan/
|
|
630
684
|
# test-cases, release tickets, and per-requirement
|
|
631
685
|
# compliance/evidence/REQ-*/ folders) are intentionally NOT uploaded
|
|
@@ -43,22 +43,72 @@ jobs:
|
|
|
43
43
|
needs: detect-req
|
|
44
44
|
if: needs.detect-req.outputs.has_tests == 'true'
|
|
45
45
|
runs-on: {{RUNNER}}
|
|
46
|
+
|
|
47
|
+
services:
|
|
48
|
+
{{DATABASE_SERVICE}}:
|
|
49
|
+
image: {{DATABASE_IMAGE}}
|
|
50
|
+
ports:
|
|
51
|
+
- {{DATABASE_PORT}}
|
|
52
|
+
|
|
46
53
|
env:
|
|
54
|
+
{{DATABASE_ENV}}
|
|
55
|
+
{{APP_ENV}}
|
|
47
56
|
DEVAUDIT_BASE_URL_VAR: ${{ vars.DEVAUDIT_BASE_URL }}
|
|
48
57
|
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
58
|
+
|
|
49
59
|
steps:
|
|
50
60
|
- uses: actions/checkout@v6
|
|
51
61
|
with:
|
|
52
62
|
fetch-depth: 0
|
|
53
63
|
- uses: actions/setup-node@v4
|
|
54
64
|
with:
|
|
55
|
-
node-version:
|
|
56
|
-
-
|
|
57
|
-
- run: npx playwright install --with-deps
|
|
58
|
-
- name: Run in-scope E2E
|
|
65
|
+
node-version: {{NODE_VERSION}}
|
|
66
|
+
- name: Install dependencies (skip if lockfile unchanged)
|
|
59
67
|
run: |
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
LOCK_HASH=$(sha256sum package-lock.json | cut -d' ' -f1)
|
|
69
|
+
if [ -f node_modules/.lock-hash ] && [ "$(cat node_modules/.lock-hash)" = "$LOCK_HASH" ]; then
|
|
70
|
+
echo "node_modules up to date — skipping npm ci"
|
|
71
|
+
else
|
|
72
|
+
npm ci
|
|
73
|
+
echo "$LOCK_HASH" > node_modules/.lock-hash
|
|
74
|
+
fi
|
|
75
|
+
- name: Install Playwright (skip if already cached)
|
|
76
|
+
run: npx playwright install --with-deps chromium 2>/dev/null || npx playwright install chromium
|
|
77
|
+
|
|
78
|
+
{{DATABASE_URI_STEP}}
|
|
79
|
+
{{E2E_SETUP_STEP}}
|
|
80
|
+
- name: Kill stale dev server
|
|
81
|
+
run: lsof -ti:3000 | xargs kill -9 2>/dev/null || true
|
|
82
|
+
|
|
83
|
+
{{E2E_DEV_SERVER_STEP}}
|
|
84
|
+
|
|
85
|
+
- name: Wait for dev server
|
|
86
|
+
run: npx wait-on http://localhost:3000 --timeout 120000
|
|
87
|
+
|
|
88
|
+
# Compute the set of e2e spec files added on this branch (relative
|
|
89
|
+
# to the merge base). The evidenceShot helper reads E2E_NEW_SPECS
|
|
90
|
+
# at capture time and tags each screenshot `origin=feature` when
|
|
91
|
+
# its spec appears in the list, else `origin=regression`.
|
|
92
|
+
- name: Compute new-spec list for E2E origin tagging
|
|
93
|
+
run: |
|
|
94
|
+
BASE_REF="${{ github.base_ref || 'develop' }}"
|
|
95
|
+
BASE_SHA=$(git merge-base "origin/${BASE_REF}" HEAD 2>/dev/null || git rev-parse HEAD)
|
|
96
|
+
NEW_SPECS=$(git diff --name-only --diff-filter=A "${BASE_SHA}...HEAD" \
|
|
97
|
+
-- 'tests/e2e/**/*.spec.ts' 'tests/e2e/**/*.spec.tsx' \
|
|
98
|
+
'e2e/**/*.spec.ts' 'e2e/**/*.spec.tsx' 2>/dev/null || true)
|
|
99
|
+
{
|
|
100
|
+
echo "E2E_NEW_SPECS<<EOF"
|
|
101
|
+
echo "$NEW_SPECS"
|
|
102
|
+
echo "EOF"
|
|
103
|
+
} >> "$GITHUB_ENV"
|
|
104
|
+
if [ -n "$NEW_SPECS" ]; then
|
|
105
|
+
echo "New e2e specs on this branch (origin=feature for captures):"
|
|
106
|
+
echo "$NEW_SPECS" | sed 's/^/ /'
|
|
107
|
+
else
|
|
108
|
+
echo "No new e2e specs detected — all captures will tag origin=regression."
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
{{E2E_FEATURE_TEST_STEP}}
|
|
62
112
|
- name: Upload feature E2E evidence (stage 2, origin=feature)
|
|
63
113
|
if: always()
|
|
64
114
|
run: |
|