create-sdd-project 0.18.0 → 0.18.1
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/lib/meta.js
CHANGED
|
@@ -281,6 +281,44 @@ function expectedSmartDiffTrackedPaths(aiTools, projectType) {
|
|
|
281
281
|
paths.add(`${dir}/skills/development-workflow/references/merge-checklist.md`);
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
// v0.18.1: shipped slash-commands — preserve user customizations across
|
|
285
|
+
// upgrades. Closes v0.18.0 known limitation where audit-merge.md was
|
|
286
|
+
// wholesale-overwritten (notable since teams may tune drift recipes for
|
|
287
|
+
// their PR/ticket conventions). Same hash decision tree as agents +
|
|
288
|
+
// standards: missing/force → write, hash match → replace, hash mismatch
|
|
289
|
+
// → preserve + .new backup, no hash → fallback content compare.
|
|
290
|
+
const COMMAND_FILES_CLAUDE = [
|
|
291
|
+
'audit-merge.md',
|
|
292
|
+
'context-prompt.md',
|
|
293
|
+
'review-plan.md',
|
|
294
|
+
'review-project.md',
|
|
295
|
+
'review-spec.md',
|
|
296
|
+
];
|
|
297
|
+
const COMMAND_FILES_GEMINI = [
|
|
298
|
+
// Each Claude command has a Gemini twin: a thin TOML wrapper + a body
|
|
299
|
+
// -instructions.md. Both must be tracked since users may edit either.
|
|
300
|
+
'audit-merge.toml',
|
|
301
|
+
'audit-merge-instructions.md',
|
|
302
|
+
'context-prompt.toml',
|
|
303
|
+
'context-prompt-instructions.md',
|
|
304
|
+
'review-plan.toml',
|
|
305
|
+
'review-plan-instructions.md',
|
|
306
|
+
'review-project.toml',
|
|
307
|
+
'review-project-instructions.md',
|
|
308
|
+
'review-spec.toml',
|
|
309
|
+
'review-spec-instructions.md',
|
|
310
|
+
];
|
|
311
|
+
if (aiTools !== 'gemini') {
|
|
312
|
+
for (const cmd of COMMAND_FILES_CLAUDE) {
|
|
313
|
+
paths.add(`.claude/commands/${cmd}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (aiTools !== 'claude') {
|
|
317
|
+
for (const cmd of COMMAND_FILES_GEMINI) {
|
|
318
|
+
paths.add(`.gemini/commands/${cmd}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
284
322
|
return paths;
|
|
285
323
|
}
|
|
286
324
|
|
package/package.json
CHANGED
|
@@ -55,14 +55,14 @@ Eleven empirically-validated drift patterns. Failures are NOT blockers for the c
|
|
|
55
55
|
**12. P1 — PR body test count stale.** The PR body's "npm test" line should match the terminal test count in the ticket (AC / DoD / Completion Log last entry). Agents commonly open the PR at Step 4 and add tests during Step 5 review — the PR body number becomes stale.
|
|
56
56
|
```bash
|
|
57
57
|
PR_BODY=$(gh pr view --json body -q .body)
|
|
58
|
-
PR_TESTS=$(echo "$PR_BODY" | grep -iE "(npm test|tests
|
|
59
|
-
TICKET_TESTS=$(grep -iE "(npm test|tests
|
|
58
|
+
PR_TESTS=$(echo "$PR_BODY" | grep -iE "(npm test|tests?[^|]*[0-9]|[*: ]tests?[*: ]+[0-9])" | grep -oE "[0-9]+/[0-9]+" | head -1)
|
|
59
|
+
TICKET_TESTS=$(grep -iE "(npm test|tests?[^|]*[0-9]|[*: ]tests?[*: ]+[0-9])" "$TICKET" | grep -oE "[0-9]+/[0-9]+" | tail -1)
|
|
60
60
|
[ -n "$PR_TESTS" ] && [ -n "$TICKET_TESTS" ] && [ "$PR_TESTS" != "$TICKET_TESTS" ] && flag "P1 drift: PR body $PR_TESTS vs ticket $TICKET_TESTS"
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
**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.
|
|
64
64
|
```bash
|
|
65
|
-
awk '/^## Merge Checklist Evidence
|
|
65
|
+
awk '/^## Merge Checklist Evidence/{flag=1; next} /^## /{flag=0} flag' "$TICKET" \
|
|
66
66
|
| grep -E '^\|.*\[x\].*(to be |will |pending|TBD|Will be |to be created|next commit|aspirational)' \
|
|
67
67
|
&& flag "P2 drift: aspirational row(s) found"
|
|
68
68
|
```
|
|
@@ -82,7 +82,7 @@ done < /tmp/pm_items.txt
|
|
|
82
82
|
|
|
83
83
|
**15. P4 — Remote branch orphan after "deleted".** Workflow Step 6 claims `[x] branch deleted` but origin still has the branch.
|
|
84
84
|
```bash
|
|
85
|
-
BRANCH=$(grep -
|
|
85
|
+
BRANCH=$(grep -oE '\*\*[Bb]ranch:\*\*[[:space:]]*[^[:space:]|()]+' "$TICKET" | head -1 | sed -E 's/^\*\*[Bb]ranch:\*\*[[:space:]]*//')
|
|
86
86
|
git fetch origin --prune --quiet
|
|
87
87
|
git ls-remote --heads origin "$BRANCH" 2>/dev/null | grep -q refs/heads && flag "P4 drift: remote branch $BRANCH still exists (run: git push origin --delete $BRANCH)"
|
|
88
88
|
```
|
|
@@ -91,7 +91,10 @@ git ls-remote --heads origin "$BRANCH" 2>/dev/null | grep -q refs/heads && flag
|
|
|
91
91
|
```bash
|
|
92
92
|
FROZEN_COUNT=0
|
|
93
93
|
for t in docs/tickets/*.md; do
|
|
94
|
-
status=$(grep -E "^\*\*Status:\*\*" "$t" | head -1
|
|
94
|
+
status=$(grep -E "^\*\*Status:\*\*" "$t" | head -1 \
|
|
95
|
+
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
96
|
+
| sed -E 's/[[:space:]]*\*?\*?[[:space:]]*\|.*//' \
|
|
97
|
+
| sed -E 's/[[:space:]]+$//')
|
|
95
98
|
[ "$status" = "Done" ] && continue
|
|
96
99
|
ticket_id=$(basename "$t" .md | sed -E 's/-[a-z].*//')
|
|
97
100
|
git log --all --oneline --grep="$ticket_id" | grep -q . && FROZEN_COUNT=$((FROZEN_COUNT+1))
|
|
@@ -126,15 +129,15 @@ COMPLETION=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET")
|
|
|
126
129
|
CHECKED_STEPS=$(echo "$WORKFLOW" | grep -E "^- \[x\] Step [0-9]+:" | sed -E 's/^- \[x\] Step ([0-9]+):.*/\1/' | sort -u)
|
|
127
130
|
while read -r step_num; do
|
|
128
131
|
[ -z "$step_num" ] && continue
|
|
129
|
-
echo "$COMPLETION" | grep -qE "Step[[:space:]]+$step_num([^0-9]|$)" || flag "P8 drift: Step $step_num [x] but no Completion Log entry"
|
|
132
|
+
echo "$COMPLETION" | grep -qE "^\|[^|]*\|[[:space:]]*Step[[:space:]]+$step_num([^0-9]|$)" || flag "P8 drift: Step $step_num [x] but no dedicated Completion Log entry"
|
|
130
133
|
done <<< "$CHECKED_STEPS"
|
|
131
134
|
```
|
|
132
135
|
|
|
133
136
|
**20. P9 — Tracker header "Last Updated" stale.** The `**Last Updated:**` header and the `**Active Feature:**` detail should agree on step number (e.g., both say 5/6). Mismatch suggests the header wasn't refreshed after state transitions.
|
|
134
137
|
```bash
|
|
135
138
|
TRACKER=docs/project_notes/product-tracker.md
|
|
136
|
-
HEADER_STEP=$(grep -oE 'Step [0-9]+/6'
|
|
137
|
-
DETAIL_STEP=$(grep -A 1 '^\*\*Active Feature:\*\*' "$TRACKER" | grep -oE 'Step [0-9]+/6' | head -1)
|
|
139
|
+
HEADER_STEP=$(grep '^\*\*Last Updated:\*\*' "$TRACKER" | grep -oE '(Step )?[0-9]+/6' | head -1 | sed -E 's/^Step //')
|
|
140
|
+
DETAIL_STEP=$(grep -A 1 '^\*\*Active Feature:\*\*' "$TRACKER" | grep -oE '(Step )?[0-9]+/6' | head -1 | sed -E 's/^Step //')
|
|
138
141
|
[ -n "$HEADER_STEP" ] && [ -n "$DETAIL_STEP" ] && [ "$HEADER_STEP" != "$DETAIL_STEP" ] \
|
|
139
142
|
&& flag "P9 drift: tracker header says $HEADER_STEP, Active Feature says $DETAIL_STEP"
|
|
140
143
|
```
|
|
@@ -151,7 +154,10 @@ awk -F'|' '/^\| [0-9]{4}-[0-9]{2}-[0-9]{2}/ {
|
|
|
151
154
|
|
|
152
155
|
**22. P11 — Tracker Features table status vs ticket Status mismatch.** Ticket Status=Ready for Merge / Review → tracker expects `in-progress`. Ticket Status=Done → tracker expects `done`. Mismatch means one side wasn't updated after the state change.
|
|
153
156
|
```bash
|
|
154
|
-
TICKET_STATUS=$(grep -E "^\*\*Status:\*\*" "$TICKET" | head -1
|
|
157
|
+
TICKET_STATUS=$(grep -E "^\*\*Status:\*\*" "$TICKET" | head -1 \
|
|
158
|
+
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
159
|
+
| sed -E 's/[[:space:]]*\*?\*?[[:space:]]*\|.*//' \
|
|
160
|
+
| sed -E 's/[[:space:]]+$//')
|
|
155
161
|
FEATURE_ID=$(basename "$TICKET" .md | sed -E 's/-[a-z].*//')
|
|
156
162
|
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/')
|
|
157
163
|
case "$TICKET_STATUS" in
|
|
@@ -163,6 +169,20 @@ esac
|
|
|
163
169
|
&& flag "P11 drift: ticket Status='$TICKET_STATUS' expects tracker='$EXPECTED' but tracker='$TRACKER_STATUS'"
|
|
164
170
|
```
|
|
165
171
|
|
|
172
|
+
### Execution discipline (added v0.18.1)
|
|
173
|
+
|
|
174
|
+
For each of the 11 drift checks (P1–P11), 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.
|
|
175
|
+
|
|
176
|
+
Recommended pattern:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
P1 PR body test count stale | PASS | PR_TESTS=4110/4110, TICKET_TESTS=4110/4110 (matched)
|
|
180
|
+
P2 Aspirational rows | PASS | awk … | grep … (no rows matched)
|
|
181
|
+
P5 Frozen ticket Status | PASS | FROZEN_COUNT=0
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
This prevents two failure modes empirically observed during the v0.18.1 origin audit (fx F-H9 + F-H10): (a) the agent abbreviates execution and reports CLEAN by inference from MEMORY/design knowledge; (b) buggy recipes return empty output silently and the agent treats empty as PASS without verifying the recipe ran. Both are caught when literal output is required.
|
|
185
|
+
|
|
166
186
|
### Output Format
|
|
167
187
|
|
|
168
188
|
Report two tables — one for **structural (blocking)** compliance, one for **drift (advisory)**. Emit two verdicts plus a combined summary line.
|
|
@@ -55,48 +55,52 @@ Eleven empirically-validated drift patterns. Failures are NOT blockers for the c
|
|
|
55
55
|
**12. P1 — PR body test count stale.** Ratio must co-occur with test/pass/green marker to avoid AC/DoD ratios (14/14, 7/7).
|
|
56
56
|
```bash
|
|
57
57
|
PR_BODY=$(gh pr view --json body -q .body)
|
|
58
|
-
PR_TESTS=$(echo "$PR_BODY" | grep -iE "(npm test|tests
|
|
59
|
-
TICKET_TESTS=$(grep -iE "(npm test|tests
|
|
60
|
-
[ -n "$PR_TESTS" ] && [ -n "$TICKET_TESTS" ] && [ "$PR_TESTS" != "$TICKET_TESTS" ] && flag "P1: PR body $PR_TESTS vs ticket $TICKET_TESTS"
|
|
58
|
+
PR_TESTS=$(echo "$PR_BODY" | grep -iE "(npm test|tests?[^|]*[0-9]|[*: ]tests?[*: ]+[0-9])" | grep -oE "[0-9]+/[0-9]+" | head -1)
|
|
59
|
+
TICKET_TESTS=$(grep -iE "(npm test|tests?[^|]*[0-9]|[*: ]tests?[*: ]+[0-9])" "$TICKET" | grep -oE "[0-9]+/[0-9]+" | tail -1)
|
|
60
|
+
[ -n "$PR_TESTS" ] && [ -n "$TICKET_TESTS" ] && [ "$PR_TESTS" != "$TICKET_TESTS" ] && flag "P1 drift: PR body $PR_TESTS vs ticket $TICKET_TESTS"
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
**13. P2 — Merge Checklist Evidence aspirational.** `[x]` rows with future-tense text.
|
|
64
64
|
```bash
|
|
65
|
-
awk '/^## Merge Checklist Evidence
|
|
65
|
+
awk '/^## Merge Checklist Evidence/{flag=1; next} /^## /{flag=0} flag' "$TICKET" \
|
|
66
66
|
| grep -E '^\|.*\[x\].*(to be |will |pending|TBD|Will be |to be created|next commit|aspirational)' \
|
|
67
|
-
&& flag "P2: aspirational row(s)"
|
|
67
|
+
&& flag "P2 drift: aspirational row(s) found"
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
**14. P3 — Post-merge actions not logged** (post-merge only).
|
|
71
71
|
```bash
|
|
72
|
+
# Strip checkbox prefix before comparison; use grep -Fq fixed-string match.
|
|
72
73
|
grep -E "^- \[ \].*(post-merge|operator|prod rollout|pending verification)" "$TICKET" \
|
|
73
74
|
| sed -E 's/^- \[ \] //' > /tmp/pm_items.txt
|
|
74
75
|
COMPLETION=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET")
|
|
75
76
|
while IFS= read -r item; do
|
|
76
77
|
[ -z "$item" ] && continue
|
|
77
78
|
KEY=$(echo "$item" | cut -c1-40)
|
|
78
|
-
echo "$COMPLETION" | grep -Fq "$KEY" || flag "P3: '$item' not
|
|
79
|
+
echo "$COMPLETION" | grep -Fq "$KEY" || flag "P3 drift: post-merge '$item' not in Completion Log"
|
|
79
80
|
done < /tmp/pm_items.txt
|
|
80
81
|
```
|
|
81
82
|
|
|
82
83
|
**15. P4 — Remote branch orphan.**
|
|
83
84
|
```bash
|
|
84
|
-
BRANCH=$(grep -
|
|
85
|
+
BRANCH=$(grep -oE '\*\*[Bb]ranch:\*\*[[:space:]]*[^[:space:]|()]+' "$TICKET" | head -1 | sed -E 's/^\*\*[Bb]ranch:\*\*[[:space:]]*//')
|
|
85
86
|
git fetch origin --prune --quiet
|
|
86
|
-
git ls-remote --heads origin "$BRANCH" 2>/dev/null | grep -q refs/heads && flag "P4: branch $BRANCH still
|
|
87
|
+
git ls-remote --heads origin "$BRANCH" 2>/dev/null | grep -q refs/heads && flag "P4 drift: remote branch $BRANCH still exists (run: git push origin --delete $BRANCH)"
|
|
87
88
|
```
|
|
88
89
|
|
|
89
90
|
**16. P5 — Frozen ticket Status post-merge.** Multi-word status via sed char class, not `\w+`.
|
|
90
91
|
```bash
|
|
91
92
|
FROZEN_COUNT=0
|
|
92
93
|
for t in docs/tickets/*.md; do
|
|
93
|
-
status=$(grep -E "^\*\*Status:\*\*" "$t" | head -1
|
|
94
|
+
status=$(grep -E "^\*\*Status:\*\*" "$t" | head -1 \
|
|
95
|
+
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
96
|
+
| sed -E 's/[[:space:]]*\*?\*?[[:space:]]*\|.*//' \
|
|
97
|
+
| sed -E 's/[[:space:]]+$//')
|
|
94
98
|
[ "$status" = "Done" ] && continue
|
|
95
99
|
ticket_id=$(basename "$t" .md | sed -E 's/-[a-z].*//')
|
|
96
100
|
git log --all --oneline --grep="$ticket_id" | grep -q . && FROZEN_COUNT=$((FROZEN_COUNT+1))
|
|
97
101
|
done
|
|
98
|
-
[ "$FROZEN_COUNT" -ge 2 ] && flag "P5 SYSTEMIC: $FROZEN_COUNT frozen tickets"
|
|
99
|
-
[ "$FROZEN_COUNT" -eq 1 ] && flag "P5: 1 frozen ticket"
|
|
102
|
+
[ "$FROZEN_COUNT" -ge 2 ] && flag "P5 drift (SYSTEMIC): $FROZEN_COUNT frozen tickets — Status not updated post-merge"
|
|
103
|
+
[ "$FROZEN_COUNT" -eq 1 ] && flag "P5 drift: 1 frozen ticket"
|
|
100
104
|
```
|
|
101
105
|
|
|
102
106
|
**17. P6 — AC count off-by-N.**
|
|
@@ -104,7 +108,7 @@ done
|
|
|
104
108
|
ACTUAL=$(awk '/^## Acceptance Criteria/,/^## Definition of Done/' "$TICKET" | grep -cE "^- \[[x ]\]")
|
|
105
109
|
CLAIMED=$(grep -oE 'all [0-9]+ marked|AC: [0-9]+/[0-9]+' "$TICKET" | head -1 | grep -oE "[0-9]+" | head -1)
|
|
106
110
|
[ -n "$CLAIMED" ] && [ "$CLAIMED" != "$ACTUAL" ] && [ $((ACTUAL - CLAIMED)) -ge 2 -o $((CLAIMED - ACTUAL)) -ge 2 ] \
|
|
107
|
-
&& flag "P6: claim $CLAIMED vs actual $ACTUAL"
|
|
111
|
+
&& flag "P6 drift: claim '$CLAIMED' vs actual AC count $ACTUAL"
|
|
108
112
|
```
|
|
109
113
|
|
|
110
114
|
**18. P7 — Test count drift within ticket (final-sections only).**
|
|
@@ -112,8 +116,9 @@ CLAIMED=$(grep -oE 'all [0-9]+ marked|AC: [0-9]+/[0-9]+' "$TICKET" | head -1 | g
|
|
|
112
116
|
TERMINAL=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET" | grep -iE "(test|pass|green)" | grep -oE "[0-9]+/[0-9]+" | tail -1)
|
|
113
117
|
AC=$(awk '/^## Acceptance Criteria/,/^## Definition of Done/' "$TICKET")
|
|
114
118
|
DOD=$(awk '/^## Definition of Done/,/^## Workflow Checklist/' "$TICKET")
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
FINAL_NUMS=$(printf '%s\n%s\n' "$AC" "$DOD" | grep -iE "(test|pass|green)" | grep -oE "[0-9]+/[0-9]+" | sort -u)
|
|
120
|
+
for n in $FINAL_NUMS; do
|
|
121
|
+
[ -n "$TERMINAL" ] && [ "$n" != "$TERMINAL" ] && flag "P7 drift: final-section count $n vs terminal $TERMINAL"
|
|
117
122
|
done
|
|
118
123
|
```
|
|
119
124
|
|
|
@@ -124,17 +129,17 @@ COMPLETION=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET")
|
|
|
124
129
|
CHECKED_STEPS=$(echo "$WORKFLOW" | grep -E "^- \[x\] Step [0-9]+:" | sed -E 's/^- \[x\] Step ([0-9]+):.*/\1/' | sort -u)
|
|
125
130
|
while read -r step_num; do
|
|
126
131
|
[ -z "$step_num" ] && continue
|
|
127
|
-
echo "$COMPLETION" | grep -qE "Step[[:space:]]+$step_num([^0-9]|$)" || flag "P8: Step $step_num [x] but no
|
|
132
|
+
echo "$COMPLETION" | grep -qE "^\|[^|]*\|[[:space:]]*Step[[:space:]]+$step_num([^0-9]|$)" || flag "P8 drift: Step $step_num [x] but no dedicated Completion Log entry"
|
|
128
133
|
done <<< "$CHECKED_STEPS"
|
|
129
134
|
```
|
|
130
135
|
|
|
131
136
|
**20. P9 — Tracker header stale.**
|
|
132
137
|
```bash
|
|
133
138
|
TRACKER=docs/project_notes/product-tracker.md
|
|
134
|
-
HEADER_STEP=$(grep -oE 'Step [0-9]+/6'
|
|
135
|
-
DETAIL_STEP=$(grep -A 1 '^\*\*Active Feature:\*\*' "$TRACKER" | grep -oE 'Step [0-9]+/6' | head -1)
|
|
139
|
+
HEADER_STEP=$(grep '^\*\*Last Updated:\*\*' "$TRACKER" | grep -oE '(Step )?[0-9]+/6' | head -1 | sed -E 's/^Step //')
|
|
140
|
+
DETAIL_STEP=$(grep -A 1 '^\*\*Active Feature:\*\*' "$TRACKER" | grep -oE '(Step )?[0-9]+/6' | head -1 | sed -E 's/^Step //')
|
|
136
141
|
[ -n "$HEADER_STEP" ] && [ -n "$DETAIL_STEP" ] && [ "$HEADER_STEP" != "$DETAIL_STEP" ] \
|
|
137
|
-
&& flag "P9: header $HEADER_STEP
|
|
142
|
+
&& flag "P9 drift: tracker header says $HEADER_STEP, Active Feature says $DETAIL_STEP"
|
|
138
143
|
```
|
|
139
144
|
|
|
140
145
|
**21. P10 — Duplicate Completion Log rows.**
|
|
@@ -143,12 +148,16 @@ awk -F'|' '/^\| [0-9]{4}-[0-9]{2}-[0-9]{2}/ {
|
|
|
143
148
|
key = $2 "|" $3 "|" substr($4, 1, 80)
|
|
144
149
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
|
|
145
150
|
print key
|
|
146
|
-
}' "$TICKET" | sort | uniq -d
|
|
151
|
+
}' "$TICKET" | sort | uniq -d \
|
|
152
|
+
| while read -r dup; do flag "P10 drift: duplicate Completion Log row: $dup"; done
|
|
147
153
|
```
|
|
148
154
|
|
|
149
155
|
**22. P11 — Tracker Features table status vs ticket Status mismatch.**
|
|
150
156
|
```bash
|
|
151
|
-
TICKET_STATUS=$(grep -E "^\*\*Status:\*\*" "$TICKET" | head -1
|
|
157
|
+
TICKET_STATUS=$(grep -E "^\*\*Status:\*\*" "$TICKET" | head -1 \
|
|
158
|
+
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
159
|
+
| sed -E 's/[[:space:]]*\*?\*?[[:space:]]*\|.*//' \
|
|
160
|
+
| sed -E 's/[[:space:]]+$//')
|
|
152
161
|
FEATURE_ID=$(basename "$TICKET" .md | sed -E 's/-[a-z].*//')
|
|
153
162
|
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/')
|
|
154
163
|
case "$TICKET_STATUS" in
|
|
@@ -157,9 +166,23 @@ case "$TICKET_STATUS" in
|
|
|
157
166
|
*) EXPECTED="" ;;
|
|
158
167
|
esac
|
|
159
168
|
[ -n "$EXPECTED" ] && [ -n "$TRACKER_STATUS" ] && [ "$TRACKER_STATUS" != "$EXPECTED" ] \
|
|
160
|
-
&& flag "P11: Status='$TICKET_STATUS' expects tracker='$EXPECTED' but tracker='$TRACKER_STATUS'"
|
|
169
|
+
&& flag "P11 drift: ticket Status='$TICKET_STATUS' expects tracker='$EXPECTED' but tracker='$TRACKER_STATUS'"
|
|
161
170
|
```
|
|
162
171
|
|
|
172
|
+
### Execution discipline (added v0.18.1)
|
|
173
|
+
|
|
174
|
+
For each of the 11 drift checks (P1–P11), 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.
|
|
175
|
+
|
|
176
|
+
Recommended pattern:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
P1 PR body test count stale | PASS | PR_TESTS=4110/4110, TICKET_TESTS=4110/4110 (matched)
|
|
180
|
+
P2 Aspirational rows | PASS | awk … | grep … (no rows matched)
|
|
181
|
+
P5 Frozen ticket Status | PASS | FROZEN_COUNT=0
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
This prevents two failure modes empirically observed during the v0.18.1 origin audit (fx F-H9 + F-H10): (a) the agent abbreviates execution and reports CLEAN by inference from MEMORY/design knowledge; (b) buggy recipes return empty output silently and the agent treats empty as PASS without verifying the recipe ran. Both are caught when literal output is required.
|
|
185
|
+
|
|
163
186
|
### Output Format
|
|
164
187
|
|
|
165
188
|
Report two tables — one for **structural (blocking)** compliance, one for **drift (advisory)**. Emit two verdicts plus a combined summary line.
|