@metasession.co/devaudit-cli 0.3.6 → 0.3.8

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.
@@ -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
- INTEGRATION_BRANCH=$(jq -r '.integration_branch // "develop"' sdlc-config.json 2>/dev/null || echo "develop")
50
+ # Scoped to integration-branch pushes (correct for the normal workflow).
44
51
 
45
- # Read stdin to get the pushed refs (husky pre-push protocol)
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
- while read -r local_ref local_sha remote_ref remote_sha; do
110
- case "$remote_ref" in
111
- *"refs/heads/$INTEGRATION_BRANCH") ;;
112
- *) continue ;;
113
- esac
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metasession.co/devaudit-sdlc",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "DevAudit SDLC CLI Engine — cross-agent terminal-driven SDLC orchestrator.",
5
5
  "bin": {
6
6
  "devaudit-sdlc": "./src/bin/devaudit-sdlc.js"
@@ -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: 'devaudit-cli-engine',
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 deferred, who/what decided. Auditors (and future maintainers) can see the scope decision was *made*, not implicit.
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 / `deferred` for skipped ones]
156
+ **Layers covered:** [same list, marked ✓ for shipped layers / `NOT_NEEDED` for skipped ones with reason]
157
157
 
158
- **Deferrals (if any):**
158
+ **Exemptions (if any):**
159
159
 
160
- - [e.g. "e2e N/A — schema-only change, no UI surface reads the new fields yet; deferred to REQ-NNN when the admin form lands"]
161
- - [e.g. "visual regression N/A — backend service change, no UI affected"]
162
- - A deferral without a stated rationale is a gap, not a deferral. Either name *why* it was skipped or do the work.
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 |