create-sdd-project 0.18.2 → 0.18.3
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 +1 -1
- package/template/.claude/commands/audit-merge.md +110 -14
- package/template/.claude/skills/development-workflow/references/merge-checklist.md +2 -0
- package/template/.gemini/commands/audit-merge-instructions.md +110 -14
- package/template/.gemini/skills/development-workflow/references/merge-checklist.md +2 -0
package/package.json
CHANGED
|
@@ -52,12 +52,21 @@ Run only if `git diff origin/<target-branch>..HEAD --name-only` shows `.json` fi
|
|
|
52
52
|
|
|
53
53
|
Eleven empirically-validated drift patterns. Failures are NOT blockers for the compliance verdict, but MUST be refreshed before requesting user authorization (the user will otherwise catch them during audit and send the PR back). Each check has a concrete shell recipe — use BSD-grep-compatible regex (no `\K`).
|
|
54
54
|
|
|
55
|
-
**12. P1 — PR body test count stale.** The PR body's
|
|
55
|
+
**12. P1 — PR body test count stale (v0.18.3 multi-workspace extension — C1).** The PR body's test ratios should all appear in ticket evidence (AC / DoD / Completion Log). Agents commonly open the PR at Step 4 and add tests during Step 5 review — the PR body numbers become stale. In monorepos with multiple workspaces (e.g. api, web, bot, scraper) the PR body may quote several `N/N` ratios; v0.18.3 walks them all instead of comparing only the first. Subset direction: PR ratios ⊆ ticket ratios (the ticket Completion Log is the more comprehensive record and accumulates intermediate per-step ratios that the PR body legitimately omits). Three fallback cases: (a) ≥ 1 ratio on each side → verify each PR ratio appears in ticket; (b) PR has ratios but ticket has none (or vice versa) → emit explicit `P1 N/A` note, no drift flag; (c) neither side has ratios → emit `P1 N/A` note.
|
|
56
56
|
```bash
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
TEST_KW_RE='(npm test|pnpm test|tests?[^|]*[0-9]|[*: ]tests?[*: ]+[0-9])'
|
|
58
|
+
PR_BODY=$(gh pr view --json body -q .body 2>/dev/null || true)
|
|
59
|
+
PR_RATIOS=$(echo "$PR_BODY" | grep -iE "$TEST_KW_RE" | grep -oE "[0-9]+/[0-9]+" | sort -u)
|
|
60
|
+
TICKET_RATIOS=$(grep -iE "$TEST_KW_RE" "$TICKET" | grep -oE "[0-9]+/[0-9]+" | sort -u)
|
|
61
|
+
if [ -z "$PR_RATIOS" ] || [ -z "$TICKET_RATIOS" ]; then
|
|
62
|
+
echo "P1 N/A: no comparable test-count ratios extracted (PR=$(echo "$PR_RATIOS" | tr '\n' ',' ), ticket=$(echo "$TICKET_RATIOS" | tr '\n' ',' ))"
|
|
63
|
+
else
|
|
64
|
+
while IFS= read -r r; do
|
|
65
|
+
[ -z "$r" ] && continue
|
|
66
|
+
echo "$TICKET_RATIOS" | grep -qFx "$r" \
|
|
67
|
+
|| flag "P1 drift: PR ratio $r not found in ticket evidence (ticket ratios: $(echo "$TICKET_RATIOS" | tr '\n' ' '))"
|
|
68
|
+
done <<< "$PR_RATIOS"
|
|
69
|
+
fi
|
|
61
70
|
```
|
|
62
71
|
|
|
63
72
|
**13. P2 — Merge Checklist Evidence rows aspirational.** Rows marked `[x]` with future-tense Evidence ("will land", "to be created", "pending", "next commit", "TBD") — the row claims done but the work hasn't happened yet.
|
|
@@ -94,6 +103,8 @@ for t in docs/tickets/*.md; do
|
|
|
94
103
|
status=$(grep -E "^\*\*Status:\*\*" "$t" | head -1 \
|
|
95
104
|
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
96
105
|
| sed -E 's/[[:space:]]*\*?\*?[[:space:]]*\|.*//' \
|
|
106
|
+
| sed -E 's/[[:space:]]+(\(.*\)|—.*|–.*|-.*)$//' \
|
|
107
|
+
| sed -E 's/\*\*[[:space:]]*$//' \
|
|
97
108
|
| sed -E 's/[[:space:]]+$//')
|
|
98
109
|
[ "$status" = "Done" ] && continue
|
|
99
110
|
ticket_id=$(basename "$t" .md | sed -E 's/-[a-z].*//')
|
|
@@ -103,12 +114,27 @@ done
|
|
|
103
114
|
[ "$FROZEN_COUNT" -eq 1 ] && flag "P5 drift: 1 frozen ticket"
|
|
104
115
|
```
|
|
105
116
|
|
|
106
|
-
**17. P6 — AC count off-by-N.** Merge Checklist Evidence row 1 claim
|
|
117
|
+
**17. P6 — AC count off-by-N.** Merge Checklist Evidence row 1 claim diverges from actual count. Two canonical forms: `all N marked` (N = total, implies all are `[x]`) and `AC: X/Y done` (X = marked, Y = total — supports deferred ACs where Y > X intentionally). For `AC: X/Y` form, compare Y to actual total AND X to actual marked. For `all N marked` form, compare N to actual total.
|
|
107
118
|
```bash
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
AC_BLOCK=$(awk '/^## Acceptance Criteria/,/^## Definition of Done/' "$TICKET")
|
|
120
|
+
ACTUAL_TOTAL=$(echo "$AC_BLOCK" | grep -cE "^- \[[x ]\]")
|
|
121
|
+
ACTUAL_MARKED=$(echo "$AC_BLOCK" | grep -cE "^- \[x\]")
|
|
122
|
+
CLAIM_LINE=$(grep -oE 'all [0-9]+ marked|AC: [0-9]+/[0-9]+' "$TICKET" | head -1)
|
|
123
|
+
if echo "$CLAIM_LINE" | grep -qE '^AC: [0-9]+/[0-9]+'; then
|
|
124
|
+
CLAIMED_MARKED=$(echo "$CLAIM_LINE" | grep -oE '[0-9]+' | head -1)
|
|
125
|
+
CLAIMED_TOTAL=$(echo "$CLAIM_LINE" | grep -oE '[0-9]+' | tail -1)
|
|
126
|
+
[ -n "$CLAIMED_TOTAL" ] && [ "$CLAIMED_TOTAL" != "$ACTUAL_TOTAL" ] \
|
|
127
|
+
&& [ $((ACTUAL_TOTAL - CLAIMED_TOTAL)) -ge 2 -o $((CLAIMED_TOTAL - ACTUAL_TOTAL)) -ge 2 ] \
|
|
128
|
+
&& flag "P6 drift: claim AC total '$CLAIMED_TOTAL' vs actual total $ACTUAL_TOTAL"
|
|
129
|
+
[ -n "$CLAIMED_MARKED" ] && [ "$CLAIMED_MARKED" != "$ACTUAL_MARKED" ] \
|
|
130
|
+
&& [ $((ACTUAL_MARKED - CLAIMED_MARKED)) -ge 2 -o $((CLAIMED_MARKED - ACTUAL_MARKED)) -ge 2 ] \
|
|
131
|
+
&& flag "P6 drift: claim AC marked '$CLAIMED_MARKED' vs actual marked $ACTUAL_MARKED"
|
|
132
|
+
elif [ -n "$CLAIM_LINE" ]; then
|
|
133
|
+
CLAIMED=$(echo "$CLAIM_LINE" | grep -oE '[0-9]+' | head -1)
|
|
134
|
+
[ -n "$CLAIMED" ] && [ "$CLAIMED" != "$ACTUAL_TOTAL" ] \
|
|
135
|
+
&& [ $((ACTUAL_TOTAL - CLAIMED)) -ge 2 -o $((CLAIMED - ACTUAL_TOTAL)) -ge 2 ] \
|
|
136
|
+
&& flag "P6 drift: 'all $CLAIMED marked' vs actual AC count $ACTUAL_TOTAL"
|
|
137
|
+
fi
|
|
112
138
|
```
|
|
113
139
|
|
|
114
140
|
**18. P7 — Test count drift within ticket (final-sections only).** Only flag AC / DoD / tracker Active-Session numbers diverging from Completion Log terminal. Intermediate rows are legitimate.
|
|
@@ -157,6 +183,8 @@ awk -F'|' '/^\| [0-9]{4}-[0-9]{2}-[0-9]{2}/ {
|
|
|
157
183
|
TICKET_STATUS=$(grep -E "^\*\*Status:\*\*" "$TICKET" | head -1 \
|
|
158
184
|
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
159
185
|
| sed -E 's/[[:space:]]*\*?\*?[[:space:]]*\|.*//' \
|
|
186
|
+
| sed -E 's/[[:space:]]+(\(.*\)|—.*|–.*|-.*)$//' \
|
|
187
|
+
| sed -E 's/\*\*[[:space:]]*$//' \
|
|
160
188
|
| sed -E 's/[[:space:]]+$//')
|
|
161
189
|
FEATURE_ID=$(basename "$TICKET" .md | sed -E 's/-[a-z].*//')
|
|
162
190
|
TRACKER_STATUS=$(grep -F "$FEATURE_ID" docs/project_notes/product-tracker.md | grep -oE "\| (in-progress|done|pending|blocked) \|" | head -1 | sed -E 's/\| ([a-z-]+) \|/\1/')
|
|
@@ -189,9 +217,69 @@ if [ -f "$TRACKER" ]; then
|
|
|
189
217
|
fi
|
|
190
218
|
```
|
|
191
219
|
|
|
220
|
+
**24. P13 — key_facts delta vs ticket atom-count mismatch (added v0.18.3).** When the ticket's Completion Log or MCE quantifies a delta against `key_facts.md` (e.g. `+8 atoms`, `+5 aliases`, `+27 dishes`), the corresponding feature row in `key_facts.md` should record the same delta. Whitespace-safe iteration via `while IFS= read -r`; FEATURE_ID-anchored block scan avoids false-pass on identical deltas elsewhere in the file. English keyword set; Spanish (`átomos`, `platos`) deferred to v0.19.x.
|
|
221
|
+
```bash
|
|
222
|
+
KEY_FACTS=docs/project_notes/key_facts.md
|
|
223
|
+
if [ -f "$KEY_FACTS" ]; then
|
|
224
|
+
FEATURE_ID=$(basename "$TICKET" .md | sed -E 's/-[a-z].*//')
|
|
225
|
+
TICKET_DELTAS=$(grep -oE '\+[0-9]+ (atoms?|aliases?|dishes?|entries|rows)' "$TICKET" | sort -u)
|
|
226
|
+
KEY_FACTS_BLOCK=$(grep -A 3 -F "$FEATURE_ID" "$KEY_FACTS" 2>/dev/null || true)
|
|
227
|
+
while IFS= read -r claim; do
|
|
228
|
+
[ -z "$claim" ] && continue
|
|
229
|
+
if [ -z "$KEY_FACTS_BLOCK" ] || ! echo "$KEY_FACTS_BLOCK" | grep -qF "$claim"; then
|
|
230
|
+
flag "P13 drift: ticket claims '$claim' but key_facts.md block for $FEATURE_ID lacks the same delta"
|
|
231
|
+
fi
|
|
232
|
+
done <<< "$TICKET_DELTAS"
|
|
233
|
+
fi
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**25. P14 — MCE Action 1 row stale post-merge (added v0.18.3).** When ticket Status normalizes to `Done` AND the MCE Action 1 row still has `Step 6 [ ]` / `Step 6 [-]`, the row was written pre-merge and not updated post-squash. **Strict scoping**: awk state machine terminates the MCE block at the NEXT `^## ` line of any name (NOT `[^M]` — that incorrectly absorbs subsequent `## M*` sections). **Strict signal**: only `Step 6 [ ]` / `Step 6 [-]` patterns flag; standalone `(this merge)` is omitted because it commonly appears in past-tense narrative ("merged at SHA (this merge)") and produces false positives. Reuses `TICKET_STATUS` defined in P11; do NOT use `$status` from the P5 loop. NIT severity.
|
|
237
|
+
```bash
|
|
238
|
+
if [ "$TICKET_STATUS" = "Done" ]; then
|
|
239
|
+
MCE_BLOCK=$(awk '
|
|
240
|
+
/^## Merge Checklist Evidence/ { in_mce=1; print; next }
|
|
241
|
+
in_mce && /^## / { in_mce=0 }
|
|
242
|
+
in_mce { print }
|
|
243
|
+
' "$TICKET")
|
|
244
|
+
if echo "$MCE_BLOCK" | grep -qE 'Step 6 \[[ -]\]'; then
|
|
245
|
+
flag "P14 drift (NIT): MCE Action 1 row contains 'Step 6 [ ]' / 'Step 6 [-]' after merge — flip to [x] and append squash + housekeeping SHAs"
|
|
246
|
+
fi
|
|
247
|
+
fi
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**26. P15 — AC with `post-deploy` keyword admitted without Completion Log evidence (added v0.18.3).** ACs containing production-parity keywords (`post-deploy`, `post-merge`, `production parity`, `prod verification`, `on dev API`, `on prod`) are explicit gates; marking them `[x]` without a dated Completion Log row defeats their purpose. Empirical origin: fx F-CATALOG-COV-001 AC-NEW-qa-battery silent-PASS until external audit caught it. Line-safe iteration via `while IFS= read -r`.
|
|
251
|
+
```bash
|
|
252
|
+
COMPLETION=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET")
|
|
253
|
+
AC_LINES=$(grep -nE '^[[:space:]]*-[[:space:]]*\[[ x]\][[:space:]]+AC-[A-Za-z0-9_-]+' "$TICKET" \
|
|
254
|
+
| grep -iE 'post-deploy|post-merge|production parity|prod verification|on dev API|on prod')
|
|
255
|
+
while IFS= read -r line; do
|
|
256
|
+
[ -z "$line" ] && continue
|
|
257
|
+
echo "$line" | grep -q '\[x\]' || continue
|
|
258
|
+
ac_id=$(echo "$line" | grep -oE 'AC-[A-Za-z0-9_-]+' | head -1)
|
|
259
|
+
[ -z "$ac_id" ] && continue
|
|
260
|
+
if ! echo "$COMPLETION" | grep -E "^\|[[:space:]]*[0-9]{4}-[0-9]{2}-[0-9]{2}" | grep -qF "$ac_id"; then
|
|
261
|
+
flag "P15 drift: $ac_id marked [x] with post-deploy semantics but no dated Completion Log entry anchoring this AC-ID"
|
|
262
|
+
fi
|
|
263
|
+
done <<< "$AC_LINES"
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**27. P16 — Feature missing from Features table (added v0.18.3).** Ticket Status `Ready for Merge` / `Done` should have a row in some `## Features — *` table in `product-tracker.md`. **Strict signal**: requires the feature ID to appear as the first cell of a pipe-table row (`| FEATURE_ID |` with optional whitespace), NOT just anywhere in the tracker — narrative mentions or `**Active Feature:**` references must not silence the drift. NIT severity. Reuses `TICKET_STATUS` and `FEATURE_ID` defined in P11.
|
|
267
|
+
```bash
|
|
268
|
+
TRACKER=docs/project_notes/product-tracker.md
|
|
269
|
+
case "$TICKET_STATUS" in
|
|
270
|
+
"Ready for Merge"|"Done")
|
|
271
|
+
if [ -f "$TRACKER" ]; then
|
|
272
|
+
if ! grep -qE "^\|[[:space:]]*$FEATURE_ID[[:space:]]*\|" "$TRACKER"; then
|
|
273
|
+
flag "P16 drift (NIT): $FEATURE_ID not in any Features table row (must appear as first cell in '| FEATURE_ID | ... |' form)"
|
|
274
|
+
fi
|
|
275
|
+
fi
|
|
276
|
+
;;
|
|
277
|
+
esac
|
|
278
|
+
```
|
|
279
|
+
|
|
192
280
|
### Execution discipline (added v0.18.1)
|
|
193
281
|
|
|
194
|
-
For each of the
|
|
282
|
+
For each of the 16 drift checks (P1–P16), if you declare PASS, **include the literal command output** (or its absence — explicit "no rows matched", "extracted: feature/foo", "FROZEN_COUNT=0") as evidence in your report. A bare "PASS" without supporting output is treated as **NOT EXECUTED** by the auditor — re-run with output captured.
|
|
195
283
|
|
|
196
284
|
Recommended pattern:
|
|
197
285
|
|
|
@@ -228,7 +316,7 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
228
316
|
|
|
229
317
|
**STRUCTURAL: READY FOR MERGE** (or **STRUCTURAL: NEEDS FIX — N blockers**)
|
|
230
318
|
|
|
231
|
-
### Drift (12-
|
|
319
|
+
### Drift (12-27) — advisory, refresh before user authorization
|
|
232
320
|
|
|
233
321
|
| # | Pattern | Status | Detail |
|
|
234
322
|
|---|---------|:------:|--------|
|
|
@@ -244,6 +332,10 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
244
332
|
| 21 | P10 Duplicate log rows | PASS | no duplicates |
|
|
245
333
|
| 22 | P11 Tracker status mismatch | PASS | in-progress for Ready for Merge |
|
|
246
334
|
| 23 | P12 Tracker HEAD reference | PASS | tracker HEAD = git HEAD |
|
|
335
|
+
| 24 | P13 key_facts delta mismatch | PASS | N/A — no quantified deltas |
|
|
336
|
+
| 25 | P14 MCE Action 1 stale post-merge | PASS | N/A pre-merge / row past-tense |
|
|
337
|
+
| 26 | P15 Post-deploy AC without evidence | PASS | no post-deploy keyword in ACs |
|
|
338
|
+
| 27 | P16 Feature missing from tracker | PASS | feature in Features table |
|
|
247
339
|
|
|
248
340
|
**DRIFT: CLEAN** (or **DRIFT: N advisories — refresh before merge**)
|
|
249
341
|
|
|
@@ -263,19 +355,23 @@ Fix them directly:
|
|
|
263
355
|
- Merge base diverged → `git merge origin/<target-branch>` and resolve conflicts
|
|
264
356
|
- Data file issues → fix the data
|
|
265
357
|
|
|
266
|
-
**Drift advisories (12-
|
|
358
|
+
**Drift advisories (12-27) fixes:**
|
|
267
359
|
- **P1 (PR body test count stale)** → edit PR body "Quality Gates" / "npm test" line to match ticket terminal count; add "(+N new tests)" delta note
|
|
268
360
|
- **P2 (Aspirational Evidence)** → rewrite `[x]` rows with past-tense text + commit SHA + concrete numbers
|
|
269
361
|
- **P3 (Post-merge action unlogged)** → add a Completion Log row documenting the post-merge execution with date + action + empirical result
|
|
270
362
|
- **P4 (Remote branch orphan)** → `git push origin --delete <branch>` after confirming merge succeeded
|
|
271
363
|
- **P5 (Frozen ticket Status)** → update each ticket's `**Status:**` field from "In Progress"/"Ready for Merge" to `Done`; this often belongs in a docs-only tracker-sync PR if the cycle is retroactive
|
|
272
|
-
- **P6 (AC count off-by-N)** → recount AC items; update the Merge Checklist Evidence row 1 claim
|
|
364
|
+
- **P6 (AC count off-by-N)** → recount AC items; update the Merge Checklist Evidence row 1 claim. Use canonical `AC: <marked>/<total>` form (see `merge-checklist.md`) — `total` includes intentionally-deferred ACs
|
|
273
365
|
- **P7 (Intra-ticket test drift)** → refresh AC / DoD / tracker numbers to match the Completion Log terminal entry
|
|
274
366
|
- **P8 (Completion Log gap)** → add a Completion Log row per missing Step with agent verdict + commit SHA
|
|
275
367
|
- **P9 (Tracker header stale)** → update `**Last Updated:**` line step reference to match Active Feature detail
|
|
276
368
|
- **P10 (Duplicate log rows)** → remove duplicate rows
|
|
277
369
|
- **P11 (Tracker status mismatch)** → sync tracker Features row status to ticket header Status
|
|
278
370
|
- **P12 (Tracker HEAD reference stale)** → update `**Last Updated:**` and `**Active Feature:**` `HEAD: <sha>` tokens to match `git rev-parse HEAD`. Use `git rev-parse --short HEAD` for the 7-char form.
|
|
371
|
+
- **P13 (key_facts delta mismatch)** → reconcile: either correct the ticket's claimed delta (`+N atoms`/`+M aliases`) to match the actual key_facts.md row, or update key_facts.md to match the truthful delta
|
|
372
|
+
- **P14 (MCE Action 1 stale post-merge)** → flip Step 6 checkbox to `[x]` and remove the `(this merge)` qualifier; append squash SHA + housekeeping SHA to the Workflow evidence line
|
|
373
|
+
- **P15 (Post-deploy AC without evidence)** → add a dated Completion Log row anchoring the AC-ID with empirical results from the production verification (QA battery output, dev-API smoke result, telemetry confirmation); OR re-mark the AC `[ ]` until verification lands
|
|
374
|
+
- **P16 (Feature missing from tracker)** → add a row to the relevant `## Features — *` table in `product-tracker.md` (or document explicitly in the ticket why standalone is intentional with a tracker-side note)
|
|
279
375
|
|
|
280
376
|
After fixing, re-run the audit to confirm all checks pass.
|
|
281
377
|
|
|
@@ -72,6 +72,8 @@ In the ticket, fill the `## Merge Checklist Evidence` table. For each action (0
|
|
|
72
72
|
| 0. Validate ticket structure | [x] | Sections verified: Spec, Plan, AC, DoD, Workflow, Log, Evidence |
|
|
73
73
|
| 1. Mark all items | [x] | AC: 12/12, DoD: 7/7, Workflow: 0-5/6 |
|
|
74
74
|
|
|
75
|
+
**Canonical form for the AC count claim:** write `AC: <marked>/<total>` — `marked` is the count of `[x]` Acceptance Criteria, `total` is the count of all AC items including any intentionally deferred `[ ]`. When all are checked use the matching form `AC: N/N` (or the shorthand `all N marked`). The `/audit-merge` P6 drift check parses both forms.
|
|
76
|
+
|
|
75
77
|
## Action 9: Run compliance audit
|
|
76
78
|
|
|
77
79
|
Run `/audit-merge` to verify all compliance checks pass automatically. If any check fails, fix it and re-run until all pass.
|
|
@@ -52,12 +52,21 @@ Run only if `git diff origin/<target-branch>..HEAD --name-only` shows `.json` fi
|
|
|
52
52
|
|
|
53
53
|
Eleven empirically-validated drift patterns. Failures are NOT blockers for the compliance verdict, but MUST be refreshed before requesting user authorization. Use BSD-grep-compatible regex (no `\K`).
|
|
54
54
|
|
|
55
|
-
**12. P1 — PR body test count stale.**
|
|
55
|
+
**12. P1 — PR body test count stale (v0.18.3 multi-workspace extension — C1).** All PR-body ratios must appear in ticket evidence. Subset direction: PR ⊆ ticket. Three fallback cases: (a) ratios on both sides → walk each PR ratio; (b)/(c) missing on either side → emit `P1 N/A`.
|
|
56
56
|
```bash
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
TEST_KW_RE='(npm test|pnpm test|tests?[^|]*[0-9]|[*: ]tests?[*: ]+[0-9])'
|
|
58
|
+
PR_BODY=$(gh pr view --json body -q .body 2>/dev/null || true)
|
|
59
|
+
PR_RATIOS=$(echo "$PR_BODY" | grep -iE "$TEST_KW_RE" | grep -oE "[0-9]+/[0-9]+" | sort -u)
|
|
60
|
+
TICKET_RATIOS=$(grep -iE "$TEST_KW_RE" "$TICKET" | grep -oE "[0-9]+/[0-9]+" | sort -u)
|
|
61
|
+
if [ -z "$PR_RATIOS" ] || [ -z "$TICKET_RATIOS" ]; then
|
|
62
|
+
echo "P1 N/A: no comparable test-count ratios extracted (PR=$(echo "$PR_RATIOS" | tr '\n' ',' ), ticket=$(echo "$TICKET_RATIOS" | tr '\n' ',' ))"
|
|
63
|
+
else
|
|
64
|
+
while IFS= read -r r; do
|
|
65
|
+
[ -z "$r" ] && continue
|
|
66
|
+
echo "$TICKET_RATIOS" | grep -qFx "$r" \
|
|
67
|
+
|| flag "P1 drift: PR ratio $r not found in ticket evidence (ticket ratios: $(echo "$TICKET_RATIOS" | tr '\n' ' '))"
|
|
68
|
+
done <<< "$PR_RATIOS"
|
|
69
|
+
fi
|
|
61
70
|
```
|
|
62
71
|
|
|
63
72
|
**13. P2 — Merge Checklist Evidence aspirational.** `[x]` rows with future-tense text.
|
|
@@ -94,6 +103,8 @@ for t in docs/tickets/*.md; do
|
|
|
94
103
|
status=$(grep -E "^\*\*Status:\*\*" "$t" | head -1 \
|
|
95
104
|
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
96
105
|
| sed -E 's/[[:space:]]*\*?\*?[[:space:]]*\|.*//' \
|
|
106
|
+
| sed -E 's/[[:space:]]+(\(.*\)|—.*|–.*|-.*)$//' \
|
|
107
|
+
| sed -E 's/\*\*[[:space:]]*$//' \
|
|
97
108
|
| sed -E 's/[[:space:]]+$//')
|
|
98
109
|
[ "$status" = "Done" ] && continue
|
|
99
110
|
ticket_id=$(basename "$t" .md | sed -E 's/-[a-z].*//')
|
|
@@ -103,12 +114,27 @@ done
|
|
|
103
114
|
[ "$FROZEN_COUNT" -eq 1 ] && flag "P5 drift: 1 frozen ticket"
|
|
104
115
|
```
|
|
105
116
|
|
|
106
|
-
**17. P6 — AC count off-by-N.**
|
|
117
|
+
**17. P6 — AC count off-by-N.** Two claim forms supported: `all N marked` (N = total) and `AC: X/Y done` (X = marked, Y = total — handles deferred ACs where Y > X).
|
|
107
118
|
```bash
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
AC_BLOCK=$(awk '/^## Acceptance Criteria/,/^## Definition of Done/' "$TICKET")
|
|
120
|
+
ACTUAL_TOTAL=$(echo "$AC_BLOCK" | grep -cE "^- \[[x ]\]")
|
|
121
|
+
ACTUAL_MARKED=$(echo "$AC_BLOCK" | grep -cE "^- \[x\]")
|
|
122
|
+
CLAIM_LINE=$(grep -oE 'all [0-9]+ marked|AC: [0-9]+/[0-9]+' "$TICKET" | head -1)
|
|
123
|
+
if echo "$CLAIM_LINE" | grep -qE '^AC: [0-9]+/[0-9]+'; then
|
|
124
|
+
CLAIMED_MARKED=$(echo "$CLAIM_LINE" | grep -oE '[0-9]+' | head -1)
|
|
125
|
+
CLAIMED_TOTAL=$(echo "$CLAIM_LINE" | grep -oE '[0-9]+' | tail -1)
|
|
126
|
+
[ -n "$CLAIMED_TOTAL" ] && [ "$CLAIMED_TOTAL" != "$ACTUAL_TOTAL" ] \
|
|
127
|
+
&& [ $((ACTUAL_TOTAL - CLAIMED_TOTAL)) -ge 2 -o $((CLAIMED_TOTAL - ACTUAL_TOTAL)) -ge 2 ] \
|
|
128
|
+
&& flag "P6 drift: claim AC total '$CLAIMED_TOTAL' vs actual total $ACTUAL_TOTAL"
|
|
129
|
+
[ -n "$CLAIMED_MARKED" ] && [ "$CLAIMED_MARKED" != "$ACTUAL_MARKED" ] \
|
|
130
|
+
&& [ $((ACTUAL_MARKED - CLAIMED_MARKED)) -ge 2 -o $((CLAIMED_MARKED - ACTUAL_MARKED)) -ge 2 ] \
|
|
131
|
+
&& flag "P6 drift: claim AC marked '$CLAIMED_MARKED' vs actual marked $ACTUAL_MARKED"
|
|
132
|
+
elif [ -n "$CLAIM_LINE" ]; then
|
|
133
|
+
CLAIMED=$(echo "$CLAIM_LINE" | grep -oE '[0-9]+' | head -1)
|
|
134
|
+
[ -n "$CLAIMED" ] && [ "$CLAIMED" != "$ACTUAL_TOTAL" ] \
|
|
135
|
+
&& [ $((ACTUAL_TOTAL - CLAIMED)) -ge 2 -o $((CLAIMED - ACTUAL_TOTAL)) -ge 2 ] \
|
|
136
|
+
&& flag "P6 drift: 'all $CLAIMED marked' vs actual AC count $ACTUAL_TOTAL"
|
|
137
|
+
fi
|
|
112
138
|
```
|
|
113
139
|
|
|
114
140
|
**18. P7 — Test count drift within ticket (final-sections only).**
|
|
@@ -157,6 +183,8 @@ awk -F'|' '/^\| [0-9]{4}-[0-9]{2}-[0-9]{2}/ {
|
|
|
157
183
|
TICKET_STATUS=$(grep -E "^\*\*Status:\*\*" "$TICKET" | head -1 \
|
|
158
184
|
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
159
185
|
| sed -E 's/[[:space:]]*\*?\*?[[:space:]]*\|.*//' \
|
|
186
|
+
| sed -E 's/[[:space:]]+(\(.*\)|—.*|–.*|-.*)$//' \
|
|
187
|
+
| sed -E 's/\*\*[[:space:]]*$//' \
|
|
160
188
|
| sed -E 's/[[:space:]]+$//')
|
|
161
189
|
FEATURE_ID=$(basename "$TICKET" .md | sed -E 's/-[a-z].*//')
|
|
162
190
|
TRACKER_STATUS=$(grep -F "$FEATURE_ID" docs/project_notes/product-tracker.md | grep -oE "\| (in-progress|done|pending|blocked) \|" | head -1 | sed -E 's/\| ([a-z-]+) \|/\1/')
|
|
@@ -189,9 +217,69 @@ if [ -f "$TRACKER" ]; then
|
|
|
189
217
|
fi
|
|
190
218
|
```
|
|
191
219
|
|
|
220
|
+
**24. P13 — key_facts delta vs ticket atom-count mismatch (added v0.18.3).** Whitespace-safe iteration + FEATURE_ID anchoring.
|
|
221
|
+
```bash
|
|
222
|
+
KEY_FACTS=docs/project_notes/key_facts.md
|
|
223
|
+
if [ -f "$KEY_FACTS" ]; then
|
|
224
|
+
FEATURE_ID=$(basename "$TICKET" .md | sed -E 's/-[a-z].*//')
|
|
225
|
+
TICKET_DELTAS=$(grep -oE '\+[0-9]+ (atoms?|aliases?|dishes?|entries|rows)' "$TICKET" | sort -u)
|
|
226
|
+
KEY_FACTS_BLOCK=$(grep -A 3 -F "$FEATURE_ID" "$KEY_FACTS" 2>/dev/null || true)
|
|
227
|
+
while IFS= read -r claim; do
|
|
228
|
+
[ -z "$claim" ] && continue
|
|
229
|
+
if [ -z "$KEY_FACTS_BLOCK" ] || ! echo "$KEY_FACTS_BLOCK" | grep -qF "$claim"; then
|
|
230
|
+
flag "P13 drift: ticket claims '$claim' but key_facts.md block for $FEATURE_ID lacks the same delta"
|
|
231
|
+
fi
|
|
232
|
+
done <<< "$TICKET_DELTAS"
|
|
233
|
+
fi
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**25. P14 — MCE Action 1 row stale post-merge (added v0.18.3).** Strict awk state machine terminates MCE block at NEXT `^## ` of any name (not `[^M]`). Strict signal `Step 6 [ ]` / `Step 6 [-]` only — standalone `(this merge)` omitted to prevent false positives on past-tense narrative. Reuses `TICKET_STATUS` from P11. NIT severity.
|
|
237
|
+
```bash
|
|
238
|
+
if [ "$TICKET_STATUS" = "Done" ]; then
|
|
239
|
+
MCE_BLOCK=$(awk '
|
|
240
|
+
/^## Merge Checklist Evidence/ { in_mce=1; print; next }
|
|
241
|
+
in_mce && /^## / { in_mce=0 }
|
|
242
|
+
in_mce { print }
|
|
243
|
+
' "$TICKET")
|
|
244
|
+
if echo "$MCE_BLOCK" | grep -qE 'Step 6 \[[ -]\]'; then
|
|
245
|
+
flag "P14 drift (NIT): MCE Action 1 row contains 'Step 6 [ ]' / 'Step 6 [-]' after merge — flip to [x] and append squash + housekeeping SHAs"
|
|
246
|
+
fi
|
|
247
|
+
fi
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**26. P15 — AC with post-deploy keyword admitted without Completion Log evidence (added v0.18.3).** Line-safe iteration via `while IFS= read -r`.
|
|
251
|
+
```bash
|
|
252
|
+
COMPLETION=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET")
|
|
253
|
+
AC_LINES=$(grep -nE '^[[:space:]]*-[[:space:]]*\[[ x]\][[:space:]]+AC-[A-Za-z0-9_-]+' "$TICKET" \
|
|
254
|
+
| grep -iE 'post-deploy|post-merge|production parity|prod verification|on dev API|on prod')
|
|
255
|
+
while IFS= read -r line; do
|
|
256
|
+
[ -z "$line" ] && continue
|
|
257
|
+
echo "$line" | grep -q '\[x\]' || continue
|
|
258
|
+
ac_id=$(echo "$line" | grep -oE 'AC-[A-Za-z0-9_-]+' | head -1)
|
|
259
|
+
[ -z "$ac_id" ] && continue
|
|
260
|
+
if ! echo "$COMPLETION" | grep -E "^\|[[:space:]]*[0-9]{4}-[0-9]{2}-[0-9]{2}" | grep -qF "$ac_id"; then
|
|
261
|
+
flag "P15 drift: $ac_id marked [x] with post-deploy semantics but no dated Completion Log entry anchoring this AC-ID"
|
|
262
|
+
fi
|
|
263
|
+
done <<< "$AC_LINES"
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**27. P16 — Feature missing from Features table (added v0.18.3).** Strict signal: feature ID must appear as first cell of a pipe-table row (`| FEATURE_ID |`) — narrative mentions / `**Active Feature:**` references must not silence the drift. NIT severity. Reuses `TICKET_STATUS` and `FEATURE_ID` from P11.
|
|
267
|
+
```bash
|
|
268
|
+
TRACKER=docs/project_notes/product-tracker.md
|
|
269
|
+
case "$TICKET_STATUS" in
|
|
270
|
+
"Ready for Merge"|"Done")
|
|
271
|
+
if [ -f "$TRACKER" ]; then
|
|
272
|
+
if ! grep -qE "^\|[[:space:]]*$FEATURE_ID[[:space:]]*\|" "$TRACKER"; then
|
|
273
|
+
flag "P16 drift (NIT): $FEATURE_ID not in any Features table row (must appear as first cell in '| FEATURE_ID | ... |' form)"
|
|
274
|
+
fi
|
|
275
|
+
fi
|
|
276
|
+
;;
|
|
277
|
+
esac
|
|
278
|
+
```
|
|
279
|
+
|
|
192
280
|
### Execution discipline (added v0.18.1)
|
|
193
281
|
|
|
194
|
-
For each of the
|
|
282
|
+
For each of the 16 drift checks (P1–P16), if you declare PASS, **include the literal command output** (or its absence — explicit "no rows matched", "extracted: feature/foo", "FROZEN_COUNT=0") as evidence in your report. A bare "PASS" without supporting output is treated as **NOT EXECUTED** by the auditor — re-run with output captured.
|
|
195
283
|
|
|
196
284
|
Recommended pattern:
|
|
197
285
|
|
|
@@ -228,7 +316,7 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
228
316
|
|
|
229
317
|
**STRUCTURAL: READY FOR MERGE** (or **STRUCTURAL: NEEDS FIX — N blockers**)
|
|
230
318
|
|
|
231
|
-
### Drift (12-
|
|
319
|
+
### Drift (12-27) — advisory, refresh before user authorization
|
|
232
320
|
|
|
233
321
|
| # | Pattern | Status | Detail |
|
|
234
322
|
|---|---------|:------:|--------|
|
|
@@ -244,6 +332,10 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
244
332
|
| 21 | P10 Duplicate log rows | PASS | no duplicates |
|
|
245
333
|
| 22 | P11 Tracker status mismatch | PASS | status consistent |
|
|
246
334
|
| 23 | P12 Tracker HEAD reference | PASS | tracker HEAD = git HEAD |
|
|
335
|
+
| 24 | P13 key_facts delta mismatch | PASS | N/A — no quantified deltas |
|
|
336
|
+
| 25 | P14 MCE Action 1 stale post-merge | PASS | N/A pre-merge / row past-tense |
|
|
337
|
+
| 26 | P15 Post-deploy AC without evidence | PASS | no post-deploy keyword in ACs |
|
|
338
|
+
| 27 | P16 Feature missing from tracker | PASS | feature in Features table |
|
|
247
339
|
|
|
248
340
|
**DRIFT: CLEAN** (or **DRIFT: N advisories — refresh before merge**)
|
|
249
341
|
|
|
@@ -263,19 +355,23 @@ Fix them directly:
|
|
|
263
355
|
- Merge base diverged → `git merge origin/<target-branch>` and resolve conflicts
|
|
264
356
|
- Data file issues → fix the data
|
|
265
357
|
|
|
266
|
-
**Drift advisories (12-
|
|
358
|
+
**Drift advisories (12-27) fixes:**
|
|
267
359
|
- **P1** → edit PR body npm test line to match ticket terminal count
|
|
268
360
|
- **P2** → rewrite `[x]` rows with past-tense + commit SHA
|
|
269
361
|
- **P3** → add Completion Log row for each post-merge execution
|
|
270
362
|
- **P4** → `git push origin --delete <branch>` after merge
|
|
271
363
|
- **P5** → update ticket Status from "In Progress"/"Ready for Merge" to `Done`
|
|
272
|
-
- **P6** → recount ACs
|
|
364
|
+
- **P6** → recount ACs; use canonical `AC: <marked>/<total>` form (see `merge-checklist.md`) — `total` includes intentionally-deferred ACs
|
|
273
365
|
- **P7** → sync AC/DoD/tracker numbers to Completion Log terminal
|
|
274
366
|
- **P8** → add Completion Log row per missing Step with agent verdict + commit SHA
|
|
275
367
|
- **P9** → refresh `**Last Updated:**` step reference
|
|
276
368
|
- **P10** → remove duplicate rows
|
|
277
369
|
- **P11** → sync tracker Features row status to ticket header Status
|
|
278
370
|
- **P12 (Tracker HEAD reference stale)** → update `**Last Updated:**` and `**Active Feature:**` `HEAD: <sha>` tokens to match `git rev-parse HEAD`. Use `git rev-parse --short HEAD` for the 7-char form.
|
|
371
|
+
- **P13 (key_facts delta mismatch)** → reconcile ticket delta claim with key_facts.md feature row
|
|
372
|
+
- **P14 (MCE Action 1 stale post-merge)** → flip Step 6 checkbox to `[x]` and remove the `(this merge)` qualifier
|
|
373
|
+
- **P15 (Post-deploy AC without evidence)** → add a dated Completion Log row with empirical post-deploy results, OR re-mark the AC `[ ]` until verification lands
|
|
374
|
+
- **P16 (Feature missing from tracker)** → add a row to the relevant `## Features — *` table in `product-tracker.md`
|
|
279
375
|
|
|
280
376
|
After fixing, re-run the audit to confirm all checks pass.
|
|
281
377
|
|
|
@@ -72,6 +72,8 @@ In the ticket, fill the `## Merge Checklist Evidence` table. For each action (0
|
|
|
72
72
|
| 0. Validate ticket structure | [x] | Sections verified: Spec, Plan, AC, DoD, Workflow, Log, Evidence |
|
|
73
73
|
| 1. Mark all items | [x] | AC: 12/12, DoD: 7/7, Workflow: 0-5/6 |
|
|
74
74
|
|
|
75
|
+
**Canonical form for the AC count claim:** write `AC: <marked>/<total>` — `marked` is the count of `[x]` Acceptance Criteria, `total` is the count of all AC items including any intentionally deferred `[ ]`. When all are checked use the matching form `AC: N/N` (or the shorthand `all N marked`). The `/audit-merge` P6 drift check parses both forms.
|
|
76
|
+
|
|
75
77
|
## Action 9: Run compliance audit
|
|
76
78
|
|
|
77
79
|
Run `/audit-merge` to verify all compliance checks pass automatically. If any check fails, fix it and re-run until all pass.
|