feat: a tauceti-coverage:v1 header with the report's verdict on each layer - #14
Conversation
…layer A STATUS.md may now carry a second machine header beside tauceti-status:v1: the report's state for each layer of the roadmap (done, partial, untouched, or unassessed when the material says nothing), with one line on what remains, in a form a script can read. The prose already says these things; the header exists so forty roadmaps' worth of them can be put on one page. The model still only writes prose. `plan` extracts the roadmap's layers from its README (the Layer / Lane / Part / Stage headings, or L0A-style labels) and records them with a SHA-256 of the README; the prompt asks for a trailing ```coverage block, one line per listed layer; `apply` removes the block from the prose, checks it names every listed layer once and nothing else, and writes the header; the gate treats the header as part of the canonical prefix and validates its closed schema. A body with no block is a report with no header, as before. The README hash is what binds an assessment to the specification it was made against, since a layer's requirements can change under an unchanged heading; the consumer refuses a header whose hash does not match the README it read the layers from. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e-only status prompt Three things review asked for, none a change to the header itself. The scope of this first version is now stated: `plan` reads the selected labelled area's own README and nothing below it. An umbrella area whose README is an index of sub-roadmaps (RepresentationTheory) has no layer headings, so its reports carry no header and the children's layers are not in the plan at all; on the Progress page they stay on hand transcriptions. The README says so, and a planner test builds an umbrella with two layered children and asserts the plan lists no layers. A second planner test asserts the exact inventory and README hash for an ordinary area, not merely that a plan builds. The standalone `prompt status` template is a second interface and was silent about the block. It now says it is prose-only (it is handed no layer list, so it must not invent ids) and that a report written from it carries no coverage header; the CLI help says the same, and the prompt tests are named by the interface they exercise, with a new one holding `status.md` to that. tests/fixtures/coverage-contract/ is an offline producer/consumer fixture: a README, the model's body with its block, and expected.json recording what plan+apply emit and what TauCeti's scripts/roadmap_progress.py at 9477d5c made of it (ids, lines, states, notes, and the refusals against an edited README body, an edited title with stable ids, and another library commit). test_contract.py proves the producer still emits that header byte for byte, that the gate accepts the pair, and that a body without a block yields a plain report. Nothing is fetched in CI. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The model now writes a JSON array inside the trailing ```coverage block instead of a line mini-language: split_block decodes it and coverage() hands the plan-owned envelope to files.require_coverage, the gate's own validator, then checks the plan's inventory (the one thing the gate cannot check) and restores README order. A present block that is not JSON is an error, never "no coverage". The wire format is unchanged. The contract test and its fixtures shrink to a README, a model body and the recorded header line, folded into the apply tests; the schema and block refusals become labeled tables. The README carries the schema, trust boundary, missing-block behaviour, README hash, scope and rollout once, and the module prose that repeated them is cut. Checked: tests/run green; the fixture header accepted by TauCeti's scripts/roadmap_progress.py at b48d6c9, and refused against an edited README and another library commit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review of
|
split_block returned None both for "no block" and for a block whose JSON decoded to null, so a null block was dropped and the report published without a coverage header instead of being refused. Reject null in split_block, with an apply-level test that null is refused, an absent block still gives a headerless report, and a full block still round-trips. Also correct the fixture's point count (y^2 = x^3 + 1 over F_7 has 11 affine points, 12 projective) and re-record its README hash and header; the consumer at the recorded revision accepts the new header. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Addressed in 5ba2711. 1.
2. Fixture point count.
|
|
The producer/consumer contract looks sound, and the hand-transcription gap on the Progress page is real. I have two follow-ups:
|
A new report changes the report hash, which retires the site's hand transcription of the old one, so publishing it headerless would turn assessed layers into unassessed ones. When the plan lists layers the worker now refuses a body with no ```coverage block; the model can always say `unassessed`. The gate still accepts a status file without the header, as every report before the header looks. The prompt says the block is required, and an apply-level test pins both halves. The README gains a rollout note on leaving the hand transcriptions: nothing is backfilled, top-level areas move over at their next report, and the twelve RepresentationTheory children stay hand-maintained (retiring together at each umbrella report) until a follow-up gives each child a carrier of its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RepresentationTheory is one labelled area with one report, and its twelve sub-roadmaps (105 layers) could only be assessed by hand transcription, all of which a new umbrella report retires at once. Now: - plan lists `sub_roadmaps`: each directory directly below the area with a README.md and a Suggested.lean (the consumer's rule), with layers, its `Area/Child` id, README path and its own readme_sha; - the prompt points the model at those READMEs and, for an umbrella, asks for a JSON object keyed by roadmap id instead of an array; - apply writes one tauceti-coverage:v1 header per sub-roadmap, bound to its own README, after the area's own and in ascending order of name, and refuses a block that leaves a sub-roadmap or a layer out; - the gate validates each (roadmap `Area/Child` directly below the status file's area, at most 32) as part of the canonical prefix. A consumer that does not know these headers ignores them. The new coverage-contract-umbrella fixture records the header lines TauCeti's roadmap_progress.py (with the matching change, 8809a66) was checked accepting; against TauCetiRoadmap 9fad8ac the planner and the consumer agree on all twelve children, and a worst-case report is 34 KB of 64. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Addressed in b9cad58 and d5a4e09. 1. When the plan lists layers, 2. Rather than leave the children on hand transcription, this PR now covers them. For an umbrella area the plan lists each sub-roadmap, a directory below the area with a README and a The consumer change is TauCetiProject/TauCeti#8786: a child reads its own marker from the inherited report. The consumer before that change ignores these lines, so the two can merge in either order. Against TauCetiRoadmap Nothing is backfilled: each roadmap switches to headers at its next report, and its transcription entry can then be deleted. The README's rollout note and the PR description are updated to match. |
The prompt called the facts file ground truth and said a result not in it did not land, while the coverage block describes the whole roadmap. Read cautiously, that turns every layer finished in an earlier window into `unassessed` on the Progress page, which matters far more now that an umbrella report states all 105 RepresentationTheory layers. The facts file is now ground truth for this window; the previous STATUS.md (and its coverage headers) and PROGRESS.md are the evidence for earlier ones, and their verdicts stand unless this window or a changed README gives a reason to revise them. A prompt test pins the wording. A `remaining` note could hold a lone surrogate spelled as a JSON escape, which passed validation and then failed with UnicodeEncodeError when the header was written unescaped. REMAINING_RE now excludes surrogates, so it is a FormatError at the block like any other malformed note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a second machine header to
STATUS.md,tauceti-coverage:v1: the report's verdict on each layer of the roadmap (done / partial / untouched / unassessed, with one line on what remains), in a form a script can read. The prose already says these things; the header exists so forty roadmaps' worth of them can be put on one page without a person re-reading every report. The consumer is the Progress page on the TauCeti site (TauCetiProject/TauCeti#7502, merged), which today reads the same verdicts from a hand-transcribed file and reads them from this header when a report carries one.What changes
The model supplies untrusted prose and assessments; code decides identity, window, validation, serialisation and publication.
planrecords the roadmap's layers in the plan: the README'sLayer/Lane/Part/Stageheadings (orL0A-style labels), each with its id, title and line, plusreadme_sha, a SHA-256 of the README's text. For an umbrella area it also listssub_roadmaps, each with its own layers and README hash (below). A README with no such headings gives no layers. The extraction rule is the consumer's.```coverageblock holding a JSON array, one{"id", "state", "remaining"?}object per listed id. For an umbrella it is instead an object keyed by roadmap id, each value such an array. The state is one of four words; the note is optional, one line, at most 200 characters.applyremoves the block from the prose, decodes it, hands the plan-owned envelope (roadmap, commit, README hash) tofiles.require_coverage, then checks the one thing the gate cannot: that every listed layer appears exactly once and nothing else, restoring README order. A block that is present but not JSON (including JSONnull), or does not fit, is refused on the worker. So is a missing block when the plan lists layers: a new report retires the site's hand transcription of the old one, so a headerless report would turn assessed layers into unassessed ones, and the model always hasunassessedfor a layer it cannot judge.files.py) treat the header as part of the canonical prefix: the line after the status header and nowhere else, the same roadmap and commit, a closed schema (bounded layer count, restricted ids, notes without angle brackets so nothing can close the HTML comment).require_coverageis the one schema definition and runs at both trust boundaries. A status file without the header is unchanged and still passes the gate.The gate proves the header's shape, never its truth, as for the prose. It cannot check
readme_sha(it never checks out the roadmap repository); the consumer does, and refuses a header whose hash does not match the README it reads, showing those layers as unassessed rather than applying an old verdict to new requirements. Layer ids alone are not a specification identity: requirements change under unchanged headings.Umbrella areas
RepresentationTheory is one labelled area with one report, so that report is the only account of its twelve sub-roadmaps (105 layers). A sub-roadmap is a directory directly below the area with a
README.mdand aSuggested.lean, which is the consumer's rule.planlists each one undersub_roadmaps, with itsArea/Childid, README path, layers and its ownreadme_sha. The model reads those READMEs and answers with a block keyed by roadmap id.applywrites onetauceti-coverage:v1line per sub-roadmap, withroadmapset toRepresentationTheory/<Child>and bound to that child's README, after the area's own line (if its README has layers) and in ascending order of name. The gate holds each to the same schema, requires a child directly below the status file's own area, caps them at 32, and treats all of them as part of the canonical prefix. The consumer change is TauCetiProject/TauCeti#8786; the consumer before it ignores these lines.The standalone
prompt statustemplate is handed no layer list, stays prose-only, and says so.Files
progress/layers.py(new): heading extraction, the block splitter, and the assembly of validated payloads, one per roadmap for an umbrella.progress/files.py: the marker, its schema, the prefix, parsing and the reserved-marker scan.progress/plan.py,progress/apply.py: carrying the layers and sub-roadmaps, and turning the block into the headers.progress/prompts/progress.md: the block instructions.README.md: the schema, trust boundary, missing-block behaviour, README hash, scope and rollout, in one place.tests/test_layers.py(new), and additions totest_files.py,test_apply_announce.py,test_window.pyandtest_prompts.py. Refusals are labeled tables.tests/fixtures/coverage-contract/holds a README, a model body with its block, and the exact header line the consumer was recorded accepting; the apply tests prove the producer still emits it byte for byte, that the gate accepts the pair, and that editing the README changes the hash under stable ids.tests/fixtures/coverage-contract-umbrella/does the same for an umbrella with two sub-roadmaps and areferences/folder that is not one. Nothing is fetched in CI../tests/runpasses in full.Cross-check with the consumer
Run locally, not in CI: the fixture header is accepted by TauCeti's
scripts/roadmap_progress.pyatb48d6c9with the intended states and notes, and refused by it against a README with an added requirement, a README with a retitled layer under a stable id, and another library commit. The umbrella fixture's two sub-roadmap lines are accepted by the consumer with TauCeti#8786 (8809a66), and a child's line is refused once that child's README is edited. Against TauCetiRoadmap9fad8ac, the producer and the consumer extract identical layer ids, lines and README hashes for all 50 roadmaps and all 12 sub-roadmaps. A worst-case RepresentationTheory report, with every layer carrying a 200-character note, is 34 KB of the 64 KB cap and passes the gate.After merging
progress-merge.ymlpins this repository by SHA in two places; bump them to the merged SHA, together with the worker'sPROGRESS_REF, so the generator and the gate run one version. Until then a report carrying the header would be refused by the old gate, and one without it is accepted by both.scripts/roadmap_coverage.jsonto its header at its next report, and its entry there can then be deleted. The sub-roadmaps need TauCeti#8786 merged by then.🤖 Generated with Claude Code