Skip to content

check-doc-checkpoint cannot see PARTIAL or ANCHOR-BACKFILL, so 188 rows can change lifecycle state with no gate #1434

Description

@localai-bot

scripts/check-doc-checkpoint.py enforces CLAUDE.md's rule that docs/STATUS.md, docs/BENCHMARKS.md and the moved row spec's ## Now are owed when a row changes lifecycle state (CLAUDE.md:409-414). It recognises the state by matching a backticked token against a fixed tuple (:56-66):

STATES = ("TODO", "READY", "ACTIVE", "GATING", "BLOCKED", "DONE", "DROPPED", "N/A")
STATE_CELL = re.compile(r"`(" + "|".join(re.escape(s) for s in STATES) + r")`")

PARTIAL and ANCHOR-BACKFILL are not in that tuple, and both are lifecycle states in active use.

.agents/feature-matrix.md:20 declares them explicitly: "PARTIAL, BLOCKED, and ANCHOR-BACKFILL as explicit non-done states." BLOCKED is in the tuple. The other two are not.

Scale

Counting backticked state cells across .agents/*-matrix.md, using the checker's own STATE_CELL shape:

state cells in STATES?
PARTIAL 115 no
ANCHOR-BACKFILL 73 no
DONE 73 yes
BLOCKED 9 yes

PARTIAL — 115 cells across six matrices (engine 30, model 28, feature 25, quantization 14, backend 11, kernel 7) — is more used than DONE, and it is the one the gate cannot see. Together the two unguarded states cover 188 rows.

The consequence

A row moving into or out of PARTIAL or ANCHOR-BACKFILL owes docs/STATUS.md and its spec's ## Now under the prose rule, and no gate can observe that it moved. The state cell never matches STATE_CELL, so the change is not a lifecycle change as far as the checker is concerned, and the projection surfaces silently go stale.

This is not hypothetical. It surfaced during the fresh review of LOAD-GGUF-MMPROJ (#821 W1), whose row moves READYPARTIAL. The reviewer proved the instrument was live — flipping the row to DONE in a scratch commit reds it (rc 1, naming docs/BENCHMARKS.md and the spec's ## Now) — then reverted docs/STATUS.md, then docs/USAGE.md, then all of docs/, and got rc 0 every time. The green was honest (no checker was weakened, and no scripts/ change was in that diff) but vacuous: the row owed the checker nothing because its destination state is invisible.

That is the shape CLAUDE.md's "Changing the rules or a checker" section warns about — the checker's behaviour and the prose rule diverging, with no gate over the divergence.

Why this is filed rather than fixed in flow

Adding two strings to a tuple is a one-line edit, but it is a semantic checker change: it moves 188 rows into the population the gate enforces against, and the first change to touch any of them then owes STATUS/BENCHMARKS/## Now where it previously did not. That needs a spec, a red-before test, and a decision about whether ANCHOR-BACKFILL — which reads more like a maintenance marker than a lifecycle state — belongs in the same tuple as PARTIAL at all.

It is also exactly the ratchet situation that has already reddened main once: .agents/issue-index.md records #1376, where filling a spec's ## Gates moved a row into the runnable population and RUNNABLE_BASELINE was not re-pinned in the same change. Widening STATES has the same shape and wants the same care.

Suggested check when this is fixed

Take a row currently in PARTIAL, move it to ACTIVE in a scratch commit without touching docs/STATUS.md, and confirm the checker reds. Today it does not.

Verified at 63d87805c.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions