create-sdd-project 0.19.0 → 0.20.0
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/README.md +2 -2
- package/lib/doctor.js +90 -0
- package/lib/upgrade-generator.js +30 -0
- package/package.json +1 -1
- package/template/.claude/commands/audit-merge.md +139 -37
- package/template/.claude/skills/development-workflow/references/merge-checklist.md +25 -4
- package/template/.claude/skills/development-workflow/references/ticket-template.md +12 -0
- package/template/.claude/skills/pm-orchestrator/SKILL.md +19 -0
- package/template/.claude/skills/pm-orchestrator/references/pm-session-template.md +2 -0
- package/template/.gemini/commands/audit-merge-instructions.md +139 -37
- package/template/.gemini/skills/development-workflow/references/merge-checklist.md +25 -4
- package/template/.gemini/skills/development-workflow/references/ticket-template.md +12 -0
- package/template/.gemini/skills/pm-orchestrator/SKILL.md +19 -0
- package/template/.gemini/skills/pm-orchestrator/references/pm-session-template.md +2 -0
package/README.md
CHANGED
|
@@ -312,7 +312,7 @@ SDD DevFlow combines three proven practices:
|
|
|
312
312
|
| `/review-plan` | Cross-model plan review — runs automatically after plan self-review when external CLIs are available; catches implementation blind spots |
|
|
313
313
|
| `/context-prompt` | Generates a context recovery prompt after `/compact` with Workflow Recovery to prevent checkpoint skipping |
|
|
314
314
|
| `/review-project` | Comprehensive project-level review using up to 3 AI models in parallel — 6 domains, audit context, consolidated report with action plan |
|
|
315
|
-
| `/audit-merge` | Automated compliance audit —
|
|
315
|
+
| `/audit-merge` | Automated compliance audit — 13 structural pre-merge checks (ticket, tracker, evidence, merge base, working tree, data files, CI state via `gh pr checks`, MCE Action 9 + Audit Merge Output) + 17 advisory drift patterns. Auto-fixes issues. |
|
|
316
316
|
|
|
317
317
|
### Spec & Plan Quality
|
|
318
318
|
|
|
@@ -372,7 +372,7 @@ Classify complexity for each (Simple / Standard / Complex):
|
|
|
372
372
|
|
|
373
373
|
After classification, the loop runs autonomously: spec → plan → implement → review → merge for each feature, with all quality gates enforced.
|
|
374
374
|
|
|
375
|
-
**
|
|
375
|
+
**11 guardrails** prevent runaway execution: max 5 features/session, mandatory compact after 2 features, target-branch baseline check, intra-batch dependency check, circuit breaker (3 failures → stop), kill switch (`stop pm`), session lock, post-merge sanity check (`npm test`), rolling batch (1-3 features at a time), clean workspace validation, quality gates always on (`/audit-merge` included).
|
|
376
376
|
|
|
377
377
|
**Recovery**: after `/compact` or terminal restart, run `continue pm` — the session state in `pm-session.md` tracks exactly where you left off.
|
|
378
378
|
|
package/lib/doctor.js
CHANGED
|
@@ -82,6 +82,9 @@ function runDoctor(cwd) {
|
|
|
82
82
|
// 15. .sdd-meta.json structural integrity (v0.17.0)
|
|
83
83
|
results.push(checkMetaJson(cwd, aiTools, projectType));
|
|
84
84
|
|
|
85
|
+
// 16. PM session coherence (v0.20.0) — pm-session.md SDD version vs current
|
|
86
|
+
results.push(checkSessionCoherence(cwd));
|
|
87
|
+
|
|
85
88
|
return results;
|
|
86
89
|
}
|
|
87
90
|
|
|
@@ -1179,7 +1182,94 @@ function checkMetaJson(cwd, aiTools, projectType) {
|
|
|
1179
1182
|
};
|
|
1180
1183
|
}
|
|
1181
1184
|
|
|
1185
|
+
/**
|
|
1186
|
+
* checkSessionCoherence — v0.20.0 Item B (PM session SDD-version drift).
|
|
1187
|
+
*
|
|
1188
|
+
* If a PM session is in-progress AND its recorded "SDD version at start"
|
|
1189
|
+
* differs from the currently-installed .sdd-version, emit WARN. The cached
|
|
1190
|
+
* PM session context (system prompt + skill references loaded at session
|
|
1191
|
+
* start) does NOT auto-refresh on SDD upgrade — only /compact reloads.
|
|
1192
|
+
*
|
|
1193
|
+
* Returns PASS if:
|
|
1194
|
+
* - No pm-session.md (no active session to check).
|
|
1195
|
+
* - pm-session.md has Status: done (terminated session).
|
|
1196
|
+
* - SDD version at start matches current .sdd-version.
|
|
1197
|
+
*
|
|
1198
|
+
* Returns WARN if:
|
|
1199
|
+
* - pm-session.md Status: in-progress AND versions differ.
|
|
1200
|
+
*
|
|
1201
|
+
* Returns PASS (N/A note) if pm-session.md exists but lacks the field
|
|
1202
|
+
* (legacy session pre-v0.20.0; no actionable signal).
|
|
1203
|
+
*/
|
|
1204
|
+
function checkSessionCoherence(cwd) {
|
|
1205
|
+
const pmSessionPath = path.join(cwd, 'docs', 'project_notes', 'pm-session.md');
|
|
1206
|
+
const sddVersionPath = path.join(cwd, '.sdd-version');
|
|
1207
|
+
|
|
1208
|
+
if (!fs.existsSync(pmSessionPath)) {
|
|
1209
|
+
return {
|
|
1210
|
+
status: PASS,
|
|
1211
|
+
message: 'PM session: none active (N/A)',
|
|
1212
|
+
details: [],
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
if (!fs.existsSync(sddVersionPath)) {
|
|
1217
|
+
// SDD not installed cleanly; let earlier checks flag this.
|
|
1218
|
+
return {
|
|
1219
|
+
status: PASS,
|
|
1220
|
+
message: 'PM session coherence: N/A (no .sdd-version)',
|
|
1221
|
+
details: [],
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
const sessionContent = fs.readFileSync(pmSessionPath, 'utf8');
|
|
1226
|
+
const currentVersion = fs.readFileSync(sddVersionPath, 'utf8').trim();
|
|
1227
|
+
|
|
1228
|
+
const statusMatch = sessionContent.match(/^\*\*Status:\*\*\s*(\S+)/m);
|
|
1229
|
+
if (!statusMatch || statusMatch[1].toLowerCase() !== 'in-progress') {
|
|
1230
|
+
return {
|
|
1231
|
+
status: PASS,
|
|
1232
|
+
message: 'PM session: not in-progress (N/A)',
|
|
1233
|
+
details: [],
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
const versionFieldMatch = sessionContent.match(
|
|
1238
|
+
/^\*\*SDD version at start:\*\*\s*(\S+)/m,
|
|
1239
|
+
);
|
|
1240
|
+
if (!versionFieldMatch) {
|
|
1241
|
+
return {
|
|
1242
|
+
status: PASS,
|
|
1243
|
+
message: 'PM session coherence: legacy session (no version field; v0.20.0+ records it)',
|
|
1244
|
+
details: [
|
|
1245
|
+
'Pre-v0.20.0 PM sessions did not record SDD version at start.',
|
|
1246
|
+
'Drift detection unavailable for this session; consider /compact + continue pm to refresh.',
|
|
1247
|
+
],
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
const startVersion = versionFieldMatch[1];
|
|
1252
|
+
if (startVersion === currentVersion) {
|
|
1253
|
+
return {
|
|
1254
|
+
status: PASS,
|
|
1255
|
+
message: `PM session coherence: in sync (SDD ${currentVersion})`,
|
|
1256
|
+
details: [],
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
return {
|
|
1261
|
+
status: WARN,
|
|
1262
|
+
message: `PM session coherence: SDD drift detected — session started under ${startVersion}, current ${currentVersion}`,
|
|
1263
|
+
details: [
|
|
1264
|
+
'Cached workflow references in this session may be stale.',
|
|
1265
|
+
'Recommendation: run `/compact` then `continue pm` to reload templates.',
|
|
1266
|
+
'See pm-orchestrator/SKILL.md "Session-coherence check" for details.',
|
|
1267
|
+
],
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1182
1271
|
module.exports = {
|
|
1183
1272
|
runDoctor,
|
|
1184
1273
|
printResults,
|
|
1274
|
+
checkSessionCoherence,
|
|
1185
1275
|
};
|
package/lib/upgrade-generator.js
CHANGED
|
@@ -1329,6 +1329,36 @@ function generateUpgrade(config) {
|
|
|
1329
1329
|
);
|
|
1330
1330
|
}
|
|
1331
1331
|
|
|
1332
|
+
// v0.20.0 Item B — upgrade-time PM session coherence advisory.
|
|
1333
|
+
// If a pm-session.md is in-progress, the agent's cached context (loaded at
|
|
1334
|
+
// session start under the prior SDD version) survives the upgrade in memory.
|
|
1335
|
+
// Recommend /compact + continue pm to reload templates before next Step.
|
|
1336
|
+
const pmSessionPath = path.join(config.projectDir, 'docs', 'project_notes', 'pm-session.md');
|
|
1337
|
+
if (fs.existsSync(pmSessionPath)) {
|
|
1338
|
+
try {
|
|
1339
|
+
const sessionContent = fs.readFileSync(pmSessionPath, 'utf8');
|
|
1340
|
+
const statusMatch = sessionContent.match(/^\*\*Status:\*\*\s*(\S+)/m);
|
|
1341
|
+
if (statusMatch && statusMatch[1].toLowerCase() === 'in-progress') {
|
|
1342
|
+
console.log('\n ⚠ PM SESSION COHERENCE (v0.20.0):');
|
|
1343
|
+
console.log(
|
|
1344
|
+
' An in-progress PM session was detected (docs/project_notes/pm-session.md).'
|
|
1345
|
+
);
|
|
1346
|
+
console.log(
|
|
1347
|
+
' The cached PM orchestrator context loaded under the prior SDD version'
|
|
1348
|
+
);
|
|
1349
|
+
console.log(
|
|
1350
|
+
' does NOT auto-refresh on this upgrade. Run `/compact` and then'
|
|
1351
|
+
);
|
|
1352
|
+
console.log(
|
|
1353
|
+
' `continue pm` to reload templates before resuming development work.'
|
|
1354
|
+
);
|
|
1355
|
+
console.log(' Doctor check #16 will WARN until /compact runs.');
|
|
1356
|
+
}
|
|
1357
|
+
} catch (_err) {
|
|
1358
|
+
// best-effort advisory; never block the upgrade on read failure
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1332
1362
|
console.log(`\nNext: git add -A && git commit -m "chore: upgrade SDD DevFlow to ${newVersion}"\n`);
|
|
1333
1363
|
}
|
|
1334
1364
|
|
package/package.json
CHANGED
|
@@ -88,11 +88,42 @@ else
|
|
|
88
88
|
fi
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
**13. MCE Action 9 present** (D1, added v0.20.0) — Verify the ticket's `## Merge Checklist Evidence` table includes row 9 marked `[x]`, AND the dedicated `## Audit Merge Output` section below the MCE table has non-empty body content containing a structural-compliance signal (e.g. `Structural: 13/13 PASS` or `Verdict: APPROVE`). Stable ID `D1`. Empirical motivation: fx PR #299 F-WEB-HISTORY merged without `/audit-merge` having been run — MCE table contained Actions 0-8 only (no row 9). The prose instruction at `merge-checklist.md:87` ("Run /audit-merge to verify all compliance checks pass automatically") was easier to skip than a structural table row. D1 makes the skip falsifiable: row 9 stays unfilled if the audit didn't run.
|
|
92
|
+
```bash
|
|
93
|
+
ACTION_9_LINE=$(awk '/^## Merge Checklist Evidence/,/^---|^## (Audit Merge Output|Acceptance|$)/' "$TICKET" | grep -E '^\| 9\. ' | head -1)
|
|
94
|
+
AUDIT_SECTION=$(awk '/^## Audit Merge Output/,/^## |^---|^\*Ticket/' "$TICKET")
|
|
95
|
+
AUDIT_SECTION_BODY=$(printf '%s' "$AUDIT_SECTION" | sed '1d;$d' | grep -vE '^[[:space:]]*>' | tr -d '[:space:]')
|
|
96
|
+
# Compliance signal: case-insensitive. Mirrors JS twin's `.*?` semantics so
|
|
97
|
+
# `Structural: N/N PASS` (colon), `Structural — N/N` (em-dash), `Structural N/N`
|
|
98
|
+
# (space) all match. The `/audit-merge` Output Format section MUST emit at least
|
|
99
|
+
# one canonical signal per the Self-Verification Guarantee (see Output Format).
|
|
100
|
+
COMPLIANCE_HIT=$(printf '%s' "$AUDIT_SECTION" | grep -ciE '(structural[^[:alnum:]]+[0-9]+/[0-9]+)|(\b13/13\b)|(\ball[[:space:]]+(checks[[:space:]]+)?pass)|(verdict[[:space:]]*:[[:space:]]*approve)')
|
|
101
|
+
|
|
102
|
+
if [ -z "$ACTION_9_LINE" ] && [ -z "$AUDIT_SECTION" ]; then
|
|
103
|
+
# Legacy ticket predating v0.20.0 — neither row 9 NOR Audit Merge Output section exists.
|
|
104
|
+
# Emit N/A with migration hint instead of BLOCKER. Forward-looking: tickets created
|
|
105
|
+
# post-v0.20.0 will have at least the section header (from ticket-template.md), so
|
|
106
|
+
# legitimate skips still surface via the other branches below.
|
|
107
|
+
echo "D1 N/A: legacy ticket (no row 9 and no ## Audit Merge Output section) — add both per merge-checklist.md to comply with v0.20.0"
|
|
108
|
+
elif [ -z "$ACTION_9_LINE" ]; then
|
|
109
|
+
flag "D1 BLOCKER: MCE Action 9 row missing (required since v0.20.0; ## Audit Merge Output section exists but row 9 not added to MCE table)"
|
|
110
|
+
elif printf '%s' "$ACTION_9_LINE" | grep -qE '\| \[ \] \|'; then
|
|
111
|
+
flag "D1 BLOCKER: MCE Action 9 row unchecked — /audit-merge not run"
|
|
112
|
+
elif [ -z "$AUDIT_SECTION_BODY" ]; then
|
|
113
|
+
flag "D1 BLOCKER: ## Audit Merge Output section missing or empty"
|
|
114
|
+
elif [ "$COMPLIANCE_HIT" -eq 0 ]; then
|
|
115
|
+
flag "D1 BLOCKER: ## Audit Merge Output section present but no structural compliance signal (expect 'Structural: N/N PASS' or 'Verdict: APPROVE')"
|
|
116
|
+
else
|
|
117
|
+
CHAR_COUNT=$(printf '%s' "$AUDIT_SECTION" | wc -c | tr -d ' ')
|
|
118
|
+
echo "D1 PASS: ## Audit Merge Output section pasted (~${CHAR_COUNT} chars)"
|
|
119
|
+
fi
|
|
120
|
+
```
|
|
121
|
+
|
|
91
122
|
### Drift Checks (added v0.18.0) — ADVISORY, not blocking
|
|
92
123
|
|
|
93
124
|
Sixteen 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`).
|
|
94
125
|
|
|
95
|
-
**
|
|
126
|
+
**14. 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.
|
|
96
127
|
```bash
|
|
97
128
|
TEST_KW_RE='(npm test|pnpm test|tests?[^|]*[0-9]|[*: ]tests?[*: ]+[0-9])'
|
|
98
129
|
PR_BODY=$(gh pr view --json body -q .body 2>/dev/null || true)
|
|
@@ -109,14 +140,14 @@ else
|
|
|
109
140
|
fi
|
|
110
141
|
```
|
|
111
142
|
|
|
112
|
-
**
|
|
143
|
+
**15. 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.
|
|
113
144
|
```bash
|
|
114
145
|
awk '/^## Merge Checklist Evidence/{flag=1; next} /^## /{flag=0} flag' "$TICKET" \
|
|
115
146
|
| grep -E '^\|.*\[x\].*(to be |will |pending|TBD|Will be |to be created|next commit|aspirational)' \
|
|
116
147
|
&& flag "P2 drift: aspirational row(s) found"
|
|
117
148
|
```
|
|
118
149
|
|
|
119
|
-
**
|
|
150
|
+
**16. P3 — Post-merge actions not logged** (only fires if PR is MERGED and ticket Status is Done). Items marked as post-merge operator actions (AC / DoD / Test plan unchecked with post-merge keywords) should have a Completion Log row documenting execution.
|
|
120
151
|
```bash
|
|
121
152
|
# Strip checkbox prefix before comparison; use grep -Fq fixed-string match.
|
|
122
153
|
grep -E "^- \[ \].*(post-merge|operator|prod rollout|pending verification)" "$TICKET" \
|
|
@@ -129,14 +160,14 @@ while IFS= read -r item; do
|
|
|
129
160
|
done < /tmp/pm_items.txt
|
|
130
161
|
```
|
|
131
162
|
|
|
132
|
-
**
|
|
163
|
+
**17. P4 — Remote branch orphan after "deleted".** Workflow Step 6 claims `[x] branch deleted` but origin still has the branch.
|
|
133
164
|
```bash
|
|
134
165
|
BRANCH=$(grep -oE '\*\*[Bb]ranch:\*\*[[:space:]]*[^[:space:]|()]+' "$TICKET" | head -1 | sed -E 's/^\*\*[Bb]ranch:\*\*[[:space:]]*//')
|
|
135
166
|
git fetch origin --prune --quiet
|
|
136
167
|
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)"
|
|
137
168
|
```
|
|
138
169
|
|
|
139
|
-
**
|
|
170
|
+
**18. P5 — Frozen ticket Status post-merge.** Scan all tickets in `docs/tickets/`; flag any with Status ≠ Done whose ticket-ID appears in `git log --all --grep`. Multi-word Status values like "Ready for Merge" must be handled (use `sed -E` char class, not `\w+`).
|
|
140
171
|
```bash
|
|
141
172
|
FROZEN_COUNT=0
|
|
142
173
|
for t in docs/tickets/*.md; do
|
|
@@ -154,7 +185,7 @@ done
|
|
|
154
185
|
[ "$FROZEN_COUNT" -eq 1 ] && flag "P5 drift: 1 frozen ticket"
|
|
155
186
|
```
|
|
156
187
|
|
|
157
|
-
**
|
|
188
|
+
**19. P6 — AC count off-by-N (header-form aware since v0.19.0).** Merge Checklist Evidence row 1 claim diverges from actual count. Supports two canonical AC forms: `[x]`/`[ ]` checkbox form and `### AC<N>` header form. When header form is present (≥ 1 `### AC<N>` heading), headers are authoritative — checkbox sub-items under each header are NOT double-counted. A header is "deferred" (not marked) when its body contains `**Status**: Deferred|Pending|Skipped|Blocked` before the next `### ` or section terminator. Two MCE claim shapes: `all N marked` (N = total, implies all marked) and `AC: X/Y done` (X = marked, Y = total — supports deferred ACs where Y > X intentionally). v0.19.0 drops the v0.18.3 `-ge 2` tolerance — comparison is now exact-match (off-by-1 advisories surface).
|
|
158
189
|
```bash
|
|
159
190
|
AC_BLOCK=$(awk '/^## Acceptance Criteria/,/^## Definition of Done/' "$TICKET")
|
|
160
191
|
HEADER_ACS=$(echo "$AC_BLOCK" | grep -cE '^### AC[0-9]+')
|
|
@@ -196,7 +227,7 @@ elif [ -n "$CLAIM_LINE" ]; then
|
|
|
196
227
|
fi
|
|
197
228
|
```
|
|
198
229
|
|
|
199
|
-
**
|
|
230
|
+
**20. 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.
|
|
200
231
|
```bash
|
|
201
232
|
TERMINAL=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET" | grep -iE "(test|pass|green)" | grep -oE "[0-9]+/[0-9]+" | tail -1)
|
|
202
233
|
AC=$(awk '/^## Acceptance Criteria/,/^## Definition of Done/' "$TICKET")
|
|
@@ -207,7 +238,7 @@ for n in $FINAL_NUMS; do
|
|
|
207
238
|
done
|
|
208
239
|
```
|
|
209
240
|
|
|
210
|
-
**
|
|
241
|
+
**21. P8 — Completion Log gap vs Workflow Checklist.** Each `[x]` Step N in Workflow should have ≥1 Completion Log row mentioning "Step N". Use `while-read` on unique step numbers (not `for-in` which splits on whitespace).
|
|
211
242
|
```bash
|
|
212
243
|
WORKFLOW=$(awk '/^## Workflow Checklist/,/^## Completion Log/' "$TICKET")
|
|
213
244
|
COMPLETION=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET")
|
|
@@ -218,7 +249,7 @@ while read -r step_num; do
|
|
|
218
249
|
done <<< "$CHECKED_STEPS"
|
|
219
250
|
```
|
|
220
251
|
|
|
221
|
-
**
|
|
252
|
+
**22. 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.
|
|
222
253
|
```bash
|
|
223
254
|
TRACKER=docs/project_notes/product-tracker.md
|
|
224
255
|
HEADER_STEP=$(grep '^\*\*Last Updated:\*\*' "$TRACKER" | grep -oE '(Step )?[0-9]+/6' | head -1 | sed -E 's/^Step //')
|
|
@@ -227,7 +258,7 @@ DETAIL_STEP=$(grep -A 1 '^\*\*Active Feature:\*\*' "$TRACKER" | grep -oE '(Step
|
|
|
227
258
|
&& flag "P9 drift: tracker header says $HEADER_STEP, Active Feature says $DETAIL_STEP"
|
|
228
259
|
```
|
|
229
260
|
|
|
230
|
-
**
|
|
261
|
+
**23. P10 — Duplicate Completion Log rows.** Hash `date | action | first-80-of-notes`. Duplicates suggest copy-paste error during editing.
|
|
231
262
|
```bash
|
|
232
263
|
awk -F'|' '/^\| [0-9]{4}-[0-9]{2}-[0-9]{2}/ {
|
|
233
264
|
key = $2 "|" $3 "|" substr($4, 1, 80)
|
|
@@ -237,7 +268,7 @@ awk -F'|' '/^\| [0-9]{4}-[0-9]{2}-[0-9]{2}/ {
|
|
|
237
268
|
| while read -r dup; do flag "P10 drift: duplicate Completion Log row: $dup"; done
|
|
238
269
|
```
|
|
239
270
|
|
|
240
|
-
**
|
|
271
|
+
**24. 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.
|
|
241
272
|
```bash
|
|
242
273
|
TICKET_STATUS=$(grep -E "^\*\*Status:\*\*" "$TICKET" | head -1 \
|
|
243
274
|
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
@@ -274,7 +305,7 @@ case "$TICKET_BASENAME" in
|
|
|
274
305
|
esac
|
|
275
306
|
```
|
|
276
307
|
|
|
277
|
-
**
|
|
308
|
+
**25. P12 — Tracker HEAD references stale (added v0.18.2).** The `**Last Updated:**` and `**Active Feature:**` lines may embed `HEAD <sha>` or `HEAD: <sha>` references that were correct when written but went stale as further commits landed (empirically observed in fx F-WEB-MENU-VISION-001 audit cycle 2026-05-06: tracker said `HEAD: fd752e4` while `git rev-parse HEAD` was `6fa801e` after the agent's own self-edit commit). Compare each extracted SHA against the active branch HEAD. Bidirectional prefix tolerance: a 7-char tracker SHA matches the full 40-char actual HEAD if it's a prefix; a full 40-char tracker SHA matches if its first 7 chars equal the actual short form. Scoped strictly to the two header lines so narrative SHAs in "Last Completed" prose never false-positive-fire.
|
|
278
309
|
```bash
|
|
279
310
|
TRACKER=docs/project_notes/product-tracker.md
|
|
280
311
|
if [ -f "$TRACKER" ]; then
|
|
@@ -294,7 +325,7 @@ if [ -f "$TRACKER" ]; then
|
|
|
294
325
|
fi
|
|
295
326
|
```
|
|
296
327
|
|
|
297
|
-
**
|
|
328
|
+
**26. 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.
|
|
298
329
|
```bash
|
|
299
330
|
KEY_FACTS=docs/project_notes/key_facts.md
|
|
300
331
|
if [ -f "$KEY_FACTS" ]; then
|
|
@@ -310,7 +341,7 @@ if [ -f "$KEY_FACTS" ]; then
|
|
|
310
341
|
fi
|
|
311
342
|
```
|
|
312
343
|
|
|
313
|
-
**
|
|
344
|
+
**27. 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.
|
|
314
345
|
```bash
|
|
315
346
|
if [ "$TICKET_STATUS" = "Done" ]; then
|
|
316
347
|
MCE_BLOCK=$(awk '
|
|
@@ -324,7 +355,7 @@ if [ "$TICKET_STATUS" = "Done" ]; then
|
|
|
324
355
|
fi
|
|
325
356
|
```
|
|
326
357
|
|
|
327
|
-
**
|
|
358
|
+
**28. 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`.
|
|
328
359
|
```bash
|
|
329
360
|
COMPLETION=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET")
|
|
330
361
|
AC_LINES=$(grep -nE '^[[:space:]]*-[[:space:]]*\[[ x]\][[:space:]]+AC-[A-Za-z0-9_-]+' "$TICKET" \
|
|
@@ -340,7 +371,7 @@ while IFS= read -r line; do
|
|
|
340
371
|
done <<< "$AC_LINES"
|
|
341
372
|
```
|
|
342
373
|
|
|
343
|
-
**
|
|
374
|
+
**29. 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.
|
|
344
375
|
```bash
|
|
345
376
|
TRACKER=docs/project_notes/product-tracker.md
|
|
346
377
|
case "$TICKET_STATUS" in
|
|
@@ -354,9 +385,66 @@ case "$TICKET_STATUS" in
|
|
|
354
385
|
esac
|
|
355
386
|
```
|
|
356
387
|
|
|
388
|
+
**30. P17 — Auth-touching backend change requires bearer-flow integration test on non-/me route** (added v0.20.0). When the diff modifies a backend auth-adjacent module (`auth*`, `actorResolver*`, `bearer*`, `rateLimit*`), the diff should ALSO include or modify an integration test that exercises the bearer flow on a NON-`/me` route. Empirically double-confirmed: BUG-PROD-013 (actorResolver bearer-path 500 on `/conversation/*` — non-`/me`), BUG-API-RATELIMIT-BEARER-001 (rateLimit helpers tested only api-key + IP, no bearer-through-global-limiter integration coverage). The `/me`-itself integrity bug class (F107a-FU2) is a distinct pattern — opt-out annotation `P17 N/A: <reason ≥ 1 word>` is recognized for frontend-only auth changes or other edge cases. Advisory, never blocks merge.
|
|
389
|
+
```bash
|
|
390
|
+
# P17 — Auth-touching change requires bearer-flow integration test on non-/me route.
|
|
391
|
+
AUTH_TOUCHED=$(git diff --name-only "$BASE_REF...HEAD" 2>/dev/null | \
|
|
392
|
+
grep -iE '(auth|actorResolver|bearer|rateLimit)' | \
|
|
393
|
+
grep -E '^(packages/api|api|server|backend|src/server|src/api)/' | \
|
|
394
|
+
grep -vE '\.(test|spec)\.[jt]sx?$|__tests__/|/web/|/frontend/|/client/' | head -20)
|
|
395
|
+
|
|
396
|
+
if [ -z "$AUTH_TOUCHED" ]; then
|
|
397
|
+
echo "P17 N/A: no backend auth-adjacent files in diff"
|
|
398
|
+
else
|
|
399
|
+
TOUCHED_COUNT=$(printf '%s\n' "$AUTH_TOUCHED" | wc -l | tr -d ' ')
|
|
400
|
+
|
|
401
|
+
# Explicit opt-out (Gemini R1 M4 strengthened: require `: <reason ≥ 1 word>`)
|
|
402
|
+
OPTOUT=$(grep -E 'P17[[:space:]]+(N/A|opt[- ]?out|exempt)[[:space:]]*:[[:space:]]+[A-Za-z0-9].{2,}' "$TICKET" 2>/dev/null | head -1)
|
|
403
|
+
if [ -n "$OPTOUT" ]; then
|
|
404
|
+
echo "P17 N/A: explicit opt-out — $(printf '%s' "$OPTOUT" | head -c 120)"
|
|
405
|
+
else
|
|
406
|
+
INTEGRATION_TESTS=$(git diff --name-only "$BASE_REF...HEAD" 2>/dev/null | \
|
|
407
|
+
grep -iE '(integration|__tests__/.*\.integration\.|/integration/)' | \
|
|
408
|
+
grep -E '\.(test|spec)\.[jt]sx?$')
|
|
409
|
+
|
|
410
|
+
# Two extraction passes (Codex R2 I-1 multi-line fix + Gemini R2 I-1 backticks):
|
|
411
|
+
# Pass A: dot-method calls — `.get('/path')` / `.post(\`/path\`)` (single-line).
|
|
412
|
+
# Pass B: `url: '...'` property values within multi-line app.inject({ ... }).
|
|
413
|
+
BEARER_NON_ME_HITS=0
|
|
414
|
+
while IFS= read -r test_file; do
|
|
415
|
+
[ -z "$test_file" ] && continue
|
|
416
|
+
[ ! -f "$test_file" ] && continue
|
|
417
|
+
if ! grep -qiE '(bearer|authorization)' "$test_file" 2>/dev/null; then
|
|
418
|
+
continue
|
|
419
|
+
fi
|
|
420
|
+
ROUTES_A=$(grep -iE "\.(get|post|put|patch|delete)\([\`\"'](/[^\`\"']+)[\`\"']" "$test_file" 2>/dev/null | \
|
|
421
|
+
grep -oE "[\`\"'](/[^\`\"']+)[\`\"']" | sed -E "s/^[\`\"']|[\`\"']\$//g")
|
|
422
|
+
ROUTES_B=$(grep -iE "url[[:space:]]*:[[:space:]]*[\`\"'](/[^\`\"']+)[\`\"']" "$test_file" 2>/dev/null | \
|
|
423
|
+
grep -oE "[\`\"'](/[^\`\"']+)[\`\"']" | sed -E "s/^[\`\"']|[\`\"']\$//g")
|
|
424
|
+
ROUTE_STRINGS=$(printf '%s\n%s\n' "$ROUTES_A" "$ROUTES_B" | grep -vE '^$' | sort -u)
|
|
425
|
+
# Strip ${...} template-literal interpolations
|
|
426
|
+
ROUTE_STRINGS=$(printf '%s\n' "$ROUTE_STRINGS" | sed -E 's/\$\{[^}]*\}//g')
|
|
427
|
+
if [ -n "$ROUTE_STRINGS" ]; then
|
|
428
|
+
if printf '%s\n' "$ROUTE_STRINGS" | grep -qvE '^/me($|/)' ; then
|
|
429
|
+
BEARER_NON_ME_HITS=$((BEARER_NON_ME_HITS + 1))
|
|
430
|
+
fi
|
|
431
|
+
fi
|
|
432
|
+
done <<EOF
|
|
433
|
+
$INTEGRATION_TESTS
|
|
434
|
+
EOF
|
|
435
|
+
|
|
436
|
+
if [ "$BEARER_NON_ME_HITS" -eq 0 ]; then
|
|
437
|
+
flag "P17 drift (advisory): $TOUCHED_COUNT backend auth-adjacent file(s) but no integration test exercises bearer on a non-/me route (BUG-PROD-013 / BUG-RATELIMIT-BEARER pattern). Add an integration test or annotate \`P17 N/A: <reason>\` in the ticket."
|
|
438
|
+
else
|
|
439
|
+
echo "P17 PASS: $TOUCHED_COUNT auth-adjacent backend file(s), $BEARER_NON_ME_HITS integration test(s) cover bearer non-/me routes"
|
|
440
|
+
fi
|
|
441
|
+
fi
|
|
442
|
+
fi
|
|
443
|
+
```
|
|
444
|
+
|
|
357
445
|
### Execution discipline (added v0.18.1)
|
|
358
446
|
|
|
359
|
-
For each of the
|
|
447
|
+
For each of the 17 drift checks (P1–P17), 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.
|
|
360
448
|
|
|
361
449
|
Recommended pattern:
|
|
362
450
|
|
|
@@ -375,7 +463,7 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
375
463
|
```
|
|
376
464
|
## Merge Compliance Audit — [FEATURE-ID]
|
|
377
465
|
|
|
378
|
-
### Structural (1-
|
|
466
|
+
### Structural (1-13) — blocking merge gate
|
|
379
467
|
|
|
380
468
|
| # | Check | Status | Detail |
|
|
381
469
|
|---|-------|:------:|--------|
|
|
@@ -383,7 +471,7 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
383
471
|
| 2 | Acceptance Criteria | PASS | 14/14 |
|
|
384
472
|
| 3 | Definition of Done | PASS | 7/7 |
|
|
385
473
|
| 4 | Workflow Checklist | PASS | 7/8 (Step 6 pending) |
|
|
386
|
-
| 5 | Merge Checklist Evidence | PASS |
|
|
474
|
+
| 5 | Merge Checklist Evidence | PASS | 9/9 with evidence |
|
|
387
475
|
| 6 | Completion Log | PASS | 5 entries, bugs documented |
|
|
388
476
|
| 7 | Tracker Sync | PASS | Active Session + Features table correct |
|
|
389
477
|
| 8 | key_facts.md | PASS | N/A — no new infrastructure |
|
|
@@ -391,35 +479,37 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
391
479
|
| 10 | Working Tree | PASS | Clean |
|
|
392
480
|
| 11 | Data Files | PASS | N/A — no JSON seed files |
|
|
393
481
|
| 12 | CI State | PASS | PR #123 — all checks pass or pending |
|
|
482
|
+
| 13 | MCE Action 9 (D1) | PASS | ## Audit Merge Output section pasted (~3200 chars) |
|
|
394
483
|
|
|
395
484
|
**STRUCTURAL: READY FOR MERGE** (or **STRUCTURAL: NEEDS FIX — N blockers**)
|
|
396
485
|
|
|
397
|
-
### Drift (
|
|
486
|
+
### Drift (14-30) — advisory, refresh before user authorization
|
|
398
487
|
|
|
399
488
|
| # | Pattern | Status | Detail |
|
|
400
489
|
|---|---------|:------:|--------|
|
|
401
|
-
|
|
|
402
|
-
|
|
|
403
|
-
|
|
|
404
|
-
|
|
|
405
|
-
|
|
|
406
|
-
|
|
|
407
|
-
|
|
|
408
|
-
|
|
|
409
|
-
|
|
|
410
|
-
|
|
|
411
|
-
|
|
|
412
|
-
|
|
|
413
|
-
|
|
|
414
|
-
|
|
|
415
|
-
|
|
|
416
|
-
|
|
|
490
|
+
| 14 | P1 PR body test count stale | PASS | matches ticket terminal |
|
|
491
|
+
| 15 | P2 Aspirational Evidence rows | PASS | all rows past-tense |
|
|
492
|
+
| 16 | P3 Post-merge actions logged | PASS | N/A pre-merge |
|
|
493
|
+
| 17 | P4 Remote branch orphan | PASS | not checked pre-merge |
|
|
494
|
+
| 18 | P5 Frozen ticket Status | PASS | 0 frozen |
|
|
495
|
+
| 19 | P6 AC count off-by-N | PASS | claim matches actual (form: header) |
|
|
496
|
+
| 20 | P7 Intra-ticket test drift | PASS | final sections = terminal |
|
|
497
|
+
| 21 | P8 Completion Log gap | PASS | every [x] step has narrative |
|
|
498
|
+
| 22 | P9 Tracker header stale | PASS | header = detail |
|
|
499
|
+
| 23 | P10 Duplicate log rows | PASS | no duplicates |
|
|
500
|
+
| 24 | P11 Tracker status mismatch | PASS | in-progress for Ready for Merge |
|
|
501
|
+
| 25 | P12 Tracker HEAD reference | PASS | tracker HEAD = git HEAD |
|
|
502
|
+
| 26 | P13 key_facts delta mismatch | PASS | N/A — no quantified deltas |
|
|
503
|
+
| 27 | P14 MCE Action 1 stale post-merge | PASS | N/A pre-merge / row past-tense |
|
|
504
|
+
| 28 | P15 Post-deploy AC without evidence | PASS | no post-deploy keyword in ACs |
|
|
505
|
+
| 29 | P16 Feature missing from tracker | PASS | feature in Features table |
|
|
506
|
+
| 30 | P17 Auth integration coverage | PASS | 2 backend file(s), 1 integration test covers bearer non-/me |
|
|
417
507
|
|
|
418
508
|
**DRIFT: CLEAN** (or **DRIFT: N advisories — refresh before merge**)
|
|
419
509
|
|
|
420
510
|
### Combined verdict
|
|
421
511
|
|
|
422
|
-
- Both PASS → **READY FOR MERGE** (compliance
|
|
512
|
+
- Both PASS → **READY FOR MERGE** (compliance 13/13, drift clean)
|
|
423
513
|
- Structural fail → **NEEDS FIX — N structural blockers** (any drift noted separately)
|
|
424
514
|
- Structural pass + drift advisories → **READY FOR MERGE PENDING DRIFT CLEANUP — N advisories**
|
|
425
515
|
```
|
|
@@ -432,6 +522,18 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
432
522
|
|
|
433
523
|
This rule exists because an LLM auditor, given header-form tickets where checkbox count is 0, will hallucinate `18/18` to "agree" with the MCE row 1 claim. The recipe says `0` and the MCE says `18/19`; the audit Detail must reflect both honestly so the reader can spot the form mismatch.
|
|
434
524
|
|
|
525
|
+
**Audit Output Self-Verification Guarantee** (added v0.20.0, required by D1): after emitting all structural rows + drift rows + combined verdict, ALWAYS append a final single-line summary in this exact form, regardless of pass/fail:
|
|
526
|
+
|
|
527
|
+
```
|
|
528
|
+
Structural: <passed>/<total> PASS | Drift: <flagged> advisory | Verdict: <APPROVE|REVISE>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
Examples:
|
|
532
|
+
- `Structural: 13/13 PASS | Drift: 0 advisory | Verdict: APPROVE`
|
|
533
|
+
- `Structural: 11/13 PASS | Drift: 4 advisory | Verdict: REVISE`
|
|
534
|
+
|
|
535
|
+
This line is the canonical compliance signal that `D1` (MCE Action 9 present, line 92) parses. Without it, a verbatim paste of legitimate audit output may fail D1's signal check. The line must always be the last non-empty line in the output.
|
|
536
|
+
|
|
435
537
|
### If issues are found
|
|
436
538
|
|
|
437
539
|
Fix them directly:
|
|
@@ -65,12 +65,14 @@ Determine the target branch from `docs/project_notes/key_facts.md` → `branchin
|
|
|
65
65
|
|
|
66
66
|
## Action 8: Fill Merge Checklist Evidence
|
|
67
67
|
|
|
68
|
-
In the ticket, fill the `## Merge Checklist Evidence` table. For each action (0–7), mark `[x]` and write the actual evidence (not placeholders). Example:
|
|
68
|
+
In the ticket, fill the `## Merge Checklist Evidence` table. For each action (0–7), mark `[x]` and write the actual evidence (not placeholders). Then complete Action 9 below — it MUST appear as the 9th row of the same table. Example:
|
|
69
69
|
|
|
70
70
|
| Action | Done | Evidence |
|
|
71
71
|
|--------|:----:|----------|
|
|
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
|
+
| ... (rows 2-7 follow same shape) | [x] | ... |
|
|
75
|
+
| 9. Run `/audit-merge` | [x] | See § "Audit Merge Output" below |
|
|
74
76
|
|
|
75
77
|
**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
78
|
|
|
@@ -82,11 +84,30 @@ In the ticket, fill the `## Merge Checklist Evidence` table. For each action (0
|
|
|
82
84
|
|
|
83
85
|
Sub-scope tickets reach `Status: Done` independently while the parent feature's tracker row stays at its parent status (typically `pending` or `in-progress`) until ALL sub-scopes close. `/audit-merge` P11 detects the suffix and emits `P11 N/A` instead of flagging the parent/sub-scope status divergence as drift.
|
|
84
86
|
|
|
85
|
-
## Action 9: Run compliance audit
|
|
87
|
+
## Action 9: Run compliance audit (MANDATORY, added v0.20.0 as structural MCE row)
|
|
86
88
|
|
|
87
|
-
Run `/audit-merge`
|
|
89
|
+
**Action 9 is MANDATORY.** Run `/audit-merge` after Actions 0-8 are complete. Paste the FULL verbatim output (structural + drift tables + verdict + self-verification line `Structural: N/N PASS | Drift: K advisory | Verdict: X`) into the dedicated `## Audit Merge Output` section of the ticket, directly below the MCE table.
|
|
88
90
|
|
|
89
|
-
|
|
91
|
+
Do NOT abbreviate, summarize, or omit failing rows. If `/audit-merge` flags any STRUCTURAL check as FAIL, fix it and re-run until ALL structural checks PASS. Drift advisories may stay open with explanation in the surrounding ticket.
|
|
92
|
+
|
|
93
|
+
The ticket structure for Action 9 (added v0.20.0):
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
## Merge Checklist Evidence
|
|
97
|
+
|
|
98
|
+
| Action | Done | Evidence |
|
|
99
|
+
|--------|:----:|----------|
|
|
100
|
+
| 0. … | [x] | … |
|
|
101
|
+
| 1. … | [x] | … |
|
|
102
|
+
| ... (rows 2-7) | [x] | … |
|
|
103
|
+
| 9. Run `/audit-merge` | [x] | See § "Audit Merge Output" below |
|
|
104
|
+
|
|
105
|
+
## Audit Merge Output
|
|
106
|
+
|
|
107
|
+
<verbatim /audit-merge output goes here — see ticket-template.md>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The `/audit-merge` structural check `D1` (added v0.20.0) verifies row 9 exists with `[x]` AND the `## Audit Merge Output` section has non-empty body with a structural-compliance signal. Skipping this Action is now structurally visible (row 9 stays unfilled if you forget).
|
|
90
111
|
|
|
91
112
|
## Action 10: Request merge approval
|
|
92
113
|
|
|
@@ -102,6 +102,18 @@ This creates a feedback loop for improving future reviews. -->
|
|
|
102
102
|
| 5. Commit documentation | [ ] | Commit: (hash) |
|
|
103
103
|
| 6. Verify clean working tree | [ ] | `git status`: clean |
|
|
104
104
|
| 7. Verify branch up to date | [ ] | merge-base: up to date / merged origin/<branch> |
|
|
105
|
+
| 9. Run `/audit-merge` | [ ] | See § "Audit Merge Output" below |
|
|
106
|
+
|
|
107
|
+
## Audit Merge Output
|
|
108
|
+
|
|
109
|
+
> Paste the FULL verbatim output of `/audit-merge` below. The block must include both tables
|
|
110
|
+
> (structural + drift), all rows, the combined verdict, AND the final self-verification line
|
|
111
|
+
> in the form `Structural: N/N PASS | Drift: K advisory | Verdict: <APPROVE|REVISE>`. Do NOT
|
|
112
|
+
> abbreviate, summarize, or omit failing rows. Required for the D1 structural check to PASS.
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
<paste /audit-merge output here>
|
|
116
|
+
```
|
|
105
117
|
|
|
106
118
|
---
|
|
107
119
|
|
|
@@ -87,8 +87,27 @@ For each feature in the batch:
|
|
|
87
87
|
- **Lock**: If `docs/project_notes/pm-session.lock` is missing → stop (session was terminated externally).
|
|
88
88
|
- **Circuit breaker**: If 3 consecutive features are `blocked` → stop and report to user.
|
|
89
89
|
- **Context check**: If **2+ features have been completed** in this session, STOP and tell the user to run `/compact` followed by `continue pm`. Do NOT continue to the next feature — context degradation causes skipped steps, lost constraints, and poor quality. This is mandatory, not a suggestion.
|
|
90
|
+
- **Session-coherence check (added v0.20.0)**: Read `.sdd-version` from the repo root and the `**SDD version at start:** X.Y.Z` field from `docs/project_notes/pm-session.md`. If they differ → emit ADVISORY (non-blocking) once per Step transition:
|
|
91
|
+
```
|
|
92
|
+
⚠ Session-coherence drift: SDD upgraded mid-session (X.Y.Z → CURRENT).
|
|
93
|
+
Cached workflow references and rules in this session may be stale.
|
|
94
|
+
Recommendation: complete current Step's atomic work, then run `/compact`
|
|
95
|
+
followed by `continue pm` to reload templates before next Step.
|
|
96
|
+
(Non-blocking — proceed if you've reviewed the upgrade CHANGELOG and
|
|
97
|
+
confirmed no workflow-affecting changes.)
|
|
98
|
+
```
|
|
99
|
+
Do NOT auto-update the pm-session.md value here — capture the current cached-context coherence state. The field is auto-updated only by `continue pm` after `/compact` (see "Auto-update on /compact" below).
|
|
90
100
|
- **Clean workspace**: Run `git status`. If dirty, commit or stash before proceeding.
|
|
91
101
|
|
|
102
|
+
##### Auto-update on `/compact` (added v0.20.0)
|
|
103
|
+
|
|
104
|
+
When `continue pm` resumes the orchestrator after a `/compact`:
|
|
105
|
+
1. Re-read current `.sdd-version`.
|
|
106
|
+
2. Update `**SDD version at start:** X.Y.Z` in `pm-session.md` to the current value.
|
|
107
|
+
3. Reset the per-Step advisory-emitted flag.
|
|
108
|
+
|
|
109
|
+
Rationale: `/compact` reloads the cached context from the filesystem. After /compact, the agent is operating on current SDD templates, so the `SDD version at start` field should reflect that. The drift advisory above targets the WINDOW between an SDD upgrade and the next /compact — that window is the risk zone.
|
|
110
|
+
|
|
92
111
|
#### b. Start Feature
|
|
93
112
|
|
|
94
113
|
1. Update `pm-session.md`:
|
|
@@ -88,11 +88,42 @@ else
|
|
|
88
88
|
fi
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
**13. MCE Action 9 present** (D1, added v0.20.0) — Verify the ticket's `## Merge Checklist Evidence` table includes row 9 marked `[x]`, AND the dedicated `## Audit Merge Output` section below the MCE table has non-empty body content containing a structural-compliance signal (e.g. `Structural: 13/13 PASS` or `Verdict: APPROVE`). Stable ID `D1`. Empirical motivation: fx PR #299 F-WEB-HISTORY merged without `/audit-merge` having been run — MCE table contained Actions 0-8 only (no row 9). The prose instruction at `merge-checklist.md:87` ("Run /audit-merge to verify all compliance checks pass automatically") was easier to skip than a structural table row. D1 makes the skip falsifiable: row 9 stays unfilled if the audit didn't run.
|
|
92
|
+
```bash
|
|
93
|
+
ACTION_9_LINE=$(awk '/^## Merge Checklist Evidence/,/^---|^## (Audit Merge Output|Acceptance|$)/' "$TICKET" | grep -E '^\| 9\. ' | head -1)
|
|
94
|
+
AUDIT_SECTION=$(awk '/^## Audit Merge Output/,/^## |^---|^\*Ticket/' "$TICKET")
|
|
95
|
+
AUDIT_SECTION_BODY=$(printf '%s' "$AUDIT_SECTION" | sed '1d;$d' | grep -vE '^[[:space:]]*>' | tr -d '[:space:]')
|
|
96
|
+
# Compliance signal: case-insensitive. Mirrors JS twin's `.*?` semantics so
|
|
97
|
+
# `Structural: N/N PASS` (colon), `Structural — N/N` (em-dash), `Structural N/N`
|
|
98
|
+
# (space) all match. The `/audit-merge` Output Format section MUST emit at least
|
|
99
|
+
# one canonical signal per the Self-Verification Guarantee (see Output Format).
|
|
100
|
+
COMPLIANCE_HIT=$(printf '%s' "$AUDIT_SECTION" | grep -ciE '(structural[^[:alnum:]]+[0-9]+/[0-9]+)|(\b13/13\b)|(\ball[[:space:]]+(checks[[:space:]]+)?pass)|(verdict[[:space:]]*:[[:space:]]*approve)')
|
|
101
|
+
|
|
102
|
+
if [ -z "$ACTION_9_LINE" ] && [ -z "$AUDIT_SECTION" ]; then
|
|
103
|
+
# Legacy ticket predating v0.20.0 — neither row 9 NOR Audit Merge Output section exists.
|
|
104
|
+
# Emit N/A with migration hint instead of BLOCKER. Forward-looking: tickets created
|
|
105
|
+
# post-v0.20.0 will have at least the section header (from ticket-template.md), so
|
|
106
|
+
# legitimate skips still surface via the other branches below.
|
|
107
|
+
echo "D1 N/A: legacy ticket (no row 9 and no ## Audit Merge Output section) — add both per merge-checklist.md to comply with v0.20.0"
|
|
108
|
+
elif [ -z "$ACTION_9_LINE" ]; then
|
|
109
|
+
flag "D1 BLOCKER: MCE Action 9 row missing (required since v0.20.0; ## Audit Merge Output section exists but row 9 not added to MCE table)"
|
|
110
|
+
elif printf '%s' "$ACTION_9_LINE" | grep -qE '\| \[ \] \|'; then
|
|
111
|
+
flag "D1 BLOCKER: MCE Action 9 row unchecked — /audit-merge not run"
|
|
112
|
+
elif [ -z "$AUDIT_SECTION_BODY" ]; then
|
|
113
|
+
flag "D1 BLOCKER: ## Audit Merge Output section missing or empty"
|
|
114
|
+
elif [ "$COMPLIANCE_HIT" -eq 0 ]; then
|
|
115
|
+
flag "D1 BLOCKER: ## Audit Merge Output section present but no structural compliance signal (expect 'Structural: N/N PASS' or 'Verdict: APPROVE')"
|
|
116
|
+
else
|
|
117
|
+
CHAR_COUNT=$(printf '%s' "$AUDIT_SECTION" | wc -c | tr -d ' ')
|
|
118
|
+
echo "D1 PASS: ## Audit Merge Output section pasted (~${CHAR_COUNT} chars)"
|
|
119
|
+
fi
|
|
120
|
+
```
|
|
121
|
+
|
|
91
122
|
### Drift Checks (added v0.18.0) — ADVISORY, not blocking
|
|
92
123
|
|
|
93
124
|
Sixteen 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`).
|
|
94
125
|
|
|
95
|
-
**
|
|
126
|
+
**14. 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`.
|
|
96
127
|
```bash
|
|
97
128
|
TEST_KW_RE='(npm test|pnpm test|tests?[^|]*[0-9]|[*: ]tests?[*: ]+[0-9])'
|
|
98
129
|
PR_BODY=$(gh pr view --json body -q .body 2>/dev/null || true)
|
|
@@ -109,14 +140,14 @@ else
|
|
|
109
140
|
fi
|
|
110
141
|
```
|
|
111
142
|
|
|
112
|
-
**
|
|
143
|
+
**15. P2 — Merge Checklist Evidence aspirational.** `[x]` rows with future-tense text.
|
|
113
144
|
```bash
|
|
114
145
|
awk '/^## Merge Checklist Evidence/{flag=1; next} /^## /{flag=0} flag' "$TICKET" \
|
|
115
146
|
| grep -E '^\|.*\[x\].*(to be |will |pending|TBD|Will be |to be created|next commit|aspirational)' \
|
|
116
147
|
&& flag "P2 drift: aspirational row(s) found"
|
|
117
148
|
```
|
|
118
149
|
|
|
119
|
-
**
|
|
150
|
+
**16. P3 — Post-merge actions not logged** (post-merge only).
|
|
120
151
|
```bash
|
|
121
152
|
# Strip checkbox prefix before comparison; use grep -Fq fixed-string match.
|
|
122
153
|
grep -E "^- \[ \].*(post-merge|operator|prod rollout|pending verification)" "$TICKET" \
|
|
@@ -129,14 +160,14 @@ while IFS= read -r item; do
|
|
|
129
160
|
done < /tmp/pm_items.txt
|
|
130
161
|
```
|
|
131
162
|
|
|
132
|
-
**
|
|
163
|
+
**17. P4 — Remote branch orphan.**
|
|
133
164
|
```bash
|
|
134
165
|
BRANCH=$(grep -oE '\*\*[Bb]ranch:\*\*[[:space:]]*[^[:space:]|()]+' "$TICKET" | head -1 | sed -E 's/^\*\*[Bb]ranch:\*\*[[:space:]]*//')
|
|
135
166
|
git fetch origin --prune --quiet
|
|
136
167
|
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)"
|
|
137
168
|
```
|
|
138
169
|
|
|
139
|
-
**
|
|
170
|
+
**18. P5 — Frozen ticket Status post-merge.** Multi-word status via sed char class, not `\w+`.
|
|
140
171
|
```bash
|
|
141
172
|
FROZEN_COUNT=0
|
|
142
173
|
for t in docs/tickets/*.md; do
|
|
@@ -154,7 +185,7 @@ done
|
|
|
154
185
|
[ "$FROZEN_COUNT" -eq 1 ] && flag "P5 drift: 1 frozen ticket"
|
|
155
186
|
```
|
|
156
187
|
|
|
157
|
-
**
|
|
188
|
+
**19. P6 — AC count off-by-N (header-form aware since v0.19.0).** Supports two AC forms: `[x]`/`[ ]` checkbox form and `### AC<N>` header form. Headers authoritative when present (≥ 1). A header is "deferred" when its body contains `**Status**: Deferred|Pending|Skipped|Blocked` before the next `### `. Two claim shapes: `all N marked` and `AC: X/Y done`. v0.19.0 drops the v0.18.3 `-ge 2` tolerance — exact-match (off-by-1 advisories surface).
|
|
158
189
|
```bash
|
|
159
190
|
AC_BLOCK=$(awk '/^## Acceptance Criteria/,/^## Definition of Done/' "$TICKET")
|
|
160
191
|
HEADER_ACS=$(echo "$AC_BLOCK" | grep -cE '^### AC[0-9]+')
|
|
@@ -196,7 +227,7 @@ elif [ -n "$CLAIM_LINE" ]; then
|
|
|
196
227
|
fi
|
|
197
228
|
```
|
|
198
229
|
|
|
199
|
-
**
|
|
230
|
+
**20. P7 — Test count drift within ticket (final-sections only).**
|
|
200
231
|
```bash
|
|
201
232
|
TERMINAL=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET" | grep -iE "(test|pass|green)" | grep -oE "[0-9]+/[0-9]+" | tail -1)
|
|
202
233
|
AC=$(awk '/^## Acceptance Criteria/,/^## Definition of Done/' "$TICKET")
|
|
@@ -207,7 +238,7 @@ for n in $FINAL_NUMS; do
|
|
|
207
238
|
done
|
|
208
239
|
```
|
|
209
240
|
|
|
210
|
-
**
|
|
241
|
+
**21. P8 — Completion Log gap vs Workflow Checklist.**
|
|
211
242
|
```bash
|
|
212
243
|
WORKFLOW=$(awk '/^## Workflow Checklist/,/^## Completion Log/' "$TICKET")
|
|
213
244
|
COMPLETION=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET")
|
|
@@ -218,7 +249,7 @@ while read -r step_num; do
|
|
|
218
249
|
done <<< "$CHECKED_STEPS"
|
|
219
250
|
```
|
|
220
251
|
|
|
221
|
-
**
|
|
252
|
+
**22. P9 — Tracker header stale.**
|
|
222
253
|
```bash
|
|
223
254
|
TRACKER=docs/project_notes/product-tracker.md
|
|
224
255
|
HEADER_STEP=$(grep '^\*\*Last Updated:\*\*' "$TRACKER" | grep -oE '(Step )?[0-9]+/6' | head -1 | sed -E 's/^Step //')
|
|
@@ -227,7 +258,7 @@ DETAIL_STEP=$(grep -A 1 '^\*\*Active Feature:\*\*' "$TRACKER" | grep -oE '(Step
|
|
|
227
258
|
&& flag "P9 drift: tracker header says $HEADER_STEP, Active Feature says $DETAIL_STEP"
|
|
228
259
|
```
|
|
229
260
|
|
|
230
|
-
**
|
|
261
|
+
**23. P10 — Duplicate Completion Log rows.**
|
|
231
262
|
```bash
|
|
232
263
|
awk -F'|' '/^\| [0-9]{4}-[0-9]{2}-[0-9]{2}/ {
|
|
233
264
|
key = $2 "|" $3 "|" substr($4, 1, 80)
|
|
@@ -237,7 +268,7 @@ awk -F'|' '/^\| [0-9]{4}-[0-9]{2}-[0-9]{2}/ {
|
|
|
237
268
|
| while read -r dup; do flag "P10 drift: duplicate Completion Log row: $dup"; done
|
|
238
269
|
```
|
|
239
270
|
|
|
240
|
-
**
|
|
271
|
+
**24. P11 — Tracker Features table status vs ticket Status mismatch.**
|
|
241
272
|
```bash
|
|
242
273
|
TICKET_STATUS=$(grep -E "^\*\*Status:\*\*" "$TICKET" | head -1 \
|
|
243
274
|
| sed -E 's/^\*\*Status:\*\*[[:space:]]*\*?\*?//' \
|
|
@@ -274,7 +305,7 @@ case "$TICKET_BASENAME" in
|
|
|
274
305
|
esac
|
|
275
306
|
```
|
|
276
307
|
|
|
277
|
-
**
|
|
308
|
+
**25. P12 — Tracker HEAD references stale (added v0.18.2).** The `**Last Updated:**` and `**Active Feature:**` lines may embed `HEAD <sha>` or `HEAD: <sha>` references that were correct when written but went stale as further commits landed (empirically observed in fx F-WEB-MENU-VISION-001 audit cycle 2026-05-06: tracker said `HEAD: fd752e4` while `git rev-parse HEAD` was `6fa801e` after the agent's own self-edit commit). Compare each extracted SHA against the active branch HEAD. Bidirectional prefix tolerance: a 7-char tracker SHA matches the full 40-char actual HEAD if it's a prefix; a full 40-char tracker SHA matches if its first 7 chars equal the actual short form. Scoped strictly to the two header lines so narrative SHAs in "Last Completed" prose never false-positive-fire.
|
|
278
309
|
```bash
|
|
279
310
|
TRACKER=docs/project_notes/product-tracker.md
|
|
280
311
|
if [ -f "$TRACKER" ]; then
|
|
@@ -294,7 +325,7 @@ if [ -f "$TRACKER" ]; then
|
|
|
294
325
|
fi
|
|
295
326
|
```
|
|
296
327
|
|
|
297
|
-
**
|
|
328
|
+
**26. P13 — key_facts delta vs ticket atom-count mismatch (added v0.18.3).** Whitespace-safe iteration + FEATURE_ID anchoring.
|
|
298
329
|
```bash
|
|
299
330
|
KEY_FACTS=docs/project_notes/key_facts.md
|
|
300
331
|
if [ -f "$KEY_FACTS" ]; then
|
|
@@ -310,7 +341,7 @@ if [ -f "$KEY_FACTS" ]; then
|
|
|
310
341
|
fi
|
|
311
342
|
```
|
|
312
343
|
|
|
313
|
-
**
|
|
344
|
+
**27. 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.
|
|
314
345
|
```bash
|
|
315
346
|
if [ "$TICKET_STATUS" = "Done" ]; then
|
|
316
347
|
MCE_BLOCK=$(awk '
|
|
@@ -324,7 +355,7 @@ if [ "$TICKET_STATUS" = "Done" ]; then
|
|
|
324
355
|
fi
|
|
325
356
|
```
|
|
326
357
|
|
|
327
|
-
**
|
|
358
|
+
**28. P15 — AC with post-deploy keyword admitted without Completion Log evidence (added v0.18.3).** Line-safe iteration via `while IFS= read -r`.
|
|
328
359
|
```bash
|
|
329
360
|
COMPLETION=$(awk '/^## Completion Log/,/^## Merge Checklist/' "$TICKET")
|
|
330
361
|
AC_LINES=$(grep -nE '^[[:space:]]*-[[:space:]]*\[[ x]\][[:space:]]+AC-[A-Za-z0-9_-]+' "$TICKET" \
|
|
@@ -340,7 +371,7 @@ while IFS= read -r line; do
|
|
|
340
371
|
done <<< "$AC_LINES"
|
|
341
372
|
```
|
|
342
373
|
|
|
343
|
-
**
|
|
374
|
+
**29. 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.
|
|
344
375
|
```bash
|
|
345
376
|
TRACKER=docs/project_notes/product-tracker.md
|
|
346
377
|
case "$TICKET_STATUS" in
|
|
@@ -354,9 +385,66 @@ case "$TICKET_STATUS" in
|
|
|
354
385
|
esac
|
|
355
386
|
```
|
|
356
387
|
|
|
388
|
+
**30. P17 — Auth-touching backend change requires bearer-flow integration test on non-/me route** (added v0.20.0). When the diff modifies a backend auth-adjacent module (`auth*`, `actorResolver*`, `bearer*`, `rateLimit*`), the diff should ALSO include or modify an integration test that exercises the bearer flow on a NON-`/me` route. Empirically double-confirmed: BUG-PROD-013 (actorResolver bearer-path 500 on `/conversation/*` — non-`/me`), BUG-API-RATELIMIT-BEARER-001 (rateLimit helpers tested only api-key + IP, no bearer-through-global-limiter integration coverage). The `/me`-itself integrity bug class (F107a-FU2) is a distinct pattern — opt-out annotation `P17 N/A: <reason ≥ 1 word>` is recognized for frontend-only auth changes or other edge cases. Advisory, never blocks merge.
|
|
389
|
+
```bash
|
|
390
|
+
# P17 — Auth-touching change requires bearer-flow integration test on non-/me route.
|
|
391
|
+
AUTH_TOUCHED=$(git diff --name-only "$BASE_REF...HEAD" 2>/dev/null | \
|
|
392
|
+
grep -iE '(auth|actorResolver|bearer|rateLimit)' | \
|
|
393
|
+
grep -E '^(packages/api|api|server|backend|src/server|src/api)/' | \
|
|
394
|
+
grep -vE '\.(test|spec)\.[jt]sx?$|__tests__/|/web/|/frontend/|/client/' | head -20)
|
|
395
|
+
|
|
396
|
+
if [ -z "$AUTH_TOUCHED" ]; then
|
|
397
|
+
echo "P17 N/A: no backend auth-adjacent files in diff"
|
|
398
|
+
else
|
|
399
|
+
TOUCHED_COUNT=$(printf '%s\n' "$AUTH_TOUCHED" | wc -l | tr -d ' ')
|
|
400
|
+
|
|
401
|
+
# Explicit opt-out (Gemini R1 M4 strengthened: require `: <reason ≥ 1 word>`)
|
|
402
|
+
OPTOUT=$(grep -E 'P17[[:space:]]+(N/A|opt[- ]?out|exempt)[[:space:]]*:[[:space:]]+[A-Za-z0-9].{2,}' "$TICKET" 2>/dev/null | head -1)
|
|
403
|
+
if [ -n "$OPTOUT" ]; then
|
|
404
|
+
echo "P17 N/A: explicit opt-out — $(printf '%s' "$OPTOUT" | head -c 120)"
|
|
405
|
+
else
|
|
406
|
+
INTEGRATION_TESTS=$(git diff --name-only "$BASE_REF...HEAD" 2>/dev/null | \
|
|
407
|
+
grep -iE '(integration|__tests__/.*\.integration\.|/integration/)' | \
|
|
408
|
+
grep -E '\.(test|spec)\.[jt]sx?$')
|
|
409
|
+
|
|
410
|
+
# Two extraction passes (Codex R2 I-1 multi-line fix + Gemini R2 I-1 backticks):
|
|
411
|
+
# Pass A: dot-method calls — `.get('/path')` / `.post(\`/path\`)` (single-line).
|
|
412
|
+
# Pass B: `url: '...'` property values within multi-line app.inject({ ... }).
|
|
413
|
+
BEARER_NON_ME_HITS=0
|
|
414
|
+
while IFS= read -r test_file; do
|
|
415
|
+
[ -z "$test_file" ] && continue
|
|
416
|
+
[ ! -f "$test_file" ] && continue
|
|
417
|
+
if ! grep -qiE '(bearer|authorization)' "$test_file" 2>/dev/null; then
|
|
418
|
+
continue
|
|
419
|
+
fi
|
|
420
|
+
ROUTES_A=$(grep -iE "\.(get|post|put|patch|delete)\([\`\"'](/[^\`\"']+)[\`\"']" "$test_file" 2>/dev/null | \
|
|
421
|
+
grep -oE "[\`\"'](/[^\`\"']+)[\`\"']" | sed -E "s/^[\`\"']|[\`\"']\$//g")
|
|
422
|
+
ROUTES_B=$(grep -iE "url[[:space:]]*:[[:space:]]*[\`\"'](/[^\`\"']+)[\`\"']" "$test_file" 2>/dev/null | \
|
|
423
|
+
grep -oE "[\`\"'](/[^\`\"']+)[\`\"']" | sed -E "s/^[\`\"']|[\`\"']\$//g")
|
|
424
|
+
ROUTE_STRINGS=$(printf '%s\n%s\n' "$ROUTES_A" "$ROUTES_B" | grep -vE '^$' | sort -u)
|
|
425
|
+
# Strip ${...} template-literal interpolations
|
|
426
|
+
ROUTE_STRINGS=$(printf '%s\n' "$ROUTE_STRINGS" | sed -E 's/\$\{[^}]*\}//g')
|
|
427
|
+
if [ -n "$ROUTE_STRINGS" ]; then
|
|
428
|
+
if printf '%s\n' "$ROUTE_STRINGS" | grep -qvE '^/me($|/)' ; then
|
|
429
|
+
BEARER_NON_ME_HITS=$((BEARER_NON_ME_HITS + 1))
|
|
430
|
+
fi
|
|
431
|
+
fi
|
|
432
|
+
done <<EOF
|
|
433
|
+
$INTEGRATION_TESTS
|
|
434
|
+
EOF
|
|
435
|
+
|
|
436
|
+
if [ "$BEARER_NON_ME_HITS" -eq 0 ]; then
|
|
437
|
+
flag "P17 drift (advisory): $TOUCHED_COUNT backend auth-adjacent file(s) but no integration test exercises bearer on a non-/me route (BUG-PROD-013 / BUG-RATELIMIT-BEARER pattern). Add an integration test or annotate \`P17 N/A: <reason>\` in the ticket."
|
|
438
|
+
else
|
|
439
|
+
echo "P17 PASS: $TOUCHED_COUNT auth-adjacent backend file(s), $BEARER_NON_ME_HITS integration test(s) cover bearer non-/me routes"
|
|
440
|
+
fi
|
|
441
|
+
fi
|
|
442
|
+
fi
|
|
443
|
+
```
|
|
444
|
+
|
|
357
445
|
### Execution discipline (added v0.18.1)
|
|
358
446
|
|
|
359
|
-
For each of the
|
|
447
|
+
For each of the 17 drift checks (P1–P17), 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.
|
|
360
448
|
|
|
361
449
|
Recommended pattern:
|
|
362
450
|
|
|
@@ -375,7 +463,7 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
375
463
|
```
|
|
376
464
|
## Merge Compliance Audit — [FEATURE-ID]
|
|
377
465
|
|
|
378
|
-
### Structural (1-
|
|
466
|
+
### Structural (1-13) — blocking merge gate
|
|
379
467
|
|
|
380
468
|
| # | Check | Status | Detail |
|
|
381
469
|
|---|-------|:------:|--------|
|
|
@@ -383,7 +471,7 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
383
471
|
| 2 | Acceptance Criteria | PASS | 14/14 |
|
|
384
472
|
| 3 | Definition of Done | PASS | 7/7 |
|
|
385
473
|
| 4 | Workflow Checklist | PASS | 7/8 (Step 6 pending) |
|
|
386
|
-
| 5 | Merge Checklist Evidence | PASS |
|
|
474
|
+
| 5 | Merge Checklist Evidence | PASS | 9/9 with evidence |
|
|
387
475
|
| 6 | Completion Log | PASS | 5 entries, bugs documented |
|
|
388
476
|
| 7 | Tracker Sync | PASS | Active Session + Features table correct |
|
|
389
477
|
| 8 | key_facts.md | PASS | N/A — no new infrastructure |
|
|
@@ -391,35 +479,37 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
391
479
|
| 10 | Working Tree | PASS | Clean |
|
|
392
480
|
| 11 | Data Files | PASS | N/A — no JSON seed files |
|
|
393
481
|
| 12 | CI State | PASS | PR #123 — all checks pass or pending |
|
|
482
|
+
| 13 | MCE Action 9 (D1) | PASS | ## Audit Merge Output section pasted (~3200 chars) |
|
|
394
483
|
|
|
395
484
|
**STRUCTURAL: READY FOR MERGE** (or **STRUCTURAL: NEEDS FIX — N blockers**)
|
|
396
485
|
|
|
397
|
-
### Drift (
|
|
486
|
+
### Drift (14-30) — advisory, refresh before user authorization
|
|
398
487
|
|
|
399
488
|
| # | Pattern | Status | Detail |
|
|
400
489
|
|---|---------|:------:|--------|
|
|
401
|
-
|
|
|
402
|
-
|
|
|
403
|
-
|
|
|
404
|
-
|
|
|
405
|
-
|
|
|
406
|
-
|
|
|
407
|
-
|
|
|
408
|
-
|
|
|
409
|
-
|
|
|
410
|
-
|
|
|
411
|
-
|
|
|
412
|
-
|
|
|
413
|
-
|
|
|
414
|
-
|
|
|
415
|
-
|
|
|
416
|
-
|
|
|
490
|
+
| 14 | P1 PR body test count stale | PASS | matches ticket terminal |
|
|
491
|
+
| 15 | P2 Aspirational Evidence rows | PASS | all past-tense |
|
|
492
|
+
| 16 | P3 Post-merge actions logged | PASS | N/A pre-merge |
|
|
493
|
+
| 17 | P4 Remote branch orphan | PASS | not checked pre-merge |
|
|
494
|
+
| 18 | P5 Frozen ticket Status | PASS | 0 frozen |
|
|
495
|
+
| 19 | P6 AC count off-by-N | PASS | claim matches actual (form: header) |
|
|
496
|
+
| 20 | P7 Intra-ticket test drift | PASS | final = terminal |
|
|
497
|
+
| 21 | P8 Completion Log gap | PASS | every [x] step has narrative |
|
|
498
|
+
| 22 | P9 Tracker header stale | PASS | header = detail |
|
|
499
|
+
| 23 | P10 Duplicate log rows | PASS | no duplicates |
|
|
500
|
+
| 24 | P11 Tracker status mismatch | PASS | status consistent |
|
|
501
|
+
| 25 | P12 Tracker HEAD reference | PASS | tracker HEAD = git HEAD |
|
|
502
|
+
| 26 | P13 key_facts delta mismatch | PASS | N/A — no quantified deltas |
|
|
503
|
+
| 27 | P14 MCE Action 1 stale post-merge | PASS | N/A pre-merge / row past-tense |
|
|
504
|
+
| 28 | P15 Post-deploy AC without evidence | PASS | no post-deploy keyword in ACs |
|
|
505
|
+
| 29 | P16 Feature missing from tracker | PASS | feature in Features table |
|
|
506
|
+
| 30 | P17 Auth integration coverage | PASS | 2 backend file(s), 1 integration test covers bearer non-/me |
|
|
417
507
|
|
|
418
508
|
**DRIFT: CLEAN** (or **DRIFT: N advisories — refresh before merge**)
|
|
419
509
|
|
|
420
510
|
### Combined verdict
|
|
421
511
|
|
|
422
|
-
- Both PASS → **READY FOR MERGE** (compliance
|
|
512
|
+
- Both PASS → **READY FOR MERGE** (compliance 13/13, drift clean)
|
|
423
513
|
- Structural fail → **NEEDS FIX — N blockers**
|
|
424
514
|
- Structural pass + drift advisories → **READY FOR MERGE PENDING DRIFT CLEANUP — N advisories**
|
|
425
515
|
```
|
|
@@ -432,6 +522,18 @@ Report two tables — one for **structural (blocking)** compliance, one for **dr
|
|
|
432
522
|
|
|
433
523
|
This rule exists because an LLM auditor, given header-form tickets where checkbox count is 0, will hallucinate `18/18` to "agree" with the MCE row 1 claim. The recipe says `0` and the MCE says `18/19`; the audit Detail must reflect both honestly so the reader can spot the form mismatch.
|
|
434
524
|
|
|
525
|
+
**Audit Output Self-Verification Guarantee** (added v0.20.0, required by D1): after emitting all structural rows + drift rows + combined verdict, ALWAYS append a final single-line summary in this exact form, regardless of pass/fail:
|
|
526
|
+
|
|
527
|
+
```
|
|
528
|
+
Structural: <passed>/<total> PASS | Drift: <flagged> advisory | Verdict: <APPROVE|REVISE>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
Examples:
|
|
532
|
+
- `Structural: 13/13 PASS | Drift: 0 advisory | Verdict: APPROVE`
|
|
533
|
+
- `Structural: 11/13 PASS | Drift: 4 advisory | Verdict: REVISE`
|
|
534
|
+
|
|
535
|
+
This line is the canonical compliance signal that `D1` (MCE Action 9 present, line 92) parses. Without it, a verbatim paste of legitimate audit output may fail D1's signal check. The line must always be the last non-empty line in the output.
|
|
536
|
+
|
|
435
537
|
### If issues are found
|
|
436
538
|
|
|
437
539
|
Fix them directly:
|
|
@@ -65,12 +65,14 @@ Determine the target branch from `docs/project_notes/key_facts.md` → `branchin
|
|
|
65
65
|
|
|
66
66
|
## Action 8: Fill Merge Checklist Evidence
|
|
67
67
|
|
|
68
|
-
In the ticket, fill the `## Merge Checklist Evidence` table. For each action (0–7), mark `[x]` and write the actual evidence (not placeholders). Example:
|
|
68
|
+
In the ticket, fill the `## Merge Checklist Evidence` table. For each action (0–7), mark `[x]` and write the actual evidence (not placeholders). Then complete Action 9 below — it MUST appear as the 9th row of the same table. Example:
|
|
69
69
|
|
|
70
70
|
| Action | Done | Evidence |
|
|
71
71
|
|--------|:----:|----------|
|
|
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
|
+
| ... (rows 2-7 follow same shape) | [x] | ... |
|
|
75
|
+
| 9. Run `/audit-merge` | [x] | See § "Audit Merge Output" below |
|
|
74
76
|
|
|
75
77
|
**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
78
|
|
|
@@ -82,11 +84,30 @@ In the ticket, fill the `## Merge Checklist Evidence` table. For each action (0
|
|
|
82
84
|
|
|
83
85
|
Sub-scope tickets reach `Status: Done` independently while the parent feature's tracker row stays at its parent status (typically `pending` or `in-progress`) until ALL sub-scopes close. `/audit-merge` P11 detects the suffix and emits `P11 N/A` instead of flagging the parent/sub-scope status divergence as drift.
|
|
84
86
|
|
|
85
|
-
## Action 9: Run compliance audit
|
|
87
|
+
## Action 9: Run compliance audit (MANDATORY, added v0.20.0 as structural MCE row)
|
|
86
88
|
|
|
87
|
-
Run `/audit-merge`
|
|
89
|
+
**Action 9 is MANDATORY.** Run `/audit-merge` after Actions 0-8 are complete. Paste the FULL verbatim output (structural + drift tables + verdict + self-verification line `Structural: N/N PASS | Drift: K advisory | Verdict: X`) into the dedicated `## Audit Merge Output` section of the ticket, directly below the MCE table.
|
|
88
90
|
|
|
89
|
-
|
|
91
|
+
Do NOT abbreviate, summarize, or omit failing rows. If `/audit-merge` flags any STRUCTURAL check as FAIL, fix it and re-run until ALL structural checks PASS. Drift advisories may stay open with explanation in the surrounding ticket.
|
|
92
|
+
|
|
93
|
+
The ticket structure for Action 9 (added v0.20.0):
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
## Merge Checklist Evidence
|
|
97
|
+
|
|
98
|
+
| Action | Done | Evidence |
|
|
99
|
+
|--------|:----:|----------|
|
|
100
|
+
| 0. … | [x] | … |
|
|
101
|
+
| 1. … | [x] | … |
|
|
102
|
+
| ... (rows 2-7) | [x] | … |
|
|
103
|
+
| 9. Run `/audit-merge` | [x] | See § "Audit Merge Output" below |
|
|
104
|
+
|
|
105
|
+
## Audit Merge Output
|
|
106
|
+
|
|
107
|
+
<verbatim /audit-merge output goes here — see ticket-template.md>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The `/audit-merge` structural check `D1` (added v0.20.0) verifies row 9 exists with `[x]` AND the `## Audit Merge Output` section has non-empty body with a structural-compliance signal. Skipping this Action is now structurally visible (row 9 stays unfilled if you forget).
|
|
90
111
|
|
|
91
112
|
## Action 10: Request merge approval
|
|
92
113
|
|
|
@@ -102,6 +102,18 @@ This creates a feedback loop for improving future reviews. -->
|
|
|
102
102
|
| 5. Commit documentation | [ ] | Commit: (hash) |
|
|
103
103
|
| 6. Verify clean working tree | [ ] | `git status`: clean |
|
|
104
104
|
| 7. Verify branch up to date | [ ] | merge-base: up to date / merged origin/<branch> |
|
|
105
|
+
| 9. Run `/audit-merge` | [ ] | See § "Audit Merge Output" below |
|
|
106
|
+
|
|
107
|
+
## Audit Merge Output
|
|
108
|
+
|
|
109
|
+
> Paste the FULL verbatim output of `/audit-merge` below. The block must include both tables
|
|
110
|
+
> (structural + drift), all rows, the combined verdict, AND the final self-verification line
|
|
111
|
+
> in the form `Structural: N/N PASS | Drift: K advisory | Verdict: <APPROVE|REVISE>`. Do NOT
|
|
112
|
+
> abbreviate, summarize, or omit failing rows. Required for the D1 structural check to PASS.
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
<paste /audit-merge output here>
|
|
116
|
+
```
|
|
105
117
|
|
|
106
118
|
---
|
|
107
119
|
|
|
@@ -87,8 +87,27 @@ For each feature in the batch:
|
|
|
87
87
|
- **Lock**: If `docs/project_notes/pm-session.lock` is missing → stop (session was terminated externally).
|
|
88
88
|
- **Circuit breaker**: If 3 consecutive features are `blocked` → stop and report to user.
|
|
89
89
|
- **Context check**: If **2+ features have been completed** in this session, STOP and tell the user to run `/compact` followed by `continue pm`. Do NOT continue to the next feature — context degradation causes skipped steps, lost constraints, and poor quality. This is mandatory, not a suggestion.
|
|
90
|
+
- **Session-coherence check (added v0.20.0)**: Read `.sdd-version` from the repo root and the `**SDD version at start:** X.Y.Z` field from `docs/project_notes/pm-session.md`. If they differ → emit ADVISORY (non-blocking) once per Step transition:
|
|
91
|
+
```
|
|
92
|
+
⚠ Session-coherence drift: SDD upgraded mid-session (X.Y.Z → CURRENT).
|
|
93
|
+
Cached workflow references and rules in this session may be stale.
|
|
94
|
+
Recommendation: complete current Step's atomic work, then run `/compact`
|
|
95
|
+
followed by `continue pm` to reload templates before next Step.
|
|
96
|
+
(Non-blocking — proceed if you've reviewed the upgrade CHANGELOG and
|
|
97
|
+
confirmed no workflow-affecting changes.)
|
|
98
|
+
```
|
|
99
|
+
Do NOT auto-update the pm-session.md value here — capture the current cached-context coherence state. The field is auto-updated only by `continue pm` after `/compact` (see "Auto-update on /compact" below).
|
|
90
100
|
- **Clean workspace**: Run `git status`. If dirty, commit or stash before proceeding.
|
|
91
101
|
|
|
102
|
+
##### Auto-update on `/compact` (added v0.20.0)
|
|
103
|
+
|
|
104
|
+
When `continue pm` resumes the orchestrator after a `/compact`:
|
|
105
|
+
1. Re-read current `.sdd-version`.
|
|
106
|
+
2. Update `**SDD version at start:** X.Y.Z` in `pm-session.md` to the current value.
|
|
107
|
+
3. Reset the per-Step advisory-emitted flag.
|
|
108
|
+
|
|
109
|
+
Rationale: `/compact` reloads the cached context from the filesystem. After /compact, the agent is operating on current SDD templates, so the `SDD version at start` field should reflect that. The drift advisory above targets the WINDOW between an SDD upgrade and the next /compact — that window is the risk zone.
|
|
110
|
+
|
|
92
111
|
#### b. Start Feature
|
|
93
112
|
|
|
94
113
|
1. Update `pm-session.md`:
|