5d06b745994cc73ad587553ad324e44d4194be2d
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0fe19a8440 |
fix(ledger): a canon may hold a class and the to_dict beside it (#4220)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m1s
CI & Build / Python tests (push) Successful in 1m41s
CI & Build / Build & push image (push) Successful in 28s
The review surface shipped yesterday reported two canons on its first live day and both were sound. Coherence was "do all judged rows share a form", which #2844 failed at 37/62 = 0.597 for containing a model class and the to_dict the canon's own text says the class must carry, and #2849 failed at 4/7 for pairing sync loop-starters with the async ticks they schedule. A review surface whose whole output is noise is one that stops being read. The obvious repair is a trap, and there is now a test standing in front of it. Grouping by family and keeping a majority test makes the check BLIND: before #2844 was cleaned by hand it held 37 classes and 56 callables, which as families is 56/93 = 0.602 — a clean pass, and the 31 rows that had no business being there (Vue functions, route handlers, a dozen tests) would never have been reported at all. A looser bar in the same shape is worse than the bug. So the verdict is inverted. Instead of asking whether most rows agree, it asks how many rows the canon CANNOT ACCOUNT FOR: a row in the majority family is accounted for; a callable defined in a file that also holds a majority-family `type` row is a method of a member, not a foreign body; and strangers above a fifth of the readable rows make the canon incoherent. The majority vote abstains those methods, so a class's own serialisers cannot outvote the classes and turn the members into the strangers. Measured on the real ledger before it was written, which is why it is this rule and not a nudge to the share: clean #2844 has 0 strangers in 62, #2849 has 0 in 7, and polluted #2844 had 31 in 93 — the same 31 withdrawn by hand this morning, named exactly. The entry now carries `families`, the majority `family`, `attached`, `stranger_count`, `unattended`, and `strangers` — THE ROWS THAT DO NOT FIT, replacing a sample of the first twelve members. The reader's question is which rows are wrong, and a sample of the agreeing majority cannot answer it. `unattended` is the discriminator between a check that is too strict and a ledger full of junk: both canons flagged on day one were entirely audit-judged, and nothing showed that without opening each one. Scoped to the review surface. `canon_form` still answers at the precise form level for stamping and divergence, where a sync helper beside an async canon is a fair question; nothing here changes what the ledger writes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy |
||
|
|
2be17828a9 |
fix(ledger): live_rows_for called access with nothing in scope (#4208)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 45s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m39s
CI & Build / Build & push image (push) Successful in 29s
Lint caught an F821 that would have been a NameError the first time `stamps_to_review` was called: `access` is imported locally inside each of the seven functions in this module that need it — services/access reaches back here, so a module-level import closes a cycle — and the new function used it without one. I wrote the function by pattern-matching its neighbours and did not check what those neighbours do to make themselves work. Same shape as the tuple unpack two commits ago (#4207): the mistake is not in the logic I was thinking about, it is in the surrounding contract I did not read. Unit and integration were both green on the failing run (7095); only lint was red. Worth recording because the lane that caught it is the cheapest one and I had read its command as covering tests — `ruff check src/ scripts/` does not look at tests/ at all, so a clean test suite says nothing about it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy |
||
|
|
400253d039 |
feat(ledger): the ledger can say "these look wrong" without acting on it (#4208)
CI & Build / Python lint (push) Failing after 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 57s
CI & Build / integration (push) Successful in 1m6s
CI & Build / Python tests (push) Successful in 1m49s
CI & Build / Build & push image (push) Skipped
THE HALF THAT WAS MISSING. #4204 put a floor under what the write-path hook may assert. A floor only guards new writes; every row already stored stands (lesson #4202). Measured after that fix shipped: Portal carried 32 rows under one canon and 3 under another, all stamped on scores of 0.69-0.77 — below the 0.80 floor, so none of them could be written today, and all of them were still there. Scribe's own ledger carries 334 under #2860. `stamps_to_review` reports two things and changes nothing: weak — rows the hook stamped on a resemblance below the current floor, each with its score, signature and derived form. incoherent — canons whose own judged rows do not agree on a form. A canon claims some shapes are the same sort of thing; when its members are a class, three getters and a dozen tests, that claim has stopped being true and every base-rate reading built on it is reading noise. `canon_form` already made such a canon fall silent — nothing made it VISIBLE. IT DELIBERATELY CANNOT FIX ANYTHING, and that is the design, not an omission. The first version of this commit was an automatic sweep that reset rows by score. That is the original defect pointed the other way: what harmed the ledger was not one wrong score, it was a machine recording permanent classifications unattended. Un-recording them unattended is the same act with a wider blast radius. An agent reads the evidence, judges, and records the judgment under its own name through `classify_shapes`. `test_the_service_carries_no_machinery_for_bulk_withdrawal` asserts that structurally, so the next person to reach for an auto-retire has the argument again on purpose rather than in a diff nobody reads. A JUDGMENT IS NEVER LISTED AS WEAK, whatever its age. This is the measured correction to an assumption I nearly shipped: of Scribe's 334 rows under #2860, 302 are in `services/` — the canon's own home — and the ones sampled there are `classified_by="audit"` with no score at all. The legitimate bulk of that canon was never scored; it was judged by an agent in batch. Listing those as weak would invite an agent to withdraw the only real judgments in the ledger. An agent's decision is a different KIND of evidence, not a worse one. THE SCORE NOW HAS A PARSER. It lived only inside a prose sentence, so nothing could ask how strong the evidence for a row was without re-deriving it — which is how 32 rows sat unexamined for nineteen days. Format and reader are one constant apart (`_RESEMBLE_REASON` / `stamp_score`), with a round-trip test and a test pinned to reason strings taken verbatim from the two poisoned ledgers. `live_rows_for` is `live_rows` behind the project read gate, for callers that arrive from outside rather than from a job that already knows who is asking. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy |