* fix(#1343): parse decision bullets with text before the colon parseDecisions() silently dropped any `- **D-NN ...:**` decision bullet whose header had freeform text (a parenthetical, em-dash, or prose) before the `:**`, so the blocking check.decision-coverage-plan gate computed coverage over a narrowed set and reported a false pass. - Broaden bulletRe to tolerate a freeform run before the colon while preserving the optional [bracket] tag capture (drives `trackable`). - Add a parse-miss guard: a line that looks like a D-NN bullet but still fails the regex flushes the current decision and warns instead of vanishing — the gate-integrity floor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#1343): add changeset for decision-coverage false-pass fix Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#1343): relocate decision-parser regression into owning module test file CI's lint-regression-test-names bans new bug-NNNN-*.test.cjs files. Move the 9 regression cases from tests/bug-1343-parsedecisions-drop.test.cjs into the owning parser test file tests/post-planning-gaps-2493.test.cjs (which already exercises parseDecisions) and delete the banned file. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -70,7 +70,13 @@ export function parseDecisions(content: unknown): Decision[] {
|
||||
// in addition to numeric-only IDs (D-42). The first character after `D-` must
|
||||
// be alphanumeric, so malformed shapes like `D--foo` or `D-_bar` are rejected.
|
||||
// CJS callers consume {id, text} and ignore the optional extras.
|
||||
const bulletRe = /^\s*-\s+\*\*D-([A-Za-z0-9][A-Za-z0-9_-]*)(?:\s*\[([^\]]+)\])?\s*:\*\*\s*(.*)$/;
|
||||
// #1343: `[^:*]*` replaces the old `\s*` before `:**` so that a freeform run
|
||||
// such as `(parenthetical)`, an em-dash, or other prose between the optional
|
||||
// bracket-tag group and the closing `:**` is tolerated rather than silently
|
||||
// dropping the whole decision. `[^:*]*` subsumes plain whitespace and stops
|
||||
// correctly at `:**`. Capture groups 1 (id), 2 (bracket tags), 3 (text) are
|
||||
// unchanged.
|
||||
const bulletRe = /^\s*-\s+\*\*D-([A-Za-z0-9][A-Za-z0-9_-]*)(?:\s*\[([^\]]+)\])?[^:*]*:\*\*\s*(.*)$/;
|
||||
let current: Decision | null = null;
|
||||
const flush = (): void => {
|
||||
if (current) {
|
||||
@@ -111,6 +117,18 @@ export function parseDecisions(content: unknown): Decision[] {
|
||||
current = { id, text: bulletMatch[3], category, tags, trackable };
|
||||
continue;
|
||||
}
|
||||
// Parse-miss guard (#1343): a line that looks like a `D-NN` decision bullet
|
||||
// but failed `bulletRe` (e.g. a `:` or `*` inside the pre-colon run) must NOT
|
||||
// be silently dropped — a narrowed trackable set lets a blocking coverage gate
|
||||
// report a false pass. Surface it loudly instead.
|
||||
if (/^\s*-\s+\*\*D-/.test(line)) {
|
||||
// A malformed D-bullet still starts a (failed) new decision, so it ends the
|
||||
// previous one — flush before warning so a following continuation line cannot
|
||||
// be mis-appended to the prior valid decision.
|
||||
flush();
|
||||
console.warn(`parseDecisions: ignored unparseable decision bullet: ${trimmed}`);
|
||||
continue;
|
||||
}
|
||||
// Continuation line for current decision (indented with space OR tab,
|
||||
// non-bullet, non-empty) — tab indentation must work too (review F12).
|
||||
if (current && trimmed !== '' && !trimmed.startsWith('-') && /^[ \t]/.test(line)) {
|
||||
|
||||
Reference in New Issue
Block a user