@metasession.co/devaudit-cli 0.1.70 → 0.1.71

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metasession.co/devaudit-cli",
3
- "version": "0.1.70",
3
+ "version": "0.1.71",
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.70",
36
+ "@metasession.co/devaudit-plugin-sdk": "^0.1.71",
37
37
  "ajv": "^8.20.0",
38
38
  "commander": "^12.1.0",
39
39
  "consola": "^3.2.3",
@@ -26,6 +26,13 @@
26
26
  # bare date — when exactly one ticket is open, attribute to it.
27
27
  # Multiple open tickets stays ambiguous → bare-date fallback.
28
28
  #
29
+ # Note (DevAudit-Installer#220): `devaudit update` syncs now include
30
+ # `[skip ci]` in their commit message, so they no longer trigger CI
31
+ # at all. The bare-date fallback (step 5) is therefore reached only by
32
+ # human-authored housekeeping commits — the intended use case. Skipped
33
+ # housekeeping changes are bundled into the next REQ release via
34
+ # `generate-bundled-changes.sh` (run by the register-release CI job).
35
+ #
29
36
  # Step 4-bis (DevAudit-Installer#95) is the zero-ceremony equivalent:
30
37
  # RTM.md is the file the operator already maintains as the source of
31
38
  # truth for release state. When step 4 finds no ticket and exactly one
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env bash
2
+ # generate-bundled-changes.sh
3
+ #
4
+ # Scans commits since a given ref (tag, SHA, or branch) and filters for
5
+ # housekeeping commit types (chore/docs/ci/build/test/revert) that were
6
+ # skipped via [skip ci] or otherwise not individually released. Outputs
7
+ # a markdown summary suitable for upload as `bundled_changes` evidence
8
+ # against a REQ-tagged release.
9
+ #
10
+ # Synced into the consumer's `scripts/` by `devaudit update`; invoked
11
+ # from `ci.yml` register-release job when the derived version is a
12
+ # REQ-tagged release (not bare-date).
13
+ #
14
+ # Usage:
15
+ # bash scripts/generate-bundled-changes.sh <since-ref> [<version>]
16
+ #
17
+ # Where:
18
+ # <since-ref> — git ref to scan from (tag, SHA, or branch name).
19
+ # Typically the previous release tag or the merge-base
20
+ # of the current branch and the previous release.
21
+ # <version> — (optional) the release version for the header.
22
+ # Defaults to "current release".
23
+ #
24
+ # Output (stdout): markdown document listing housekeeping commits
25
+ # absorbed into this release.
26
+ #
27
+ # DevAudit-Installer#220.
28
+
29
+ set -euo pipefail
30
+
31
+ SINCE_REF="${1:-}"
32
+ VERSION="${2:-current release}"
33
+
34
+ if [ -z "$SINCE_REF" ]; then
35
+ echo "Usage: bash scripts/generate-bundled-changes.sh <since-ref> [<version>]" >&2
36
+ exit 1
37
+ fi
38
+
39
+ # Verify the ref exists before proceeding.
40
+ if ! git rev-parse --verify "$SINCE_REF" >/dev/null 2>&1; then
41
+ echo "Error: ref '$SINCE_REF' not found in git history." >&2
42
+ exit 1
43
+ fi
44
+
45
+ # Housekeeping commit type prefixes (Conventional Commits).
46
+ # feat/fix are tracked changes — excluded. Anything else is housekeeping.
47
+ # The git log format is "<short-sha>\t<subject>", so the regex matches
48
+ # the commit type after the tab character.
49
+ HOUSEKEEPING_TYPES='^[0-9a-f]+ (chore|docs|ci|build|test|revert|style|perf|refactor)(\(.+\))?!?:'
50
+
51
+ # Collect commits since the ref, filtering for housekeeping types.
52
+ # Format: <short-sha>\t<subject>
53
+ COMMITS=$(git log "$SINCE_REF"..HEAD --format='%h %s' 2>/dev/null || true)
54
+
55
+ if [ -z "$COMMITS" ]; then
56
+ # No commits since ref — output an empty summary.
57
+ echo "## Bundled Changes"
58
+ echo ""
59
+ echo "No housekeeping commits found since \`${SINCE_REF}\`."
60
+ echo ""
61
+ exit 0
62
+ fi
63
+
64
+ # Filter for housekeeping commit types.
65
+ BUNDLED=$(echo "$COMMITS" | grep -E "$HOUSEKEEPING_TYPES" || true)
66
+
67
+ if [ -z "$BUNDLED" ]; then
68
+ echo "## Bundled Changes"
69
+ echo ""
70
+ echo "No housekeeping commits found since \`${SINCE_REF}\`."
71
+ echo ""
72
+ exit 0
73
+ fi
74
+
75
+ # Count the bundled commits.
76
+ COUNT=$(echo "$BUNDLED" | wc -l | tr -d ' ')
77
+
78
+ # Generate the markdown summary.
79
+ echo "## Bundled Changes"
80
+ echo ""
81
+ echo "The following ${COUNT} housekeeping commit(s) were absorbed into release \`${VERSION}\` since \`${SINCE_REF}\`:"
82
+ echo ""
83
+ echo "$BUNDLED" | while IFS=$'\t' read -r SHA SUBJECT; do
84
+ echo "- \`${SHA}\` ${SUBJECT}"
85
+ done
86
+ echo ""
87
+ echo "These commits were not individually released (tooling syncs / housekeeping)."
88
+ echo "Gate evidence above covers the full state of develop at CI time, including these changes."
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env bash
2
+ # generate-bundled-changes.test.sh — Tests for generate-bundled-changes.sh.
3
+ #
4
+ # Builds a throwaway git repo per case, crafts commits with mixed types,
5
+ # runs the helper against it, asserts on the stdout content. Hermetic:
6
+ # runs inside mktemp'd directories that are torn down at the end.
7
+ #
8
+ # Usage:
9
+ # ./scripts/generate-bundled-changes.test.sh
10
+
11
+ set -euo pipefail
12
+
13
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
14
+ HELPER="$SCRIPT_DIR/generate-bundled-changes.sh"
15
+ [ -x "$HELPER" ] || chmod +x "$HELPER"
16
+
17
+ PASS=0
18
+ FAIL=0
19
+
20
+ TMPDIR_BASE="$(mktemp -d)"
21
+ trap 'rm -rf "$TMPDIR_BASE"' EXIT
22
+
23
+ # Build a fresh git fixture with an initial commit.
24
+ make_fixture() {
25
+ local dir="$1"
26
+ rm -rf "$dir"
27
+ mkdir -p "$dir"
28
+ cd "$dir"
29
+ git init -q --initial-branch=main
30
+ git config user.email "test@example.com"
31
+ git config user.name "test"
32
+ echo "x" > f.txt
33
+ git add f.txt
34
+ git commit -q -m "feat: initial commit [REQ-001]"
35
+ }
36
+
37
+ # Add a commit with a given message.
38
+ add_commit() {
39
+ local msg="$1"
40
+ echo "$(date +%s%N)" >> f.txt
41
+ git add f.txt
42
+ git commit -q -m "$msg"
43
+ }
44
+
45
+ assert_contains() {
46
+ local desc="$1" want="$2" got="$3"
47
+ if echo "$got" | grep -qF "$want"; then
48
+ echo " PASS: $desc"
49
+ PASS=$((PASS + 1))
50
+ else
51
+ echo " FAIL: $desc"
52
+ echo " want (substring): $want"
53
+ echo " got:"
54
+ echo "$got" | sed 's/^/ /'
55
+ FAIL=$((FAIL + 1))
56
+ fi
57
+ }
58
+
59
+ assert_not_contains() {
60
+ local desc="$1" want="$2" got="$3"
61
+ if echo "$got" | grep -qF "$want"; then
62
+ echo " FAIL: $desc"
63
+ echo " should not contain: $want"
64
+ echo " got:"
65
+ echo "$got" | sed 's/^/ /'
66
+ FAIL=$((FAIL + 1))
67
+ else
68
+ echo " PASS: $desc"
69
+ PASS=$((PASS + 1))
70
+ fi
71
+ }
72
+
73
+ # ─── Test 1: Mixed commits — only housekeeping types in output ─────
74
+ echo "Test 1: mixed commits filter to housekeeping only"
75
+ DIR1="$TMPDIR_BASE/test1"
76
+ make_fixture "$DIR1"
77
+ add_commit "chore: sync DevAudit templates from v0.1.69 to v0.1.70 [skip ci]"
78
+ add_commit "docs: update API reference for /bookings endpoint"
79
+ add_commit "feat: add booking widget [REQ-042]"
80
+ add_commit "chore: bump eslint 9.0.5 to 9.0.6"
81
+ add_commit "fix: resolve null pointer in booking service [REQ-042]"
82
+ # Tag the initial commit as the "since" ref
83
+ SINCE=$(git rev-list --max-parents=0 HEAD)
84
+ OUTPUT=$(bash "$HELPER" "$SINCE" "REQ-042" 2>&1 || true)
85
+ assert_contains "housekeeping chore: sync in output" "chore: sync DevAudit templates" "$OUTPUT"
86
+ assert_contains "housekeeping docs: update in output" "docs: update API reference" "$OUTPUT"
87
+ assert_contains "housekeeping chore: bump eslint in output" "chore: bump eslint" "$OUTPUT"
88
+ assert_not_contains "feat commit excluded" "feat: add booking widget" "$OUTPUT"
89
+ assert_not_contains "fix commit excluded" "fix: resolve null pointer" "$OUTPUT"
90
+ assert_contains "header present" "## Bundled Changes" "$OUTPUT"
91
+ assert_contains "version in header" "REQ-042" "$OUTPUT"
92
+ echo
93
+
94
+ # ─── Test 2: No housekeeping commits ────────────────────────────────
95
+ echo "Test 2: no housekeeping commits — empty summary"
96
+ DIR2="$TMPDIR_BASE/test2"
97
+ make_fixture "$DIR2"
98
+ add_commit "feat: add feature A [REQ-010]"
99
+ add_commit "fix: fix bug B [REQ-010]"
100
+ SINCE=$(git rev-list --max-parents=0 HEAD)
101
+ OUTPUT=$(bash "$HELPER" "$SINCE" "REQ-010" 2>&1 || true)
102
+ assert_contains "no housekeeping message" "No housekeeping commits found" "$OUTPUT"
103
+ echo
104
+
105
+ # ─── Test 3: All housekeeping types captured ────────────────────────
106
+ echo "Test 3: all housekeeping commit types captured"
107
+ DIR3="$TMPDIR_BASE/test3"
108
+ make_fixture "$DIR3"
109
+ add_commit "ci: update workflow timeout"
110
+ add_commit "build: bump node version to 22"
111
+ add_commit "test: add unit test for booking"
112
+ add_commit "revert: remove experimental feature"
113
+ add_commit "style: fix formatting in utils"
114
+ add_commit "perf: optimize query in booking"
115
+ add_commit "refactor: extract validation logic"
116
+ SINCE=$(git rev-list --max-parents=0 HEAD)
117
+ OUTPUT=$(bash "$HELPER" "$SINCE" "REQ-030" 2>&1 || true)
118
+ assert_contains "ci: type captured" "ci: update workflow timeout" "$OUTPUT"
119
+ assert_contains "build: type captured" "build: bump node version" "$OUTPUT"
120
+ assert_contains "test: type captured" "test: add unit test for booking" "$OUTPUT"
121
+ assert_contains "revert: type captured" "revert: remove experimental feature" "$OUTPUT"
122
+ assert_contains "style: type captured" "style: fix formatting" "$OUTPUT"
123
+ assert_contains "perf: type captured" "perf: optimize query" "$OUTPUT"
124
+ assert_contains "refactor: type captured" "refactor: extract validation" "$OUTPUT"
125
+ echo
126
+
127
+ # ─── Test 4: No commits since ref ───────────────────────────────────
128
+ echo "Test 4: no commits since ref — empty summary"
129
+ DIR4="$TMPDIR_BASE/test4"
130
+ make_fixture "$DIR4"
131
+ SINCE=$(git rev-parse HEAD)
132
+ OUTPUT=$(bash "$HELPER" "$SINCE" "REQ-001" 2>&1 || true)
133
+ assert_contains "no commits message" "No housekeeping commits found" "$OUTPUT"
134
+ echo
135
+
136
+ # ─── Test 5: Invalid ref ────────────────────────────────────────────
137
+ echo "Test 5: invalid ref — error exit"
138
+ DIR5="$TMPDIR_BASE/test5"
139
+ make_fixture "$DIR5"
140
+ set +e
141
+ OUTPUT=$(bash "$HELPER" "nonexistent-ref" "REQ-001" 2>&1)
142
+ RC=$?
143
+ set -e
144
+ if [ "$RC" -ne 0 ]; then
145
+ echo " PASS: non-zero exit code for invalid ref"
146
+ PASS=$((PASS + 1))
147
+ else
148
+ echo " FAIL: expected non-zero exit code for invalid ref"
149
+ FAIL=$((FAIL + 1))
150
+ fi
151
+ echo
152
+
153
+ # ─── Test 6: Scoped commit types (chore(deps)) ─────────────────────
154
+ echo "Test 6: scoped housekeeping commits captured"
155
+ DIR6="$TMPDIR_BASE/test6"
156
+ make_fixture "$DIR6"
157
+ add_commit "chore(deps): bump eslint from 9.0.5 to 9.0.6"
158
+ add_commit "ci(workflow): update timeout to 30 minutes"
159
+ SINCE=$(git rev-list --max-parents=0 HEAD)
160
+ OUTPUT=$(bash "$HELPER" "$SINCE" "REQ-050" 2>&1 || true)
161
+ assert_contains "scoped chore(deps) captured" "chore(deps): bump eslint" "$OUTPUT"
162
+ assert_contains "scoped ci(workflow) captured" "ci(workflow): update timeout" "$OUTPUT"
163
+ echo
164
+
165
+ # ─── Test 7: Missing since-ref argument ────────────────────────────
166
+ echo "Test 7: missing since-ref argument — error exit"
167
+ DIR7="$TMPDIR_BASE/test7"
168
+ make_fixture "$DIR7"
169
+ set +e
170
+ OUTPUT=$(bash "$HELPER" 2>&1)
171
+ RC=$?
172
+ set -e
173
+ if [ "$RC" -ne 0 ]; then
174
+ echo " PASS: non-zero exit code for missing argument"
175
+ PASS=$((PASS + 1))
176
+ else
177
+ echo " FAIL: expected non-zero exit code for missing argument"
178
+ FAIL=$((FAIL + 1))
179
+ fi
180
+ echo
181
+
182
+ echo "=== Summary: ${PASS} pass / ${FAIL} fail ==="
183
+ [ "$FAIL" -eq 0 ]
@@ -547,8 +547,9 @@ Invoked separately by the user after UAT activity on the portal. Trigger: "resum
547
547
 
