What happens
A checkbox marker the parser does not recognise is dropped from progress entirely — it counts toward neither the numerator nor the denominator. A change whose remaining work is written with such a marker therefore reports ✓ Complete, and openspec archive raises no incomplete-task warning for it.
This is the same failure mode as #1485 (indented sub-tasks invisible to progress), reached through a different door: there the bullet was unmatched because of its indentation, here because of the character inside the brackets.
Reproduce (parser as of main, verified against src/utils/task-progress.ts)
openspec/changes/my-change/tasks.md:
## 1. Implementation
- [x] 1.1 Done
- [~] 1.2 Deliberately deferred, not done
- [~] 1.3 Also deferred, not done
$ openspec list
Changes:
my-change ✓ Complete just now
Expected: something that does not read as finished — 1/3, or 1/1 (2 unrecognised), or a warning naming the dropped lines.
Observed in the wild on a real change with 42 [x] · 17 [~] · 0 [ ]: reported ✓ Complete, while seventeen items were open, one of them a genuine missing test. The same change reported 22/48 before the deferred items were marked — i.e. the seventeen lines left the denominator when their marker changed, rather than staying counted as not-done.
Cause
src/utils/task-progress.ts:
const TASK_LINE_PATTERN = /^\s*[-*]\s*\[([\sxX])\]\s*(.*)/;
The character class accepts only whitespace, x and X, so - [~] … does not match at all. [X]-vs-[x] and indentation are handled; an unknown marker is not.
Why this seems worth a fix rather than a docs note
The pattern's own docblock already argues the principle:
any character class tightened here … drops lines that used to count, and a task this parser drops is a task openspec archive stops warning about.
An unrecognised marker is exactly such a dropped line — it just arrives from the other direction. The current behaviour is the silent failure the comment warns about: [x] overstates only if someone ticks it, whereas an unknown marker overstates by default, with no one having claimed anything is done.
Two shapes would both fix it, and either is fine from our side:
- Fail-safe parse — widen the class to any single non-
] character and treat only x/X as done (/^\s*[-*]\s*\[([^\]])\]\s*(.*)/). Unknown markers then count as not-done, which is the conservative reading and needs no new concept.
- Report them — keep the strict class but surface unmatched checkbox-like lines, so they cannot vanish quietly (
openspec list suffix, or an archive warning).
Context on the marker
[~] is not an OpenSpec concept — it is a downstream convention in our repo for deliberately deferred, with a stated reason, enforced by our own CI. We are not asking OpenSpec to adopt it. The report is only that an unknown marker currently disappears instead of counting as not-done; whatever character a project happens to use, the safe default seems to be "not done" rather than "not there".
Related: #1485 (same symptom, indentation), #1362 (proposal for human-only tasks — a marker with similar needs).
What happens
A checkbox marker the parser does not recognise is dropped from progress entirely — it counts toward neither the numerator nor the denominator. A change whose remaining work is written with such a marker therefore reports
✓ Complete, andopenspec archiveraises no incomplete-task warning for it.This is the same failure mode as #1485 (indented sub-tasks invisible to progress), reached through a different door: there the bullet was unmatched because of its indentation, here because of the character inside the brackets.
Reproduce (parser as of
main, verified againstsrc/utils/task-progress.ts)openspec/changes/my-change/tasks.md:Expected: something that does not read as finished —
1/3, or1/1 (2 unrecognised), or a warning naming the dropped lines.Observed in the wild on a real change with 42
[x]· 17[~]· 0[ ]: reported✓ Complete, while seventeen items were open, one of them a genuine missing test. The same change reported22/48before the deferred items were marked — i.e. the seventeen lines left the denominator when their marker changed, rather than staying counted as not-done.Cause
src/utils/task-progress.ts:The character class accepts only whitespace,
xandX, so- [~] …does not match at all.[X]-vs-[x]and indentation are handled; an unknown marker is not.Why this seems worth a fix rather than a docs note
The pattern's own docblock already argues the principle:
An unrecognised marker is exactly such a dropped line — it just arrives from the other direction. The current behaviour is the silent failure the comment warns about:
[x]overstates only if someone ticks it, whereas an unknown marker overstates by default, with no one having claimed anything is done.Two shapes would both fix it, and either is fine from our side:
]character and treat onlyx/Xas done (/^\s*[-*]\s*\[([^\]])\]\s*(.*)/). Unknown markers then count as not-done, which is the conservative reading and needs no new concept.openspec listsuffix, or anarchivewarning).Context on the marker
[~]is not an OpenSpec concept — it is a downstream convention in our repo for deliberately deferred, with a stated reason, enforced by our own CI. We are not asking OpenSpec to adopt it. The report is only that an unknown marker currently disappears instead of counting as not-done; whatever character a project happens to use, the safe default seems to be "not done" rather than "not there".Related: #1485 (same symptom, indentation), #1362 (proposal for human-only tasks — a marker with similar needs).