Development record
One ingest verb validates every cold-reading output — including what the reading was licensed to produce, not only what it saw
Press Release
A reading that quietly exceeds its licence is refused at ingest. The output contract carries, per item, an identifier the ingest verb mints (adr-45 mint, run-scoped — the reading itself holds no mint, so ids are assigned at validation, never self-supplied) and, per run, the instrument name, manifest reference, target state, and regime value. Ingest validates before any durable record is written — malformed output is rejected without partial writes — and then checks the supply regime: a detection-position output attaching a proposed resolution, a comparative output containing an ordering, a score or a single named recommendation, an entailment output in which a surfaced claim arrives already dispositioned — each is refused and the offending item named. The failure this catches is silent everywhere else: such an output would pass every structural test while violating the one property the position is defined by.
Ruled
- Ruled (maintainer, 2026-08-28; decision log): build the supply-regime check now, as
a regime field on the output contract validated at ingest — the form
drafted here. Ground: a reading that quietly proposes or aggregates
passes every other planned test; the read-block eval covers what a
reading saw, never what it was licensed to produce, and the failure is
silent. The ruling matches the shape implemented below — structural
signatures at
evaluative(aggregation) andregistrative(fix-proposing),explicativechecked as record shape rather than prose, and the record-and-flag degradation path for noisy signatures. Still open (recorded open question): whether the signatures lint cleanly in practice.
What's In Scope
- The ingest verb: JSON in, validation, durable reading records out; the read-block and contract written in one place.
- The regime table, checked structurally at ingest against a strict schema
(unknown fields are refused, so every violation is a named field, never
a guess):
generative(no regime-specific refusal — the constraint falls on admission at the dispositioning end);explicative(the input schema carries no disposition field, so a dispositioned claim is refused as an unknown field — the violation is impossible to express, not merely caught);evaluative(refuses an explicit rank or score field, or an item marked recommended; arrangement order is never refused — items arrive in document order by mandate);registrative(refuses a resolution field attached to a detection). Semantic violations — prose that ranks, settles, or proposes without the fields — are checked too; any signature degrades only on observed noise, per the rung bullet below, never pre-emptively. - Item shapes are position-typed and validated per position: generative — configuration / what-admits-it; explicative — claim / type / what-implies-it; evaluative — candidate / criterion / characterisation; registrative — tension / constraint-in-play / why. The pattern named is an envelope field, validated once for every position; the strict schema is per-position and unknown fields still refuse.
- The regime's source of truth is the definition, resolved through the run's position: an output's self-declared regime that mismatches it is itself a refusal; no operator input can set a regime.
- Named provenance is enforced here, for every regime: each item carries a non-empty pattern-basis field or ingest refuses. The definitions instruct it; this contract enforces it; nothing else does.
- Ingest is staged: outputs validate into a write-aside area, records move together, and the run-metadata record lands last as the commit marker — an orphaned stage found on the next invocation is reported and cleared, so a crash mid-ingest leaves evidence, never half a run.
- The manifest reference is the content hash of the assembler's manifest — the one unforgeable form; a reference that resolves to nothing, or to a manifest whose hash disagrees, refuses the run.
- Instrument identity in run metadata (ruled 2026-08-28): "instrument name" comprises the model identity, the definition's content hash, and the assembler version — two runs claiming the same instrument are thereby provably the same, which the closing-run comparison requires.
- Refusal granularity: an item-level violation refuses the item and lands the rest; a list-level violation refuses the run. A refused run still writes a refusal record — run metadata and the named reason, no items — so the event is durable and a rerun is a new run.
- Enforcement per the ruling: every check ships enforced —
refusal is the default at birth. The degradation path is reserved, not
pre-taken: only where a signature proves noisy in practice does that
check degrade to record-and-flag, and the degradation is itself a
recorded decision that weakens the claimed property from enforced to
observed, said out loud per
loud-staging. (2026-08-28 review: the earlier observed-first stance for semantic signatures pre-empted the recorded open question, and is corrected here. Standing tension with the repo's widen-options promotion clause — "calibrated before it gates" — recorded as a standing tension; the ruled design governs the instrument meanwhile.)
What's Out of Scope
- The definitions that state each regime — a sibling intent.
- Whether the regime signatures lint cleanly — untested; the degradation path exists precisely because of it.
Scope Conditions
None stated.
Acceptance Criteria
- Given a malformed reading output, when it is ingested, then ingest refuses and names the offending field, and no durable record exists for that run in the reading-record family, in the readings tree, or in the stage.
- Given a fault injected after the reading records are staged and before the run-metadata commit marker is written, when ingest runs, then no reading records and no run metadata are durable for that run, and the next invocation names the orphaned stage and clears it.
- Given a run, when its metadata is read, then the manifest reference resolves to that run's manifest — the stored hash equalling the content hash of the manifest itself — and a reference that resolves to nothing, or to a manifest whose hash disagrees, refuses the run.
- Given a
registrativeoutput whose item carries a reserved name (resolution,fix,remedy), when it is ingested, then ingest refuses and names the item's ordinal, the field, and the licence breached. - Given a
registrativeoutput whose item body REPORTS a fix the passed material carries — quoting the record's own instruction, or naming the section that says it is merged while another says it is pending — when it is ingested, then ingest lands the item and raises nothing against it. Only afix,remedyorresolutioncarried as one of the item's OWN keys refuses (ruling of 2026-09-01 on iss-2608311518056854; the registry of prose signatures this criterion was written against is withdrawn). - Given an
evaluativeoutput carrying arank,score,orderorrecommendedfield, when it is ingested, then ingest refuses and names the field. - Given an
evaluativeoutput whose items are merely arranged in an order and carry no reserved field, when it is ingested, then ingest accepts it: arrangement order is never inspected and never refused. - Given an
explicativeoutput in which a surfaced claim carries a disposition-bearing field —disposition,status, or any field outside the explicative body schema — when it is ingested, then ingest refuses and names the field. - Given an
explicativeoutput whose claim body REPORTS a disposition the passed material carries — quoting a record'sdisposition:line, or saying that a clause settles a licensing question — when it is ingested, then ingest lands the item and raises nothing against it. Only adispositionorstatuscarried as one of the item's OWN keys refuses (ruling of 2026-09-01 on iss-2608311518056854; the registry of prose signatures this criterion was written against is withdrawn). - Given a run refused at list level, when the refusal completes, then a refusal record exists carrying the run metadata and the named reason, and no reading records exist for that run.
- Given an item at any of the four regimes whose
pattern_namedenvelope field is empty or absent, when it is ingested, then ingest refuses that item, without exception at any regime. - Given an output whose self-declared regime disagrees with the regime stated by the definition its position resolves to, when it is ingested, then ingest refuses the run at list level.
- Given an accepted output, when its reading records are read, then every item carries an identifier the verb minted, and a payload supplying its own item identifier is refused as an unknown field.
Disclosed residue (ac-5 and ac-9). The gate is STRUCTURAL, and both criteria now say so: a reserved name is matched against the item's own keys and never against the words inside a value. The residue is what that costs, and it runs in one direction only.
Under-catching is the whole of it. A reading that proposes a fix, ranks a
candidate or dispositions a claim in PROSE, without carrying a field for it,
raises nothing — because nothing reads prose any more. That is deliberate. The
registry that did read prose could not tell a reading that PROPOSES from one that
REPORTS somebody else proposing, and the second is most of what a reading
legitimately does: measured against a constructed corpus of thirty-four realistic
outputs, fourteen were caught, every one for quoting the document it read. The
detector was withdrawn rather than softened, because recording the hit instead of
refusing it leaves a gate that still reads prose and still cannot make the
distinction. internal/core/reading/ingest_corpus_test.go carries that corpus and
holds the gate to it in both directions.
Over-catching is closed by construction, and what closes it is the same fold
that used to serve the detectors: keys are compared folded, so a reserved name
respelled in code points that render the same — a fullwidth disposition,
an fi ligature in fix, a zero-width space inside score — refuses as itself,
while no fold ever reaches a value. A script-confusable key is still not
folded — a Cyrillic that is not the Latin one, and no normalisation equates them —
so a reserved name spelled in a confusable script lands as an unknown field
rather than as a named licence breach. It refuses either way; what is lost is the
account of WHY. Closing that needs a confusables table, which is a new dependency
and a maintainer's decision; until it is taken, this class is open.
Their structural halves — ac-4, ac-6 and ac-8 — carry no residue of either kind, because a field is present or it is not.
Two verdicts in the ingested audit describe superseded behaviour. The
fidelity review under receipt rcp-fe3450ca55ff records ac-5 and ac-9 as MET on
the strength of a REFUSAL, which is what the verb did when that review ran. The
same review's own diverged findings are what unmade it: the semantic signatures
became review flags on 2026-08-31 and were withdrawn altogether on 2026-09-01,
and ac-5 and ac-9 above are rewritten twice over to match, so those two verdicts
now describe behaviour this repository no longer has. The Audit Notes below are
left exactly as they were ingested — a receipt records what was found, not a
document to amend — and the two criteria need re-issuing at the next audit. No
other verdict in that receipt is affected.
Grounds
- pursued: This conjecture is pursued now because the failure it catches is silent everywhere else: a reading that quietly proposes, ranks, or arrives already dispositioned passes every structural test while violating the one property its position is defined by, and the read-block eval covers only what a reading saw, never what it was licensed to produce. Building the gate later would mean trusting outputs produced before anything checked them.
Audit Notes
Fidelity review — receipt rcp-fe3450ca55ff (verifier abcd:intent-auditor claude-opus-5[1m]).
Provenance: abcd:intent-auditor@claude-opus-5[1m] · rubric_hash sha256:542ed2cd51ff938717a3f47b2b332e8d47910beec0ca7ecdfd238ae7edf5ced5 · prompt_hash sha256:9949d1a3804fb6e68dafb44570193b4d81fdc675cbd0788d44cb457f925370c6 Input attestations: diff:78f6bd1d..8ec6f42f on build/itd-185 (9 commits), merged as 1d84ce26@sha256:ddfe21ba83c51358b6b72aff6780300798143ff1b2fae279e42ea820b753b45e;
Acceptance rollup: MET 11 · MET_WITH_CONCERNS 2 · NOT_MET 0 · INCONCLUSIVE 0
Per-criterion verdicts:
- ac-1 — MET: I built the binary from a checkout of 1d84ce26 and ran nine distinct malformed shapes through the real
abcd reading ingest— not JSON at all, a wrong_type, an unknown top-level field, an unknown field nested insideinstrument, a traversal run_id (../../etc), an unknown position, a manifest_sha256 that is not a digest, an empty item list, and a second document appended after the first. Every one exited 2 and named the offending field or value verbatim in the message. The durable half is the part I measured rather than argued: I snapshotted the union of.abcd/work/issues/readings/**,.abcd/development/readings/**and.abcd/.work.local/scratch/reading-ingest/**before the nine runs and after them, and the two sets were identical — no reading record, no run directory, no refusal record, no stage. The ordering that buys this is in the code and it is the order the criterion needs:Ingestsweeps, reads, decodes and checks the envelope before it opens anything to write, andrefuse()— the only writer of a durable refusal — is not reachable untilresolveParkedManifesthas proven the run's identity. Strict decoding is genuine at every declared level:DisallowUnknownFieldsplus adec.More()check, so a trailing second document is refused rather than ignored. evidence: internal/core/reading/ingest.go:449 — "dec.DisallowUnknownFields() ... if dec.More() { return Output{}, errors.New("reading: the output carries trailing content after the document") } — measured: nine malformed shapes, all exit 2, zero durable delta across the three locations" evidence: internal/core/reading/ingest.go:465 — "func checkEnvelope(out Output) (Position, error) { — run id checked FIRST among the values a path is built from; measured: run_id "../../etc" refused before any open" evidence: internal/core/reading/ingest.go:394 — "// The run's identity is proven from here ... A refusal below is therefore recordable against that run — nothing reaches refuse() before resolveParkedManifest returns" - ac-2 — MET_WITH_CONCERNS: The first clause is fully realised and I executed it at both windows of the protocol rather than reading it. An injected fault after the stage marker and again after the ledger batch each leave no run metadata and no reading records for the run, and leave the stage as the evidence a later invocation finds. I proved that can fail, twice: neutering
rollbackRunso the sweep clears the stage but leaves the ledger records turns TestRunMetadataLandsLast/after-ledger and TestOrphanedStageIsReportedAndCleared red; writing the commit marker before the ledger batch turns TestRunMetadataLandsLast/after-ledger red withthe run metadata landed despite a fault before it. I then reproduced the sweep independently of the seam at the CLI, against a state I built by hand — a stage plus a committed rdi record plus a person's NOTES.md plus a stage entry namednot-a-run— and a valid next invocation printedcleared: orphaned stage(s) of rdg-2608310000009999androlled back: rdi-2608310000009001 removed from the ledger, removed exactly that record, and left the other two standing. THE CONCERN, and it is the criterion's own second clause failing on a reachable path: the next invocation NAMES the orphan only if it survives to a render. The sweep is the first thing under the lock, before the payload is even read, but the CLI renders the result only on success or on a refusal that recorded a run — so an invocation whose payload fails early clears the orphan and says nothing. I demonstrated it: with an orphan staged and its recordrdi-2608310000008001.mdsitting in the committed ledger, I ran an ingest whose payload fails at_type. The record was deleted from the committed tier and the entire operator output was the_typeerror; under--jsonthe render is{"error": ...}alone, withrolled_back_recordsandcleared_stagesboth dropped. That is precisely the outcome the code's own comment says must not happen, and a retry after a crash carrying a typo is an ordinary way to reach it. evidence: internal/core/reading/ingest.go:546 — "func write(...) — stage marker, then capture.IngestReading, then the manifest, then RunFileName LAST; measured: moving the marker earlier turns TestRunMetadataLandsLast red" evidence: internal/core/reading/ingest_stage_test.go:40 — "func TestRunMetadataLandsLast(t *testing.T) { for _, step := range []string{faultAfterStage, faultAfterLedger} — both windows; measured red on two independent mutations" evidence: internal/surface/cli/reading.go:185 — "if res.RefusalPath != "" { _ = render(...) } — the ONLY render on the error path; measured: a payload failing at _type deleted rdi-2608310000008001 from the committed ledger and reported nothing, in both text and --json" evidence: internal/core/reading/ingest.go:243 — "// RolledBack names the reading records the sweep REMOVED from the committed ledger ... "cleared an orphaned stage" does not tell an operator that records left the ledger with it. — the field is populated at ingest.go:368 and then discarded by the front door" - ac-3 — MET: Four adversarial probes against the real binary, plus the read-back clause the criterion opens with. A run id that resolves to no parked manifest refuses, naming the path it looked at. A manifest_sha256 of sixty-four zeros refuses, printing both the claim and the manifest's true digest. Tampering with the parked manifest AFTER assembly — I rewrote its target_commit — refuses on the same rule, which is what makes the reference unforgeable rather than merely present. Substituting another run's manifest and citing that manifest's own true hash still refuses, because the manifest is then read back for its own RunID and position. On the read-back half: after an accepted run I recomputed the hashes myself, and run.json's
manifest_sha256equals the sha-256 of the manifest promoted beside it in the readings tree AND of the parked original, and the promoted bytes are byte-identical to the parked bytes — so the stored hash equals the content hash of the manifest the reference resolves to, through the package's own encoder, with no drift. evidence: internal/core/reading/ingest.go:505 — "func resolveParkedManifest(root *os.Root, out Output) (Manifest, error) — measured: a citation resolving to nothing refuses naming the path" evidence: internal/core/reading/ingest.go:525 — "if got := sha256Hex(raw); got != out.ManifestSHA256 { — measured: both a forged hash and a manifest tampered after assembly refuse here" evidence: internal/core/reading/ingest.go:533 — "if m.RunID != out.RunID { — measured: another run's manifest, cited by its own true hash, still refuses" evidence: internal/core/reading/ingest.go:586 — "writeJSONIn(root, runRel+"/"+ManifestFileName, m) — measured: sha256(promoted) == sha256(parked) == run.json manifest_sha256, promoted bytes identical to parked bytes" - ac-4 — MET: All three registrative reserved names were run through the real binary at the detection position and each refused with every element the criterion demands present in one message: the item's ordinal, the field, and the licence breached.
resolutionproduceditem 1 carries the reserved registrative field "resolution": a registrative reading names a tension and the constraint in play; proposing the resolution is not its licence;fixandremedytogether produced the same shape naming both. I also confirmed the granularity the criterion implies rather than states: with one offending item and one clean item in the same payload, the run LANDS one record and reportsitem 1 (reserved-name)— the reserved name refuses the item, not the run. The reserved-name check runs before the strict key-set check on purpose, so a licence breach gets an account of the licence rather than a bareunknown field. Recorded under diverged because it is real and it is not a criterion failure: the standing guard for this criterion is mutation-vacuous — emptyingReservedNames[RegimeRegistrative]leaves the wholeinternal/core/readingpackage green, because the test iterates the very table it is testing. evidence: internal/core/reading/ingest_regime.go:51 — "RegimeRegistrative: {"fix", "remedy", "resolution"}, — measured: each refuses, naming ordinal, field and licence" evidence: internal/core/reading/ingest_regime.go:287 — "if named := present(fields, ReservedNames[def.Regime]); len(named) > 0 { ... Rule: "reserved-name" — checked FIRST, before the unknown-key rule" evidence: internal/core/reading/ingest_regime.go:66 — "RegimeRegistrative: "a registrative reading names a tension and the constraint in play; proposing the resolution is not its licence" — the licence sentence the refusal quoted verbatim" - ac-5 — MET: A detection item whose
why_a_tensionreadsThe fix is to rewrite the charter.was refused by the real binary withitem 1 (RG-REG-FIXPROPOSAL): item 1 matches the registered signature RG-REG-FIXPROPOSAL— the item and the signature id both named, which is exactly what the criterion asks for. The gate is over the item BODY, and it is not evaded by the classes that used to defeat it: I confirmed at the CLI that the same phrase with a non-breaking space, an ideographic space, a soft hyphen insideremedy, a zero-width space insideresolution, and a fi-ligaturethe fix isall refuse. I proved the fold is load-bearing rather than decorative by two mutations: removingfoldForMatchingfrombodyText, and dropping the NFKC step alone, each turn TestTheRegimeGateIsNotEvadedByInvisibleRunes red. I also proved the enforce mode is guarded — degrading a signature toflagturns TestEverySignatureShipsEnforced red, so the reserved degradation path cannot be taken silently. The disclosed residue holds as written: a Cyrillic homoglyph inthe fiх isis accepted, which is the class itd-185 and spc-63 both name as open. Two things outside the criterion's letter but inside its subject are recorded under diverged: the registry refuses a great deal of legitimate quotation, and thepatternenvelope field is a signature-free channel through which the registry's own phrasing lands durably. evidence: internal/core/reading/ingest_regime.go:120 — "ID: "RG-REG-FIXPROPOSAL", Regime: RegimeRegistrative, Mode: SignatureEnforce, — measured: refuses, naming item and signature id" evidence: internal/core/reading/ingest_regime.go:345 — "if s.Pattern.MatchString(bodyText(fields, bodyFields)) { — measured: removing the fold from bodyText turns TestTheRegimeGateIsNotEvadedByInvisibleRunes red" evidence: internal/core/reading/ingest_regime.go:481 — "func foldForMatching(text string) string — measured: dropping norm.NFKC alone also turns that test red" evidence: internal/core/reading/ingest_regime_test.go:296 — "func TestEverySignatureShipsEnforced(t *testing.T) { if len(Signatures) < 4 { — measured: degrading RG-EXPL-DISPOSITION to flag turns it red; the count floor stops the table being emptied" - ac-6 — MET: All four names the criterion enumerates were run separately through the real binary at the comparative position, and each refused naming the field:
rank,score,order,recommended, every one producingitem 1 carries the reserved evaluative field "<name>": an evaluative reading characterises candidates against criteria; ordering, scoring or recommending among them is not its licence. The table in the code is exactly the criterion's four names and no more. As with ac-4, the behaviour is delivered and the standing guard for it is not: emptyingReservedNames[RegimeEvaluative]leaves the package green, so nothing would notice if a name left the table. Recorded under diverged rather than held against the criterion, because what ingest actually does is what ac-6 promises. evidence: internal/core/reading/ingest_regime.go:52 — "RegimeEvaluative: {"order", "rank", "recommended", "score"}, — measured: all four refuse, each naming its field" evidence: internal/core/reading/ingest_regime_test.go:127 — "for _, field := range ReservedNames[RegimeEvaluative] { — measured: emptying the table leaves the package green, so the loop body never runs" - ac-7 — MET: A comparative payload of three items in a deliberate arrangement — candidate ids C-best, C-mid, C-worst, characterised as cheapest, middling and dearest, in that order and carrying no reserved field — was ACCEPTED by the real binary: three records landed, zero refusals, exit 0. The criterion's stronger clause, that arrangement order is never inspected, is a property of the code rather than of one test, and I checked it directly: the item loop's index is used for one thing only, the ordinal that a refusal message quotes, and no comparison anywhere in the ingest path reads an item's position in the list. The reserved table deliberately names a FIELD rather than a property of the list, which is what makes the criterion structurally true rather than accidentally true. evidence: internal/core/reading/ingest_regime.go:188 — "for i, raw := range out.Items { ordinal := i + 1 — the index's only use; no ordering comparison exists anywhere in the path" evidence: internal/core/reading/ingest_regime_test.go:151 — "func TestEvaluativeDocumentOrderIsNeverRefused(t *testing.T) { — measured at the CLI too: three deliberately arranged items landed, RefusedItems empty"
- ac-8 — MET: The criterion has three arms and I ran all three at the entailment position.
dispositionrefuses as a reserved name with the explicative licence quoted;statusrefuses the same way; and an arbitrary field outside the schema — I usedverdict_of_the_reader— refuses asunknown-fieldnaming that field and listing the explicative body (claim_surfaced,claim_type,what_implies_it), which is the criterion'sany field outside the explicative body schemaarm. The intent's design claim that a dispositioned claim is impossible to EXPRESS rather than merely caught is realised: the item key set is closed against the position's own body fields, so there is no way to phrase a disposition-bearing field that is not refused by name. The same guard vacuity as ac-4 and ac-6 applies to the reserved half and is recorded under diverged; the unknown-field arm is guarded independently and is not vacuous. evidence: internal/core/reading/ingest_regime.go:54 — "RegimeExplicative: {"disposition", "status"}, — measured: both refuse as reserved-name with the licence stated" evidence: internal/core/reading/ingest_regime.go:311 — "if len(unknown) > 0 { ... Rule: "unknown-field" — measured: verdict_of_the_reader refused, naming the field and the explicative body" evidence: internal/core/reading/ingest_regime.go:179 — "allowed := map[string]bool{PatternField: true}; for _, f := range bodyFields { allowed[f] = true } — the closed key set per position" - ac-9 — MET: An entailment item whose
claim_surfacedreadsThis claim is already accepted by the maintainer.was refused by the real binary withitem 1 (RG-EXPL-DISPOSITION): item 1 matches the registered signature RG-EXPL-DISPOSITION— both the item and the signature id named. The signature ships in enforce mode and I proved that is guarded: changing its Mode to SignatureFlag turns TestExplicativeProseDispositionRefused AND TestEverySignatureShipsEnforced red, so a degradation cannot land unnoticed. The detector reads the folded claim body, and I confirmed at the CLI that the narrow no-break-space form of the same phrasing also refuses. The residue is disclosed and accurate: a disposition written in a confusable script is not caught. The false-refusal side of this signature is the largest in the registry and is recorded under diverged —\bdisposition\s*[:=]refuses any explicative claim that quotes the tokendisposition:from the document it read, and this repository's own records carry that token everywhere. evidence: internal/core/reading/ingest_regime.go:128 — "ID: "RG-EXPL-DISPOSITION", Regime: RegimeExplicative, Mode: SignatureEnforce, — measured: refuses naming item and signature id" evidence: internal/core/reading/ingest_regime.go:130 — "(?i)\\b(?:this\\s+claim\\s+is|the\\s+claim\\s+is|it\\s+is)\\s+(?:already\\s+)?(?:accepted|rejected|declined|settled|resolved)\\b|...|\\bdisposition\\s*[:=]" evidence: internal/core/reading/ingest_regime_test.go:244 — "func TestExplicativeProseDispositionRefused(t *testing.T) { — measured: degrading the signature to flag mode turns this and TestEverySignatureShipsEnforced red" - ac-10 — MET_WITH_CONCERNS: The mainline is realised and I verified it three ways at the CLI. A payload whose self-declared regime disagreed with the definition wrote
.abcd/development/readings/<run>/refusal.jsoncarrying the run id, position, regime, target_commit, manifest_sha256, the sanitised instrument and the reason WHOLE — uncut, which is the point of carrying it whole — with no reading records for that run and no surviving stage. A run in which every item was refused produced the same. The list is bounded in both dimensions the payload controls: a 100-item all-refused run produced a 4,095-byte reason endingand 80 more item(s) refusedand 25 terminal lines rather than 100. THE CONCERN is thata run refused at list levelis broader than the set of refusals that route throughrefuse(), and I demonstrated two classes that refuse the whole run and write no refusal record at all. First, and the ordinary one: aLoadDefinitionfailure is returned bare, AFTER the run's identity is proven. I edited a definition to state another position's regime and ingested a run whose manifest resolved cleanly — the run was refused, exit 2, and.abcd/development/readings/<run>/was never created. That contradicts the plugin page's own corrected sentence, which promises that from the identity point a list-level refusal writesrefusal.json. Second, the write-phase one: an item whoserecordBytesestimate passes at 1,044,215 but whose assembled record is 1,235,274 bytes — I built it from 520 KB of a repeated IPv4 the ledger redactor lengthens — refuses the run throughcapture.IngestReading, again with no refusal record, leaving a stage and an empty committed directory behind. evidence: internal/core/reading/ingest.go:656 — "func refuse(root *os.Root, res *IngestResult, out Output, m Manifest, def Definition, cause error) error { ... Reason: cause.Error() — measured: run metadata plus the whole named reason, no items, no reading records" evidence: internal/core/reading/ingest.go:402 — "def, err := LoadDefinition(repoRoot, pos); if err != nil { return err } — a bare return AFTER identity is proven; measured: a drifted definition refuses the run and creates no run directory at all" evidence: commands/reading.md:140 — "A refusal leaves a record once the run's identity is proven ... From that point a list-level refusal writesrefusal.json— contradicted by the LoadDefinition leg above" evidence: internal/core/reading/ingest.go:567 — "if err != nil { return fmt.Errorf("reading: writing the records of run %s: %w", out.RunID, err) } — the second class; measured reachable at 1,235,274 assembled bytes, and it wrote no refusal record" - ac-11 — MET: This is the criterion the host asked me to be hardest on, and it holds without exception across forty CLI probes: four regimes multiplied by ten forms of an empty-or-absent pattern — absent, empty string, ASCII spaces, U+200B zero-width space, U+00AD soft hyphen, U+FE0F variation selector, U+034F combining grapheme joiner, U+00A0 non-breaking space, U+2060 word joiner, and U+3164 Hangul filler. Every one of the forty refused the item with rule
named-provenance, naming the field. The zero-width hole the host flagged is genuinely closed at all four regimes, and I proved the closure is load-bearing by mutation: reverting the check tostrings.TrimSpace(fields[PatternField])turns TestEmptyPatternNamedRefusesItemAtEveryRegime red at all four positions at once. The same hole IS closed for the body fields — the identical folded-blankness rule runs over every declared body field, and U+200B, soft hyphens and U+00A0 in a body field each producemissing-body-field. The ordering matters and is right: the fold-blankness check runs BEFORE the hidden-rune encoder, so a pattern of one tab is judged blank rather than judged as the string%09. Two things recorded under diverged rather than against the criterion: U+2800 BRAILLE PATTERN BLANK is accepted as a pattern although it renders as nothing, which contradicts the residue's claim that every invisible rune is dropped; and the criterion names the fieldpattern_namedwhile the wire name ispattern. evidence: internal/core/reading/ingest_regime.go:299 — "if strings.TrimSpace(foldForMatching(fields[PatternField])) == "" { ... Rule: "named-provenance" — measured: 40 of 40 forms refuse across all four regimes" evidence: internal/core/reading/ingest_regime.go:329 — "if strings.TrimSpace(foldForMatching(fields[f])) == "" { missing = append(missing, f) — the body half; measured: ZWSP, soft hyphens and NBSP each produce missing-body-field" evidence: internal/core/reading/ingest_regime.go:201 — "// Encode the hidden runes on the way OUT, once the item has been judged ... a pattern of one tab encodes to "%09", which is no longer blank" evidence: internal/core/reading/ingest.go:98 — "const PatternField = "pattern" — the wire name; ac-11 sayspattern_named" - ac-12 — MET: A detection payload declaring
regime: generativewas refused at LIST level by the real binary — no item refusals, the whole run — withthe output declares the generative regime and agents/cold-reading-detection.md states registrative; the regime is the definition's property, resolved from the run's position. The refusal record landed carrying the run metadata and that reason, no reading records existed for the run, and no stage survived. The comparison is against the definition file resolved from the position, not against a table, and I proved the check is guarded: makingcheckRegimeunconditionally return nil turns TestSelfDeclaredRegimeMismatchRefusesRun red. On the adjacent case the criteria do not name and the host asked about: I editedagents/cold-reading-detection.mdto stateregime: evaluativeand ran the binary. Both the bare verb andreading ingestexit 2 witha definition stating another position's licence is refused rather than resolved— the definition is refused, not resolved into a changed licence, which is what commit 7fa4bbbc claims. The drifted definition is refused BEFORE the payload's regime is ever compared, so a lying definition cannot hand the ingest the wrong licence. Two residues of that adjacent case are recorded under diverged: it leaves no refusal record, and the issue it closes was left open in this checkout. evidence: internal/core/reading/ingest_regime.go:141 — "func checkRegime(out Output, def Definition) error { if out.Regime == def.Regime { return nil } — measured: making this unconditional turns TestSelfDeclaredRegimeMismatchRefusesRun red" evidence: internal/core/reading/ingest.go:408 — "if err := checkRegime(out, def); err != nil { return refuse(root, res, out, manifest, def, err) } — list level, and it writes the refusal record" evidence: internal/core/reading/definitions.go:95 — "states regime %q under position %s, which carries the %s regime ... refused rather than resolved — measured: the real binary exits 2 on bothabcd readingandabcd reading ingestwith a drifted definition" - ac-13 — MET: Both halves executed against the real binary. On acceptance, the committed reading record carries
id: "rdi-2608311447227460"— a family tag plus a sixteen-digit UTC-stamp-and-entropy id in adr-45's mint shape — and the run record'srecordsarray names the same id and its path; nothing in the payload supplied it, because the payload schema has nowhere to put one. On refusal, I tried two names the criterion's spirit covers:idanditem_id. Each was refused asunknown-field, naming the field and stating in the message itself thatthe item identity is the verb's to mint and the envelope is the verb's to compose, so neither has a field here— the criterion's exact requirement that a payload-supplied identifier is refused as an unknown field, not silently dropped and not honoured. The standing test pins three literal names (id,rdi,item_id) rather than iterating a table, so unlike ac-4/ac-6/ac-8 this guard is not vacuous. Builder deviation 3 verified: the mint is capture's, taken under the ledger lock where the collision probe can see the tree, and the test asserts the mint's SHAPE against^rdi-[0-9]{16}$and per-run uniqueness rather than injecting a clock the verb does not own. evidence: internal/core/reading/ingest.go:556 — "written, err := capture.IngestReading(capture.IngestReadingRequest{ — the mint is capture's, under the ledger lock; measured: rdi-2608311447227460 landed with no payload contribution" evidence: internal/core/reading/ingest_identity_test.go:149 — "for _, field := range []string{"id", "rdi", "item_id"} { — literal names, not a table; measured at the CLI foridanditem_id" evidence: internal/core/reading/ingest_identity_test.go:18 — "var mintedItemID = regexp.MustCompile(^rdi-[0-9]{16}$) — the shape check builder deviation 3 declares, in place of an injected clock"
Gap audit:
- honoured:
- WIRED, and this is the phase's keystone claim. capture.IngestReading now has exactly one non-test caller in the tree, and the verb reaches it and executes from BOTH front doors: I ran the real binary end to end and watched a reading record land in the committed ledger, and the plugin markdown surface carries a full ingest page invoking the same verb. Five criteria across three shipped intents sat at MET_WITH_CONCERNS on this exact absence, and the record writer now has its intended caller.
evidence: internal/core/reading/ingest.go:556 — "capture.IngestReading(capture.IngestReadingRequest{ — the ONLY non-test caller; a grep over internal/ and cmd/ finds no other"
evidence: internal/surface/cli/reading.go:174 — "res, err := reading.Ingest(reading.IngestRequest{ — measured:
abcd reading ingest --output-jsonwrote .abcd/work/issues/readings/rdg-.../rdi-....md" evidence: commands/reading.md:99 — ""${CLAUDE_PLUGIN_ROOT}/abcd" reading ingest \ --output-json ./reading-output.json --json — the second front door, with the body-field table, the mint rule and the refusal granularity" evidence: .abcd/work/issues/resolved/iss-2608310912206941-the-reading-item-mint-has-no-caller.md:13 — "resolution: "The cold-reading ingest verb is that caller ... reachable from abcd reading ingest on the CLI and from the plugin markdown surface."" - The host's headline question, answered empirically: NFKC is NOT producing false refusals of innocent prose. I A/B-ran the pre-NFKC binary (b54e8dc0) against the delivered one (8ec6f42f) over 34 constructed cases spanning every class the host named — ligatures in ordinary prose, superscripts and subscripts, vulgar fractions, Roman numerals, fullwidth CJK punctuation, non-breaking spaces in measurements and citations, soft hyphens at line breaks, combining accents in French/German/Polish, precomposed versus decomposed forms, mathematical alphanumerics, squared unit signs, the numero and trademark signs, halfwidth katakana, Kelvin/Angstrom/Ohm, and the registry's keywords inside longer words. NFKC changed the outcome in exactly 5 of the 34, and every one of the 5 is the registry's own phrase written in compatibility code points that render identically — fullwidth
Rated first, fullwidthwe recommend, the fi-ligaturethe fix is, mathematical bold𝐰𝐞 𝐫𝐞𝐜𝐨𝐦𝐦𝐞𝐧𝐝, and a fullwidth colon afterdisposition. Not one innocent case was newly refused. The reason is structural rather than lucky: all four signatures are anchored on multi-word verb phrases, and no ordinary compatibility form collapses into one. evidence: internal/core/reading/ingest_regime.go:493 — "return norm.NFKC.String(folded) — measured A/B against b54e8dc0: 5 of 34 outcomes changed, all 5 the registry's own phrasing in compatibility code points, 0 innocent prose newly refused" evidence: internal/core/reading/ingest_regime.go:107 — "Pattern: regexp.MustCompile((?i)\\b(?:ranks?|ranked|rates?|rated|scores?|scored)\\s+...— anchored on verb phrases, not bare words, which is why NFKC does not over-fire" evidence: internal/core/reading/ingest_regime_test.go:425 — "t.Run("innocent prose still lands", func(t *testing.T) { — the builder's own 12-case battery; my 34 cases reach the same conclusion over a wider surface" - The record-size cap is now sound, and I could not defeat it. The decision sits on the assembled bytes in capture.IngestReading, and the upstream recordBytes filter is honestly documented as an estimate rather than the decision. I attacked all three lengthening paths the comment names. Escaping: 900 KB of double quotes and 700 KB of backslashes were refused at item level. Hidden-rune encoding: 300 KB of zero-width spaces estimated at 5,404,245 bytes and refused. Redaction, the path the comment says exceeds the doubling: 520 KB of a repeated IPv4 PASSED the estimate filter at 1,044,215 and was then caught by the exact count at 1,235,274. So an item can slip the filter, and when it does the capture-level decision catches it — which is precisely the division of labour the code claims. evidence: internal/core/capture/reading.go:227 — "if n := len(content); n > issueschema.RecordReadLimit { — measured: caught a 1,235,274-byte record whose upstream estimate was 1,044,215" evidence: internal/core/reading/ingest_regime.go:429 — "func recordBytes(fields map[string]string, bodyFields []string) int { — "a cheap early FILTER, not the decision"; measured bypassable exactly as documented" evidence: internal/core/reading/ingest_regime.go:228 — "Rule: "record-too-large" — measured: refuses at ITEM level, landing the rest, on the escaping and encoding paths"
- All four declared builder deviations stand up under independent check. (1) Items are flat and
patternis still structurally distinguishable from a body field: it is in issueschema.ReadingRequired, the record ENVELOPE, and it appears in no position's body field set, so a payload cannot invert them — both sides are fixed key names. Ruling (18) is intact. (2) The stage holds a marker naming the run and the ids it reached, not rendered records, so the mint stays on the real ledger under the real lock. (3) ac-13 checks the mint's shape rather than an injected clock. (4) The residue text on BOTH itd-185 and spc-63 reads as OPEN, not as a closure, and I confirmed the class it names is genuinely uncaught: a Cyrillic homoglyph inthe fiх isis accepted by the delivered binary. evidence: internal/core/issueschema/reading.go:124 — "ReadingRequired = []string{"schema_version", "id", "run", "manifest", "position", "regime", "pattern"} — pattern is envelope; no position's Fields list contains it" evidence: internal/core/reading/ingest.go:210 — "type stageMarker struct { Type string; RunID string; Records []string } — a marker, not rendered records" evidence: .abcd/development/specs/open/spc-63-one-ingest-verb-validates-every-cold.md:179 — "What stays open, stated plainly because this section previously read as a closure: a script-CONFUSABLE substitution ... it is open, which is why it is disclosed rather than filed under the calibration residue" evidence: .abcd/development/intents/shipped/itd-185-one-ingest-verb-validates-every-cold.md:169 — "Closing that needs a confusables table, which is a new dependency and a maintainer's decision; until it is taken, this class is open. — measured: the Cyrillic homoglyph case is accepted, as disclosed" - Containment and trust-boundary handling are real, not asserted. Every path the verb reads, writes or DELETES inside the repository is resolved through an os.Root, the payload is read behind a single guarded open with a 4 MiB cap, the run id is the only payload value any path is built from and it is matched against the shared grammar before it becomes one, and a rerun of a run that already has an outcome — commit marker OR refusal record — is refused. I confirmed the rerun refusal at the CLI in both directions: after a successful run and after a refused one. evidence: internal/core/reading/ingest.go:315 — "root, err := os.OpenRoot(repoRoot) — every read, write and delete resolved through it; the sweep runs before the payload is even read" evidence: internal/core/reading/ingest.go:632 — "func refuseARerun(root *os.Root, runID string) error { for _, name := range []string{RunFileName, RefusalFileName} — measured at the CLI after both a committed and a refused run" evidence: internal/core/recordid/valid.go:36 — "func ValidReadingRunID(id string) bool { return readingRunIDRe.MatchString(id) } — one rule, refused before either side touches a path; measured: run_id "../../etc" refused"
- The decision log records what the build settled, and each line is true of the code that shipped. All six 2026-08-31 entries check out against the tree at 1d84ce26: the flat payload item derived from issueschema.ReadingBodyFields, the stage as a marker with a filename-grammar-bounded rollback, the position/regime disagreement refused in LoadDefinition rather than cross-checked in the ingest verb, the size decision moved onto the assembled bytes in capture, and the fold with its confusable disclosure.
evidence: dcn-2608310000000004 — "itd-185's payload item is FLAT (
patternplus the position's own body fields, read fromissueschema.ReadingBodyFields) — true of internal/core/reading/ingest_regime.go:178-182" evidence: dcn-2608310000000006 — "The position/regime disagreement is refused inreading.LoadDefinition, not cross-checked in the ingest verb — true; no cross-check exists in ingest.go" evidence: dcn-2608310000000007 — "record-size limit is DECIDED in capture.IngestReading on the assembled bytes — true of internal/core/capture/reading.go:227, and measured" - Scope Conditions: itd-185 states 'None stated', and none was invented, dispositioned or quietly added in the delivery. evidence: .abcd/development/intents/shipped/itd-185-one-ingest-verb-validates-every-cold.md:105 — "## Scope Conditions None stated."
- WIRED, and this is the phase's keystone claim. capture.IngestReading now has exactly one non-test caller in the tree, and the verb reaches it and executes from BOTH front doors: I ran the real binary end to end and watched a reading record land in the committed ledger, and the plugin markdown surface carries a full ingest page invoking the same verb. Five criteria across three shipped intents sat at MET_WITH_CONCERNS on this exact absence, and the record writer now has its intended caller.
evidence: internal/core/reading/ingest.go:556 — "capture.IngestReading(capture.IngestReadingRequest{ — the ONLY non-test caller; a grep over internal/ and cmd/ finds no other"
evidence: internal/surface/cli/reading.go:174 — "res, err := reading.Ingest(reading.IngestRequest{ — measured:
- diverged:
- NOT ON THE DECLARED LIST, AND THE MOST CONSEQUENTIAL THING I FOUND — the orphan sweep deletes committed reading records and, on a reachable path, tells the operator nothing. The sweep is the first thing under the lock, before the payload is read, and it populates
res.RolledBack. But the front door renders the result on the error path only when a refusal record was written, so an invocation whose payload fails before the run's identity is proven throws the whole result away. I demonstrated it: with an orphaned stage and its recordrdi-2608310000008001.mdin the committed ledger, an ingest whose payload fails at_typeDELETED that record and printed only the_typeerror; under--jsonthe render is{"error": ...}withrolled_back_recordsandcleared_stagesboth absent. The code's own comment names this as the thing it exists to prevent —"cleared an orphaned stage" does not tell an operator that records left the ledger with it— and the path that reaches it is ordinary: a crash, then a retry carrying a typo. This is also why ac-2 is MET_WITH_CONCERNS rather than MET. evidence: internal/core/reading/ingest.go:243 — "// RolledBack names the reading records the sweep REMOVED from the committed ledger, because their run never reached its commit marker. A delete in the committed tier is reported by id" evidence: internal/core/reading/ingest.go:366 — "cleared, rolledBack, err := sweepOrphanStages(root); res.ClearedStages = cleared; res.RolledBack = rolledBack — populated before the payload is read" evidence: internal/surface/cli/reading.go:185 — "if res.RefusalPath != "" { — measured: a _type failure deleted a committed record silently, in both text and --json renders" - NOT ON THE DECLARED LIST — the
patternenvelope field is a signature-free channel, and the registry's own phrasing lands durably through it.bodyTextdeliberately excludes the pattern, so no signature ever reads it. I ran three cases through the real binary and all three exited 0 with a committed record: a registrative item whose pattern readsP-1. The fix is to rewrite the charter, and to fix this you must delete section 4.; an explicative item whose pattern readsP-2. This claim is already accepted by the maintainer.; and an evaluative item whose pattern readsP-3. We recommend the second candidate.— the last of which is now in the ledger aspattern: "P-3. We recommend the second candidate."under the evaluative regime. By itd-185's OWN stated test this is a defect and not a residue: the residue covers phrasing OUTSIDE the registry, and this is the registry's exact phrasing, unmodified, merely placed in a field the detector does not read. It is wider than the confusables class the residue does disclose, and neither the intent, the spec nor the plugin page names it. ac-5 and ac-9 both saybody, so the criteria themselves survive — the intent's honesty about its own residue does not. evidence: internal/core/reading/ingest_regime.go:443 — "func bodyText(fields map[string]string, bodyFields []string) string { — "The pattern is not among them — it names the reading's own basis, not a finding"" evidence: internal/core/reading/ingest_regime.go:345 — "if s.Pattern.MatchString(bodyText(fields, bodyFields)) { — the only enforcing call site; the pattern field never reaches it" evidence: .abcd/development/intents/shipped/itd-185-one-ingest-verb-validates-every-cold.md:173 — "the registry's phrasing with a byte substituted is a defect in the gate — measured: the registry's phrasing with NO byte substituted, in the pattern field, lands with exit 0" - NOT ON THE DECLARED LIST — the three reserved-name tables are mutation-vacuous, so the standing guards for ac-4, ac-6 and ac-8's structural halves assert nothing. Each test iterates
ReservedNames[<regime>], the very table under test, with no floor on the count. I emptied each table in turn and the wholeinternal/core/readingpackage stayed green all three times: the loop body simply never runs. No test anywhere in the tree pins the literal names, and the criteria name them literally — ac-4 namesresolution,fix,remedy; ac-6 namesrank,score,order,recommended; ac-8 namesdisposition,status. A name dropped from a table would silently un-enforce a named criterion, and the item would then fall through to the unknown-field rule, which still refuses but cites the wrong rule and never states the licence ac-4 requires. This is exactly the class commit e55c03fb set out to close: that mutation sweep neutralised code BRANCHES and bound the three that survived, but never emptied a data table, so this shape was invisible to it. The neighbouring signature registry does have the floor — TestEverySignatureShipsEnforced refuses a registry of fewer than four — which shows the pattern was known and simply not applied here. evidence: internal/core/reading/ingest_regime_test.go:172 — "for _, field := range ReservedNames[RegimeRegistrative] { — measured: emptying the table leaves the package green" evidence: internal/core/reading/ingest_regime_test.go:127 — "for _, field := range ReservedNames[RegimeEvaluative] { — measured green when emptied" evidence: internal/core/reading/ingest_regime_test.go:224 — "for _, field := range ReservedNames[RegimeExplicative] { — measured green when emptied" evidence: internal/core/reading/ingest_regime_test.go:297 — "if len(Signatures) < 4 { t.Fatalf("the registry holds %d signature(s); spc-63 names four" — the floor the reserved tables lack" - NOT ON THE DECLARED LIST — the signature registry refuses a great deal of prose a real cold reading would legitimately write, and the intent's Disclosed residue paragraph is one-directional about it. I ran 34 realistic reading outputs across all four positions and 14 were refused. The pattern in the failures is one thing: a reading QUOTING the document it read. An explicative reading exists to surface the claims a document makes, and
\bdisposition\s*[:=]refuses any claim that quotes the tokendisposition:— a token this repository's own records carry everywhere — while\balready\s+(?:accepted|rejected|settled|resolved)\brefusesSection 4 asserts the licensing question is already settled by adr-43andthe minutes use the formula, it is resolved that the committee meets monthly. A comparative reading is refused forClause 6 says the MIT licence should be adopted, forThe cited paper closes with the sentence, we recommend further study, and forThe suite scores below the threshold the charter names. A detection reading is refused forSection 3 says the fix is already merged while section 8 says it is pending— the canonical shape of a detection. None of this is NFKC's doing; it is present in the pre-NFKC binary too. The intent DOES recordwhether the signatures lint cleanly in practiceas its open question and reserves the degradation path for it, so the risk is disclosed at the Ruled and Out-of-Scope tiers. But the paragraph added under the criteria split and titledDisclosed residue (ac-5 and ac-9)speaks only of UNDER-catching and never of over-catching, which on this evidence is the larger and likelier failure. The builder's own innocent-prose battery is 12 cases at ONE position (detection/why_a_tension), so it exercises one of the four signatures. evidence: internal/core/reading/ingest_regime.go:133 — "|\bdisposition\s*[:=]— measured: refusesThe schema lists disposition: accepted as an example rowand the Frenchla disposition : elle figure en annexe" evidence: internal/core/reading/ingest_regime.go:110 — "|\\b(?:the\\s+)?(?:strongest|weakest|best|worst)\\s+(?:candidate|option|choice)\\b— measured: refuses a characterisation that quotes the read document's own superlative" evidence: .abcd/development/intents/shipped/itd-185-one-ingest-verb-validates-every-cold.md:158 — "The first is the one this intent has always disclosed: a fix proposal or a disposition phrased outside the registry's signatures is not caught. — the residue names only the under-catching direction" evidence: internal/core/reading/ingest_regime_test.go:438 — "{"a ligature in ordinary prose", "the record de\ufb01nes the constraint the tree ignores"} — the 12-case battery, all at the detection position, exercising one signature of four" - NOT ON THE DECLARED LIST —
docs/reference/cli/commands.mdis wrong on three counts, and because the page is generated verbatim from the cobraLongstring and pinned by a test, the defect is in the shipped help text an operator reads at the terminal. It saysNothing durable is written until the whole payload validates, which is false: a list-level refusal after the run's identity is proven writesrefusal.jsoninto the committed tier precisely BECAUSE the payload did not validate. It says an orphaned stage isnamed and cleared by the next invocationand never mentions that the sweep also deletes committed reading records — reproducing the exact wording ingest.go:243 warns against, and never namingrolled_back_records. And it sayseach regime's reserved names are refusedand that the signature registrycatchesoffending prose, both of which are false at the generative regime, which has no reserved-name row and where every signature hit raises a review flag and lands the item. The plugin page states all three correctly; the reference and the terminal help do not. evidence: docs/reference/cli/commands.md:831 — "Nothing durable is written until the whole payload validates — contradicted by internal/core/reading/ingest.go:669, which writes refusal.json under ReadingsRecordDir" evidence: docs/reference/cli/commands.md:833 — "an orphaned stage is named and cleared by the next invocation — omits the committed-tier delete at ingest.go:757" evidence: internal/core/reading/ingest_regime.go:48 — "//generativehas no row and needs none — so "each regime's reserved names" is false for one of the four" evidence: internal/surface/cli/reading.go:143 — "Long: "Validate the JSON a cold reading returned..." — the single source the reference page and the terminal help are both generated from" - NOT ON THE DECLARED LIST — the brief carries the same three overclaims, and one of them is the very sentence an issue in this range corrected elsewhere.
.abcd/development/brief/04-surfaces/23-reading.mdstates unconditionally thata refused run still leaves a durable record; iss-2608311234541392 corrected exactly that sentence incommands/reading.mdby qualifying it withonce the run's identity is proven, and the correction did not propagate to the brief. The brief also saysEach regime declares reserved names, false at generative, and describes a two-layer gate at generative where — between the empty reserved table and the review-flag path — neither layer refuses anything. evidence: .abcd/development/brief/04-surfaces/23-reading.md:104 — "A list-level violation refuses the run, and a refused run still leaves a durable record — measured false on the LoadDefinition leg and on the write-phase leg" evidence: .abcd/work/issues/resolved/iss-2608311234541392-commands-reading-md-tells-a-host-that.md:13 — "resolution: the page states that a refusal records only once the run's identity is proven — corrected in one surface of three" evidence: .abcd/development/brief/04-surfaces/23-reading.md:108 — "Each regime declares reserved names — false at generative (ingest_regime.go:51)" - NOT ON THE DECLARED LIST — iss-2608311145258479 is fixed by this range and left OPEN, while shipped source cites it by id as the failure it closes. Commit 7fa4bbbc implements the position/regime refusal and
internal/core/reading/definitions.go:130names the issue asexactly the failure shape the gate exists to close (iss-2608311145258479), but the record is still in.abcd/work/issues/open/at 1d84ce26 and the commit carries noResolves:trailer. The commit message declares the reason honestly — the record lives in another checkout — so this is a disclosed constraint rather than an oversight, but the repository's own definition of done requires a fixing change to resolve its issue in the same diff, and a shipped comment citing an id that reads as open is the marker loss that rule exists to prevent. The sibling iss-2608311145286014 (the doubled error prefix) is likewise still open, and this range fixed half of it. evidence: internal/core/reading/definitions.go:130 — "which is exactly the failure shape the gate exists to close (iss-2608311145258479)" evidence: .abcd/work/issues/open/iss-2608311145258479-the-definition-locator-checks-that-a.md:1 — "still in open/ at 1d84ce26; commit 7fa4bbbc says "whose record lives in another checkout and is not resolved here" and carries no Resolves: trailer" - NOT ON THE DECLARED LIST — a resolved issue record in this range asserts a property the very next commit repudiates. iss-2608311306536908's resolution says recordBytes counts every value double,
an exact bound rather than a guess; commit 8ec6f42f then rewrote that function's doc to say itis a cheap early FILTER, not the decision ... the ledger redactor exceeds that, and moved the decision to capture. The record was superseded by iss-2608311351239562 and never amended, so the ledger now carries a resolved record telling a reader the estimate is exact when I measured it slipping by 191,059 bytes. evidence: .abcd/work/issues/resolved/iss-2608311306536908-recordbytes-under-estimates-the-reading.md:11 — "resolution: "recordBytes counts every value double, an exact bound rather than a guess"" evidence: internal/core/reading/ingest_regime.go:420 — "two attempts to make it one failed the same way ... the ledger redactor exceeds that — measured: an item estimated at 1,044,215 assembled to 1,235,274" - NOT ON THE DECLARED LIST — U+2800 BRAILLE PATTERN BLANK is accepted as a
pattern, so the residue's claim thatevery invisible rune is droppedis an overclaim. U+2800 renders as nothing in every common font, but it is a graphic character: not Cf, not Other_Default_Ignorable_Code_Point, not a Variation_Selector, and unicode.IsSpace is false for it, so the fold leaves it standing and TrimSpace does not remove it. A record then asserts a provenance that renders as blank — the same failure mode the U+200B fix closed, one category further out. This does not breach ac-11, whose words areempty or absentand U+2800 is neither; it breaches the residue paragraph's own sentence about the fold. Every other blank-rendering form I could construct is caught, including the BOM and both Hangul fillers. evidence: internal/core/reading/ingest_regime.go:482 — "case unicode.Is(unicode.Cf, r), unicode.Is(unicode.Other_Default_Ignorable_Code_Point, r), unicode.Is(unicode.Variation_Selector, r): return -1 — U+2800 is in none of the three" evidence: .abcd/development/intents/shipped/itd-185-one-ingest-verb-validates-every-cold.md:166 — "every Unicode space folds to ASCII, every invisible rune is dropped, and NFKC folds the compatibility forms — measured: a pattern of one U+2800 is ACCEPTED, exit 0" - NOT ON THE DECLARED LIST — the durable refusal record renders the elision entry as
item 0, which the code's own comment says must never happen.boundedRefusalsdeliberately gives the elision entry no ordinalbecause it is not an item: rendering it as "item 0" would name a thing that does not exist, and the terminal renderer honours that with an explicit branch. ButrenderRefusals, which composes the reason string written into refusal.json, formats every entry withitem %dunconditionally. I ran a 100-item all-refused payload and the committed record's reason endsitem 0 (refusals-elided): and 80 more item(s) refused. The fix was applied to one of the two surfaces, and it is the durable one that missed it. evidence: internal/core/reading/ingest_regime.go:267 — "// The elision entry carries NO ordinal, because it is not an item: rendering it as "item 0" would name a thing that does not exist." evidence: internal/core/reading/ingest_regime.go:546 — "parts = append(parts, fmt.Sprintf("item %d (%s): %s", r.Ordinal, r.Rule, r.Detail)) — no branch; measured in refusal.json: "item 0 (refusals-elided): and 80 more item(s) refused"" evidence: internal/surface/cli/reading.go:292 — "if r.Ordinal == 0 { fmt.Fprintf(w, " (%s) %s\n", r.Rule, r.Detail); continue } — the branch the terminal has and the record writer lacks" - ac-1 and ac-10 are in literal tension, and the delivery resolves it on a boundary the criteria never state. ac-1 requires that a malformed output leave
no durable record for that run ... in the readings tree; ac-10 requires that a list-level refusal leave a refusal record, and that record lives in the readings tree. The build draws the line atthe run's identity is proven— a refusal before the parked manifest resolves writes nothing, one after it writes refusal.json. That is coherent and documented on the plugin page, but it is not in the criteria, and it is observable: an item carrying a non-string value is malformed by any ordinary reading of the word, and it produces.abcd/development/readings/<run>/refusal.json. I judged ac-1 MET because every whole-output malformation it plausibly names writes nothing anywhere, and reading ac-1 to forbid the refusal record would make ac-10 unsatisfiable. evidence: internal/core/reading/ingest.go:394 — "// The run's identity is proven from here ... A refusal below is therefore recordable against that run — the boundary, stated in the code and not in the criteria" evidence: commands/reading.md:141 — "A refusal leaves a record once the run's identity is proven ... A refusal reached BEFORE that point ... writes nothing anywhere" evidence: internal/core/reading/ingest_regime.go:192 — "Rule: "item-shape" — measured:\"pattern\": 123produced a refusal.json in the readings tree" - The manifest's
target_commitreaches both durable records un-echoed, against the principle iss-2608311211235195 established in this same range. That record widenedechoto cover the parked manifest's run id, position and assembler version, on the ground that a manifest is read back at the operator's word and is no more trusted for being a file on disk.m.TargetCommitis the one manifest-derived value that still goes into the run record and the refusal record raw. Low severity — a manifest this verb accepted has already had its content hash proven against the payload's citation — but the exemption is undeclared. evidence: internal/core/reading/ingest.go:591 — "Position: def.Position, Regime: def.Regime, TargetCommit: m.TargetCommit, — the run record" evidence: internal/core/reading/ingest.go:659 — "Position: def.Position, Regime: def.Regime, TargetCommit: m.TargetCommit, — the refusal record" evidence: internal/core/reading/ingest.go:530 — "// The manifest is a file on disk rather than the payload, but it is no more trusted for that ... its values reach the same terminal, so they are echoed under the same rule" - The bare
abcd readingverb still prints a doubled prefix —abcd: reading: reading: agents/cold-reading-detection.md states regime ...— whileabcd reading ingestprints it once. Commit 53b00afa fixed the stutter for the ingest verb only, and the code says why in as many words: the ingest message is load-bearing because six of thirteen criteria require a field, an ordinal or a signature id to be NAMED, and the assemble path was left alone as a change to a shipped verb rather than to this delivery. That is a defensible line; its residue is that the two verbs of one sub-tree now render refusals differently, and it rides on iss-2608311145286014, which is still open and whose title covers both paths. evidence: internal/surface/cli/reading.go:267 — "func trimCorePrefix(msg string) string { return strings.TrimPrefix(msg, "reading: ") } — applied at line 190 for ingest and not at line 49 or 115" evidence: internal/surface/cli/reading.go:264 — "Theassemblepath above carries the same stutter and is deliberately left alone — measured: the bare verb prints "abcd: reading: reading: ...""
- NOT ON THE DECLARED LIST, AND THE MOST CONSEQUENTIAL THING I FOUND — the orphan sweep deletes committed reading records and, on a reachable path, tells the operator nothing. The sweep is the first thing under the lock, before the payload is read, and it populates
- missing:
- Nothing has run a real reading through this verb. Every payload the delivery validates against — the fixtures, the builder's battery, and my own forty-plus probes — is synthetic. The intent's own recorded open question is whether the signatures lint cleanly in practice, and the delivery does not answer it, cannot answer it without a dispatched reading, and reserves the degradation path precisely because of it. My 34-case realistic-prose battery is the closest thing to an answer that exists, and it says the answer is probably no: 14 refusals, dominated by a reading quoting the document it read. The gate is therefore shipped enforced over a calibration that has never been taken, which is the standing tension the intent itself records against the widen-options
calibrated before it gatesclause. evidence: .abcd/development/intents/shipped/itd-185-one-ingest-verb-validates-every-cold.md:93 — "Standing tension with the repo's widen-options promotion clause — "calibrated before it gates" — recorded as a standing tension; the ruled design governs the instrument meanwhile." evidence: .abcd/development/intents/shipped/itd-185-one-ingest-verb-validates-every-cold.md:102 — "Whether the regime signatures lint cleanly — untested; the degradation path exists precisely because of it. — still untested at 1d84ce26" - A run refused inside the write phase leaves an empty directory standing in the COMMITTED tier. rollbackRun removes the ledger run directory only from inside the closure it runs when there were entries to remove, so a run that reached capture.IngestReading, had its directory created, and was then refused by the size invariant leaves
.abcd/work/issues/readings/<run>/empty and permanent — the next invocation's sweep clears the stage and walks past it. Harmless in git, which does not track empty directories, but it is a durable artefact of a run the design says never happened, and it is the visible half of the same gap as the missing refusal record in ac-10. evidence: internal/core/reading/ingest.go:764 — "_ = root.Remove(ledgerRel) — inside unlink, which is only called when len(entries) > 0" evidence: internal/core/reading/ingest.go:767 — "if len(entries) > 0 { if err := underLedgerLock(root.Name(), unlink); err != nil { — measured: after the size-refused run and a subsequent sweep, the empty committed directory survives"
- Nothing has run a real reading through this verb. Every payload the delivery validates against — the fixtures, the builder's battery, and my own forty-plus probes — is synthetic. The intent's own recorded open question is whether the signatures lint cleanly in practice, and the delivery does not answer it, cannot answer it without a dispatched reading, and reserves the degradation path precisely because of it. My 34-case realistic-prose battery is the closest thing to an answer that exists, and it says the answer is probably no: 14 refusals, dominated by a reading quoting the document it read. The gate is therefore shipped enforced over a calibration that has never been taken, which is the standing tension the intent itself records against the widen-options
Frontmatter
| id | itd-185 |
|---|---|
| type | intent |
| lifecycle | shipped |
| kind | bundle-member |
| severity | major |
.abcd/development/intents/shipped/itd-185-one-ingest-verb-validates-every-cold.md
open on GitHub ↗ · commit history ↗
Referenced by7
- iss-2610262036243501itd-185 has reading ingest check prose that ranks, settles or proposes; the reading chapter says that prose-signature layer was removed and only fields are refusedrelated
- iss-2610262036257320itd-185 says every refused run writes a refusal record; the reading chapter says refusals made before the run's identity is proven, a hash disagreement among them, leave nonerelated
- itd-2609020625400169An intent that a reading occasioned says so in its origin, with the run and the item that occasioned itbuilt on by
- itd-2609020625400194An admission and a surprise are written by a verb, and the order the design fixes is a refusalbuilt on by
- itd-2609020625402599The scribe's context is assembled and its output is ingested by a verb, and the record can show that no session held both a reading and the ledgerbuilt on by
- itd-2609020625405251A scope condition is dispositioned from a reading run, keyed to the condition's identity and joined to the item that occasioned itbuilt on by
- spc-63The cold-reading output contract: a strict per-position schema, a supply-regime gate, and a staged ingest that leaves evidence or nothingimplemented by
mentions30
- adr-43Contributions are inbound = outbound MIT, and the trust boundary is the organisation's role ladder
- adr-45Record ids are timestamp-numeric, capture-stable, and never allocated by looking at the maximum
- dcn-26083100000000022026-08-31 — Phase 0 of the last four intents executed: the ruled criteria
- dcn-26083100000000032026-08-31 — The wave plan is re-cut, because itd-184 and itd-185 are not
- dcn-26083100000000042026-08-31 — itd-185's payload item is FLAT (`pattern` plus the position's own
- dcn-26083100000000062026-08-31 — The position/regime disagreement is refused in
- dcn-26083100000000072026-08-31 — itd-185's record-size limit is DECIDED in capture.IngestReading on
- dcn-26083100000000082026-08-31 — itd-185's ingest containment is layered and the layers are
- dcn-26083100000000092026-08-31 — itd-185 folds invisible and compatibility-equivalent runes
- dcn-26083100000000142026-08-31 — Degrade itd-185's four semantic supply-regime signatures
- iss-2608300108376943A bundle of intents cannot share one spec through the ceremony: a spec's intent back-link is a single itd-N, intent link refuses a spec that names a different intent, and spec close refuses a disagreeing link, so the bundle-member kind (itd-114 vocabulary) has no shared-spec path. Ruling (13) of the cold-reading workstream binds the instrument trio (itd-183, itd-184, itd-185) as one bundle with a shared spec; the planning session stamps bundle-member and plans three specs written as one cross-linked design instead, logged as an improvisation. Either the spec back-link becomes a list, or the bundle kind's contract states that members carry sibling specs.
- iss-2608301726130926two shipped acceptance criteria hold a MET_WITH_CONCERNS that becomes NOT_MET if itd-185 does not land in this phase
- iss-2608310912206941the reading item mint has no caller outside its own package so the admission and surprise schemas gate a corpus nothing can populate
- iss-2608310954048572spc-62 states that internal/core/reading already locates the four cold-reading definitions and hashes them for spc-63 instrument identity, and it does not: no non-test file in that package references agents/ at all. A builder reading the spec would take the locator as existing and find nothing to call. The locator is unbuilt work that spc-62 must deliver, and the sentence is a prose claim about code behaviour of exactly the shape itd-195 refuses.
- iss-2608311145258479The definition locator checks that a regime is a KNOWN value but never that it AGREES with the position it sits under. knownRegime tests membership in the four, so a definition carrying regime evaluative under position widening loads clean: the binary prints all four definitions and exits 0 while the byte-identity and pinning tests go red. The comment at the top of definitions.go argues that answering from a table rather than the file is what would make a drift undetectable, but the file is not checked against the position either, so the drift is undetectable at RUNTIME and caught only in CI. This matters downstream rather than here: spc-63's regime gate resolves a run's position to its definition and reads the regime from LoadDefinition as the source of truth, so a drifted file hands the ingest verb the wrong licence without any refusal, and the gate that exists to catch a reading exceeding its licence would be enforcing the wrong one.
- iss-2608311145286014The reading surface doubles its own error prefix: the CLI layer prepends 'reading: ' and the locator's errors already carry it, so an operator sees 'abcd: reading: reading: agents/...' on a refusal. The path this appears on is the definition-refusal path spc-62 introduced as its own proof of wiring, so the first thing the new surface shows a human on failure is a stutter.
- iss-2608311211235195The ingest verb's echo() sanitiser and its length cap are mutation-vacuous: neutralising termsafe.Sanitize, or the maxEchoedRunes truncation, leaves every test green. Both are trust-boundary guards on a path that echoes model-produced payload text to a terminal and into a durable refusal record. Three interpolations also reach a message unsanitised: ParsePosition quotes the payload's raw position token, and the parked manifest's run id, position and assembler version are interpolated verbatim.
- iss-2608311234541392commands/reading.md tells a host that every list-level refusal leaves a refusal record, but four of the six list-level refusals it enumerates (wrong _type, unresolvable run, manifest hash mismatch, drifted definition) deliberately write nothing, because the run's identity is not yet proven. The same page instructs the host to report refusal_record from the JSON, which the front door can never emit: a refusal returns an exitError and the render is never reached, so IngestResult.RefusalPath is dead surface through the only front door.
- iss-2608311306535485The supply-regime gate is evaded by a single non-breaking space: RE2's whitespace and word-boundary classes are ASCII-only and termsafe.Sanitize does not mask U+00A0, so a registered signature's own phrasing with U+00A0 between two words produces no match and the item lands with no refusal and no flag. This is NOT the residue itd-185 discloses, which covers a fix proposal or disposition phrased OUTSIDE the registry; this is the registry's own phrasing with one invisible byte substituted, so it is an evasion of the gate rather than a limit of it.
- iss-2608311306536908recordBytes under-estimates the reading record by up to a factor of two: it is measured before the record writer escapes backslashes and double quotes, so an item body of quote characters lands a record roughly twice the measured size and past issueschema.RecordReadLimit. The envelope allowance is also measured before the ledger redactor runs, which can lengthen text when a placeholder is longer than the secret it replaces.
- iss-2608311351239562The reading record size cap is still evadable, now through the ledger redactor rather than the escaper. The estimate's 2x factor is exact for the YAML escaper and was verified so, but the redactor is the other lengthening step and it expands past 2x: a network address placeholder is 18 bytes for a 7-byte span, so with a separator the growth is 2.375x and it is proportional to body length rather than bounded by the envelope allowance. A body of 65000 repetitions of a short address, well under the payload cap, is accepted by the size check and lands a 1235410-byte record against a 1048576-byte limit, and that record is permanently unanswerable: the disposition verb refuses it as past the cap. This is the third time this defect has been fixed and the second time a fix modelled one lengthening step and missed the next, which is the signature of an estimate standing in for a measurement. The exact byte count exists in one place, in the ledger ingest after the record text is assembled from already-redacted values and before it is written, and refusing there ends the class rather than moving the threshold again.
- iss-2608311518056854The supply-regime registry refuses a reading for quoting the document it read, and the rate is high: fourteen of thirty-four realistic reading outputs were refused, all by one failure mode. The disposition detector matches the bare token followed by a colon or equals, and this repository's records carry that token everywhere, so any explicative claim that quotes a record refuses. So do a claim reporting that a clause settles a licensing question, a claim quoting a paper that closes by recommending further study, a claim reporting that a suite scores below a threshold, and a detection reading that one section says a fix is merged while another says it is pending, which is the canonical shape of a detection finding. This is not the NFKC normalisation, which was measured innocent: an A/B over thirty-four cases flipped five outcomes and every one was the registry's own phrase in compatibility code points, with no innocent case newly refused. The noise predates it and is structural, because the detectors cannot distinguish a reading that PROPOSES from a reading that REPORTS someone else proposing, and the second is most of what a reading legitimately does. The intent records signature noise as its open question, but the disclosed residue names only under-catching; on this evidence over-catching is the larger risk and the residue should say so.
- iss-2609011207114761The changelog composer cannot see the previous release surface, so its `Changed`
- iss-2609012259571458The supply-regime check has a flag mode in code but no seam that can select it, so the design's degradation path cannot be taken
- itd-86abcd Can Read Its Own Design Documents As A Stranger Would
- itd-189What the widening reading proposes is admitted or declined on the record — grounds on admission, dispositions on declines, and surprises as their own entries
- spc-61The cold-reading input assembler: positive inclusion at field granularity, a pathless bundle, and a hashed per-run manifest
- spc-62Four cold-reading definitions: one verbatim blindness core, four objects, four licences no definition may borrow
- spc-64The read-block eval falsifies the firewall
- spc-2609020626042168The reading-occasioned origin: promote stamps the run and the item it graduated, and the stamp primitive refuses the kind without them