548
548
  - **Changes requested** → run change-request loop:
549
549
  - Fetch change-request comments from the PR (`gh pr view <M> --comments`) and from the portal release page.
550
- - **Classify the change request (devaudit-installer#210 §6b):** Assess whether the change request is a **defect** (the implementation doesn't match the AC — the reviewer found a bug) or a **scope change** (the AC itself needs amendment — the reviewer wants new behaviour the AC didn't specify).
550
+ - **Classify the change request (devaudit-installer#210 §6b, devaudit-installer#212 Gap 6):** Assess whether the change request is a **defect** (the implementation doesn't match the AC — the reviewer found a bug), a **requirements gap** (the reviewer identified behaviour that should have been an AC but wasn't — the plan was incomplete, not the implementation), or a **scope change** (the reviewer wants new behaviour this REQ genuinely shouldn't deliver).
551
551
  - **If it's a defect:** Delegate to `e2e-test-engineer` to file the incident with the `incident` label + `### Framework attribution` section before fixing. Invoke `Skill(name: "e2e-test-engineer", args: "Phase 6 triage — UAT reviewer found defect in REQ-XXX. Change-request comment: <summary>. Triage and file application defects with the `incident` label + `### Framework attribution` section per the standard convention.")`. `e2e-test-engineer` returns with a summary of filed incidents. Then fix the defect in the same Phase 2 re-run. This ensures UAT-discovered defects produce `incident_report` evidence, closing `ISO29119.3.5.4` for the defect — not just silently fixing it.
552
+ - **If it's a requirements gap:** Trigger the [requirements gap flow](#requirements-gap-flow-devaudit-installer212) with the reviewer's feedback as the gap report. The operator decides: accept the gap (ship without the edge case), amend the ACs (add the edge case as a new AC, update plan + SRS, re-walk Phase 2-3), or file a follow-up REQ. If "amend" is chosen, the change-request iteration includes the new AC in the delta-plan section, `requirements-aligner` is re-invoked for the new AC, and the SRS is updated. Do NOT file an incident — the plan was incomplete, not the implementation buggy. Do NOT use the scope-expansion halt gate — this is a plan quality issue, not a scope creep request.
552
553
  - **If it's a scope change:** Use the existing scope-expansion halt gate (REQ-SKILL-IMPLEMENTER-019) — file a separate issue or amend the REQ scope. Do NOT file an incident — the AC itself needs amendment, not a defect fix.
553
554
  - Add a new `## Change-request iteration N` section to `compliance/plans/REQ-XXX/implementation-plan.md` describing what changed and why.
554
555
  - **Update the release ticket status** (`compliance/pending-releases/RELEASE-TICKET-REQ-XXX.md`): set `**Status:**` to `CHANGES REQUESTED — ITERATION N` when entering the iteration. Commit the ticket update alongside the delta-plan section in the same iteration commit.
@@ -299,6 +299,35 @@ jobs:
299
299
  --environment uat --category planning \
300
300
  --git-sha ${{ github.sha }} --branch ${{ github.ref_name }} || true
301
301
 
302
+ - name: Generate and upload bundled changes (REQ releases only)
303
+ if: env.DEVAUDIT_BASE_URL != ''
304
+ run: |
305
+ VERSION="${{ steps.version.outputs.version }}"
306
+ # Only generate bundled changes for REQ-tagged releases —
307
+ # housekeeping (bare-date) releases don't bundle other housekeeping.
308
+ if [[ "$VERSION" =~ ^v[0-9]{4}\.[0-9]{2}\.[0-9]{2}(\.[0-9]+)?$ ]]; then
309
+ echo "Version ${VERSION} is housekeeping — skipping bundled changes."
310
+ exit 0
311
+ fi
312
+ chmod +x scripts/generate-bundled-changes.sh 2>/dev/null || true
313
+ chmod +x scripts/upload-evidence.sh 2>/dev/null || true
314
+ # Find the previous release tag to scan from. Fall back to last 50
315
+ # commits if no prior tag exists.
316
+ SINCE_REF=$(git tag --sort=-creatordate | head -1 || true)
317
+ if [ -z "$SINCE_REF" ]; then
318
+ SINCE_REF="HEAD~50"
319
+ fi
320
+ echo "Scanning bundled changes since ${SINCE_REF} for ${VERSION}"
321
+ BUNDLED_FILE="compliance/pending-releases/BUNDLED-CHANGES-${VERSION}.md"
322
+ mkdir -p compliance/pending-releases
323
+ bash scripts/generate-bundled-changes.sh "$SINCE_REF" "$VERSION" > "$BUNDLED_FILE"
324
+ # Upload as bundled_changes evidence against the REQ release.
325
+ bash scripts/upload-evidence.sh \
326
+ {{PROJECT_SLUG}} _compliance-docs bundled_changes "$BUNDLED_FILE" \
327
+ --release "$VERSION" \
328
+ --environment uat --category planning \
329
+ --git-sha ${{ github.sha }} --branch ${{ github.ref_name }} || true
330
+
302
331
  - name: Sync known requirements from RTM
303
332
  if: env.DEVAUDIT_BASE_URL != ''
304
333
  env: