SIGN IN SIGN UP

fix(tasks): count unrecognised checkbox markers as not done (#1773)

* fix(tasks): count unrecognised checkbox markers as not done

A checkbox marker the task parser did not recognise was dropped from
progress entirely: it counted toward neither the numerator nor the
denominator. A tasks.md whose remaining work was written `- [~] ...`
therefore reported `✓ Complete` in `openspec list`/`status`, and
`openspec archive` raised no incomplete-task warning for it. Marking
open items with such a marker *shrank* the denominator instead of
leaving them counted as not-done.

Widen the marker to any single non-`]` character and keep `x`/`X` as
the only done state, so an unrecognised marker reads as not-done - the
fail-safe direction the pattern's own docblock argues for. OpenSpec
adopts no new marker semantics; it just stops losing the line.

Closes #1761

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(tasks): close the remaining silent-loss holes in checkbox parsing

Hardening pass on the #1761 fix.

Parser: an empty `[]` and a padded `[ x]` were dropped exactly the way
`[~]` was - checkbox-like lines counting toward neither the numerator
nor the denominator, so archive stopped warning about them. The marker
is now "at most one non-whitespace token", which covers all three.
Multi-character brackets stay unmatched on purpose: widening to
`[^\]]*` would match `- [Some doc](./doc.md)`, whose `]` is followed by
`(` rather than a space, and turn every Markdown link list into phantom
unfinished work. The docblock states that boundary and a test pins it.

Guidance: archive, bulk-archive and verify told agents to count
`- [ ]` vs `- [x]` by hand, which reproduced the same bug one layer up -
an agent following it saw no incomplete tasks in a `[~]` file even with
the CLI fixed. All six bodies (skill + command per workflow) now state
the rule the parser implements; skills/ mirror and parity hashes
regenerated.

Coverage now spans every consumer of the shared counter: `openspec
list`, archive's gate, the apply task list, validate's task-numbering
check and the parser itself, including the reported 42-done/17-deferred
ratio. Reverting only src/utils/task-progress.ts fails 7 of them.

Verified empirically: the widened pattern newly matches 0 lines across
all 1090 .md/.ts files in the repo, templates and docs included.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(tasks): state the padded-tick rule in workflow guidance too

CodeRabbit review, verified and valid: the new guidance named only
exact `[x]`/`[X]` as complete, while the parser also counts `[ x]`,
`[x ]` and `[ x ]`. An agent hand-counting by that wording would have
reported a padded tick as unfinished work and disagreed with `openspec
list` - the same guidance/CLI split this PR set out to close.

All six bodies now phrase it as the parser implements it: complete
means the box holds only `x`/`X`, spacing inside the brackets ignored;
every other marker, including an empty box, is incomplete. skills/
mirror and parity hashes regenerated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(tasks): keep one-character link bullets out of the task count

alfred-openspec on #1773: the widened marker class let a one-character
Markdown link label parse as a checkbox, so `- [A](https://example.com)` and
`- [1](./one)` read as unfinished tasks and made progress and archive report
phantom work. The multi-character guard did not cover them: a one-character
label is one token.

The closing bracket may now not be followed by `(` or `[`, the only two
characters that continue Markdown link syntax. Nothing that used to count is
lost: a checkbox is followed by its description or by end of line, and
`- [x]done` still parses. Regressions cover both link forms, the reference
form, and a link inside a real task description.

Also updates the canonical contract, which said tasks not written `- [ ]` are
not tracked while this change deliberately tracks `[~]`, `[-]`, `[]` and a
padded `x`. Fixed in schemas/spec-driven/schema.yaml and in the docs-lab page
that quotes it, so the quote stays faithful.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs: say that a done checkbox is case-insensitive

CodeRabbit on #1773: the parser lowercases the marker before comparing it with
`x`, so `- [X]` is done, but the contract I added said only `- [x]` counts.
Corrected in both copies, which are the same text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(tasks): keep empty boxes followed by link syntax in the task count

The one-character link guard also dropped `- [ ](...)` and `- [ ][...]`,
which the strict pre-#1761 pattern counted as unfinished tasks. A line the
parser drops is one archive stops warning about, so a whitespace-only box
now bypasses the guard. The canonical tasks instruction also names `[-]`
and the padded `x` rule, in schema.yaml and docs-lab in lockstep.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(changeset): drop em dashes and state the padded-x done rule

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
C
Clay Good committed
11a9691524bad84a575854bf6dc5124f630479ba
Parent: 62106f4
Committed by GitHub <noreply@github.com> on 9/16/2026, 10:37:25 PM