Library: a rolled pick stands until the owner rolls again, and the AI accuracy ledger says what its numbers are - #229
Conversation
…he accuracy ledger says what its numbers are The magic book kept its pick keyed to a digest of the shelf, so rating a book, writing a note, adding a tag or renaming the shelf emptied the slot and pushed the owner into another roll. A roll is the only path that spends the engine, so ordinary library work was buying model calls nobody asked for. The fingerprint is gone. A pick now stands until the owner rolls it away and gives up its place for two free reasons only: the owner owns that book now, or has banned it. The AI accuracy ledger drew six bare ratios and hid the counts behind a hover, so "20 / 25" said nothing about what a point was. A legend names the hundred points once, every row carries its own unit, the count it was read from is drawn under its meter, and the hover keeps only the next step. Points are whole numbers; "Rated volume" is "Sample size".
…n incident report The review on #224 was right: the comment named a URL and a date, which is PR-description material and rots in the source. What matters is the rule itself, that only a switch between two libraries clears the filter.
|
Claude finished @manager's task in 2m 34s —— View job Review complete
SummaryMagic-book pick persistence. Removing the digest fingerprint and keying a stored pick's survival only to "owned now" / "banned now" (via the new One correctness gap: the daily-cap branch in AI accuracy ledger. The redesign matches the PR description well: a legend states the 100-point/six-signal split once, each row shows earned/max points as whole numbers ( Comment cleanup (#224 follow-up). Minor, non-blocking: No App Router/Tailwind/state-library/global-CSS violations, no |
| // that take a book off the shelf without a roll. | ||
| const owned = new Set(digest.ownedTitles.map(normaliseTitle)); | ||
| const bannedTitles = new Set(stored.banned.map(b => normaliseTitle(b.title))); | ||
| const gone = (title: string) => { |
There was a problem hiding this comment.
gone() is defined here but only wired into fromStore (the load path). It isn't applied in the daily-cap branch below (~line 164-174), which still returns current?.pick as 'ready' unconditionally when the cap is hit during a roll:
current?.pick
? { shelfId: shelf.id, status: 'ready', pick: current.pick }
: { ... }That means if a roll hits MAGIC_DAILY_CALL_CAP and the shelf's standing pick has since been acquired or banned, the response still reports it ready — contradicting this PR's own invariant that a pick gives up its place only for those two reasons. Worth reusing gone(current.pick.title) in that branch too.
A roll that hit the cap reported the standing pick as ready without asking whether the owner has since acquired or banned it, so the one path that skips the engine was also the one path that ignored the invariant.
|
Claude finished @manager's task in 44s —— View job Review complete
SummaryThe new commit ( current?.pick && !gone(current.pick.title)
? { shelfId: shelf.id, status: 'ready', pick: current.pick }
: { shelfId: shelf.id, status: 'empty', note: '...' }so a roll that hits No other files changed in this push — the rest of the diff ( Did not have shell access to re-run |
What changes
The magic book keeps its pick. It was keyed to a digest of the shelf, so rating a book, writing a note, adding a tag or renaming the shelf reported the standing pick as stale and emptied the slot. A roll is the only path that spends the engine, so ordinary library work was buying model calls the owner never asked for. The fingerprint is gone from the digest, the store and the route. A pick now stands until the owner rolls it away, and gives up its place for two free reasons only: the owner owns that book now, or has banned it. The AI shelf already worked this way through settle plus the bench.
The AI accuracy ledger explains itself. It drew six bare ratios (
20 / 25) with no unit and hid the counts behind a hover. Now a legend names the hundred points once, every row carries its own unit, the count it was read from is drawn under its meter, and the hover keeps only the next step. Points are whole numbers (11.2 of 20 was false precision),Rated volumeisSample size, and every detail string reads as a sentence.One review nit from #224, whose code is already on
dev: the tag-filter comment named a URL and a date, so it now states the invariant instead.Checks
tsc --noEmit: clean.