fix(#1639): parseDecisions handles the titled-colon bullet form (#1665)

* fix(#1639): parseDecisions handles titled-colon bullet form

bulletColonRe anchors on ':**' (colon immediately before close-bold) and bulletEmDashRe
requires an em-dash, so the titled-colon form '- **D-NN: Title.** body' (title between the
colon and the closing **) matched neither and was dropped by the parse-miss guard. When all
decisions used the titled convention, parseDecisions returned 0 and check.decision-coverage-
plan passed vacuously — the same false-coverage failure mode as #1343/#1364/#1365. Add a
third per-form regex bulletTitledColonRe, checked LAST (strict superset of bulletColonRe,
so it only catches bullets the other two miss — minimal blast radius); id + [tags]
trackability honored. Regression folded into decisions.test.cjs: titled-colon parses,
coexists with colon/em-dash, tags, all-titled-13 no longer vacuously 0.

* fix(#1639): tighten titled-colon title to [^:*]* so malformed pre-colon-run bullets still reject

The first cut's title run [^*]* was too permissive: it matched a genuinely-malformed
bullet with a colon in the pre-separator freeform run (e.g. 'D-07 ratio 3:1:**') by
treating the 3:1 colon as the separator, regressing the #1343 parse-miss guard tests.
Tighten the title to [^:*]* (no colon, no star) so the separator colon remains the only
colon permitted before ** — matching bulletColonRe's existing [^:*]* discipline. Valid
titled forms (colon-free titles) still parse; the malformed colon-in-freeform case still
falls through to the parse-miss guard.

* chore(#1639): backfill changeset pr ref to 1665
This commit is contained in:
Tom Boucher
2026-06-24 13:26:25 -04:00
committed by GitHub
parent 4631923982
commit b205e4c2b2
3 changed files with 71 additions and 0 deletions

View File

@@ -791,3 +791,40 @@ describe('FIX D: gap-checker surfaces decision could-not-parse even when require
);
});
});
// ─── Regression #1639: titled-colon bullet form '- **D-NN: Title.** body' ─────
// Both bulletColonRe (':**' anchor) and bulletEmDashRe (em-dash) miss the form where a
// title sits between the colon and the closing **, so it was dropped by the parse-miss
// guard and check.decision-coverage-plan passed vacuously when all decisions were titled.
describe('parseDecisions — titled-colon bullet form (#1639)', () => {
test('titled-colon bullet is parsed, not dropped', () => {
const md = '## Locked decisions\n- **D-01: Default sandbox ON.** body\n- **D-02: Reject unsigned.** body two\n';
const out = parseDecisions(md);
assert.equal(out.length, 2, 'should extract both titled-colon decisions (not 0)');
assert.equal(out[0].id, 'D-01');
assert.equal(out[1].id, 'D-02');
});
test('titled-colon coexists with colon-immediate and em-dash forms', () => {
const md = '## Locked decisions\n- **D-01:** plain colon\n- **D-02 — emdash** body\n- **D-03: Titled.** body\n';
const out = parseDecisions(md);
assert.equal(out.length, 3);
assert.deepEqual(out.map((d) => d.id), ['D-01', 'D-02', 'D-03']);
});
test('titled-colon with [tags] still parses id and tags', () => {
const md = '## Locked decisions\n- **D-01 [informational]: Title.** body\n';
const out = parseDecisions(md);
assert.equal(out.length, 1);
assert.equal(out[0].id, 'D-01');
assert.ok(out[0].tags.includes('informational'), `tags should include informational, got ${JSON.stringify(out[0].tags)}`);
});
test('all-titled CONTEXT.md parses every decision (no vacuous 0)', () => {
// The reporter case: 13 decisions all titled → previously all dropped → gate passed vacuously.
let md = '## Locked decisions\n';
for (let i = 1; i <= 13; i++) md += `- **D-${String(i).padStart(2, '0')}: Decision ${i}.** body\n`;
const out = parseDecisions(md);
assert.equal(out.length, 13, 'all 13 titled-colon decisions must parse (not vacuously 0)');
});
});