Which mark, where, and who says so - #88
Open
omars-lab wants to merge 66 commits into
Open
Conversation
A crop of print this size holds several marks and often two of the same name, so a trial that only said "put the box on the mark" was a trial about whichever mark the reader picked. That is not a small error: a placement on the neighbouring mark is a whole letter out, and the residual would have recorded a whole letter of registration error that had nothing to do with registration. So identification now travels with the trial rather than being recoverable from it. The corpus reader keeps the ligature a mark was drawn inside — the only join in that print between a mark and the letters it belongs to — and the rank is counted over the ligature rather than the word, because a word can run off the edge of a crop and "the third of three" over letters half of which are off-screen is worse than saying nothing. The first way of pointing at those letters was a mistake worth keeping in the record. Drawing them crisply in colour looks obviously right and would have quietly destroyed the measurement: the letters come from the other printing's drawing, carried onto our frame by exactly the fit that places the rectangle, so the visible gap between them and the ink underneath IS the correction, about a page unit, which at the size these panels are worked is a finger's width on screen. An hour of that and the landings would have been a tracing of our own answer. It is a wide blurred wash now, with an edge several times softer than the correction and no dependence on the fit being right — it says these letters and refuses to say anything finer. The mark itself is never washed. And one line of CSS, because giving .trial a display of its own beat the browser's rule for the hidden attribute and put all sixty cards on screen at once while the drag still moved the rectangle on whichever one the session thought was current. Restating the hidden case is the fix; the comment says why, since element screenshots cannot see this. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The forced choice can only ever say which of two rectangles is better, so §⑦ was opened to ask the other question — how far out is the one we propose — and this is the sitting that answers it. Fifty-nine of sixty landed nearer the corrected rectangle than the one the app draws today. The interval around that reaches down only to 91%, so the correction points the right way and it is not close. The typical miss fell from 1.15 page units to 0.39, which is the same sentence said in distances. The number that makes the rest readable is the hand's own: nine marks came round a second time from an independent starting point and the two landings agreed to 0.03 units. Against that floor the residual — 0.13 units, of which 0.07 across and 0.11 down — is four times the noise and therefore a fact about the boxes rather than about the reader's wrist. Only the down component separates from nought at the ordinary confidence, so the honest reading is that the corrected rectangles sit a tenth of a unit low. The pull of where each rectangle started came out at effectively nil, which is what the evenly-spread starting positions were for, and nobody once said the box was the wrong size. So: adopt, with the residual applied — and applied to the recorded per-page displacements, not to the arithmetic that derives them. The arithmetic is not what is wrong; the frame it is measured against is. That edit is mark-C's, not this commit's. What is not settled, and the record says so rather than leaving it to be noticed later: one reader, one sitting, sixty marks. A second hand disagreeing by more than 0.03 units would make this residual a fact about a person and not about the print. And it is not the other row's answer — a preference and a distance are scored separately and never summed, which was the whole reason for building two instruments. The transcript is committed the way the evidence records and the golden images are: it holds no scripture and nothing about a person. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The placing scorer treated sixty placements as sixty independent facts. They are forty pages' worth of fit: two marks on one page share that page's frame, so whatever is wrong with it is wrong for both of them, in the same direction, by nearly the same amount. Counted as pages, the residual banked as distinguishable from nought runs from 0.23 units up to 0.01 units down. It was marginal before the correction and it is not established after it. The lesson already existed here. probe-mark-ink.mjs has said in as many words, since the day it was written, that two marks on one page are not independent and a plain interval is therefore narrower than the truth. It never travelled to the scorer. So the estimators move into a library with the reason attached, and the library gets fifteen tests where the answer is known by construction — ten pages of six identical values must produce an interval about two and a half times the naive one, and values that share no page must agree. Three other things the output did not say and now does. Coverage, first, because everything else inherits it: the correction covers forty pages of 604, a trial needs a proposed move to start from, so every placement ever judged came from a page the correction was fitted to. Size: the gain is regressed and printed with the spread of the proposed moves beside it, and on forty near-identical pages that spread says the estimate could never have meant anything. And the negative results — not the mark's name, not a stretch across the page, not the starting point, not fatigue — because "we looked and found nothing" is what a later reader otherwise pays to rediscover. The residual itself does not move. Every change is a claim the output failed to qualify. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The record said "adopt the correction WITH the residual applied, only the down component separable from nought". The first clause was decided on the second, and the second does not survive counting pages instead of placements. Corrected in every place it was written: the ledger result, the issue, and the design section. It re-opens rather than closes, because the question is literally how far and the distance is unresolved. What the sitting did settle is untouched — 59 of 60 landed nearer the corrected rectangle, and nothing that lopsided is reachable by a clustering adjustment. The direction is settled and the distance is not. Two limits the sitting could not see past get a section and a row of their own. The correction covers forty pages of 604, and because a trial cannot be built for a page with no proposed move, both by-eye instruments have only ever been able to ask about those forty — the correction has been checked exclusively where it was fitted. Measuring all 604 is arithmetic and needs nobody's time; what it then decides is whether the corrections vary, which is the leverage the placing session lacked. If they vary, size becomes measurable and a second sitting is worth someone's half hour. If they are all alike, one number is the right model and the remainder is moot. And the answers a reader gave move out of a downloads folder. docs/validation/rulings/ is the third of three neighbours and the README says which is which: a transcript says a sitting happened and here is how it went, an evidence record says a machine ran and here is its exit code, a ruling says here is what was answered. It is the input a scorer re-reads to reproduce a verdict, so a verdict whose working lives on one laptop until the browser clears it is a verdict nobody else can argue with. The seed is in the name because the seed is what rebuilds the answer key. No scripture in any of it — page numbers, mark indices, offsets and timings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
A file of per-page corrections read as though it described the mushaf. It described forty pages of it, and nothing downstream could tell — not the scorer, not the session builder, not a reader opening the JSON. The omission compounded: a by-eye trial can only be built for a page that has a proposed move, so the placing session asked its questions exclusively about the pages the correction had already been fitted to, and said so nowhere. `coverage` is now the first field under the header, before any of the numbers it qualifies, and it says which pages were opened, how many survived the minimum-marks floor, and what fraction of 604 that is. Its note carries the distinction that matters: a page with no row was never looked at, not found to be correct. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The full pass ran: 30,000 marks, 604 pages, seven minutes. Six hundred pages carry a measured correction now instead of forty. Pages 1, 2, 603 and 604 do not and never will from this route — their ornamental frames never draw enough marks to be worth fitting — so they need a stated fallback rather than a silent one. The question the pass existed to settle was whether the corrections vary. They do, and the first forty pages hid it in a way worth writing down: they were representative in the middle — their mean is indistinguishable from the other 560 — but understated the spread by more than half, and most of the spread they did show was never real. Measuring those same forty twice, from different samples of their own marks, disagrees by about as much as the pages differ from each other. What looked like forty slightly different pages was largely one page measured noisily forty times, which is exactly why the sitting built on them could confirm the correction's direction and never its size. Across the whole mus'haf the proposed moves span over a unit and a half rather than a third of one, so the size becomes measurable for the first time — by a sitting that draws from both ends of that range, not at random. Down behaves nearly like a single number for the print; sideways it does not, and the pages that need no sideways move at all, along with the one page that wants to move the opposite way down, are where a reader's eye settles the question fastest. One limit the pass added rather than removed: taking each page's own displacement out repairs most of this error and measurably not all of it. A fifth of marks are still too far out afterwards. Whatever that is, it is not a page-level shift, and it is not the same quantity as the leftover distance in §⑦ — that one is a further move of the correction, this one is scatter the correction cannot reach. Neither is applied. Both displacement files come home beside the answers given about them, named with the fingerprint the rulings pin, which is what finally lets a banked verdict re-derive from committed bytes instead of from a rebuild only one laptop could do. The issue's id was renamed because it had become a false statement: the coverage half is closed, and what stays open is that no reader has yet judged a mark on a page outside the original forty. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The first placing session drew from every page that had a measured correction, which was the same forty the correction was fitted on. Two defects followed, and they turned out to be one defect: it could only ever be an in-sample check, and across those forty pages the proposed move barely varied, so the regression that would say whether the correction is the right *size* had no leverage. The gain came out -0.10 +/- 0.68 -- "exactly right" and "a fifth short" were the same answer. Over 600 pages the move does vary: the across component spans 1.75 units where the original forty spanned 0.31. So both halves have one fix, and it is a choice of pages rather than a bigger session. selectPages() holds out the pages the correction was fitted on and takes what is left from the ends of both axes. Built against the 604-page file it returns a set spanning 1.750 across and 3.375 down, against 0.375 and 0.375 -- four to six times the spread, with the mean unmoved and no overlap with the fitted set. The scorer, on its own existing criterion, flips from "Undecidable from this sample" to "Decidable". The scorer rebuilds a session from its seed, so a narrowing the builder applied and the scorer did not would put every trial index against a different mark -- and nothing would throw, because the indices would all still resolve. The residuals would just be subtractions of the wrong numbers. So the builder records its page list in the session head and the scorer replays it. Replaying rather than recomputing is deliberate: were the scorer to re-run the selection, a later improvement to how pages are chosen would silently re-score every sitting ever banked. Three things guard the join. The list is fingerprinted into the browser's resume key, so two builds from the same displacements with different pages cannot stack one's answers onto the other's trials. The scorer refuses a displacements file missing any page the session was built over, which catches the wrong file even when the fingerprint is right. And the coverage block now reads out whether a sitting was in-sample or held out, because that is not something answers can be asked to reveal. The 2026-08-12 ruling carries no page list, re-scores through the old path, and prints numbers identical to the ones banked from it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The sitting built in the previous commit can draw its marks from pages the correction has never been checked against. This is the half of that work the plan insists comes first: what it expects, banked where it can fail. A new ledger check, placement-holds-off-its-own-pages, carrying four predictions and — for each — what its own failure would mean. Direction on never-fitted pages at 90% or better, or the correction is a property of the forty pages it was fitted on and mark-C stops. The leftover distance returning either near (-0.073, -0.110) or near nought, which are different findings and which no earlier sitting could tell apart. The size estimate at 1.0 with an interval a fifth of a unit wide rather than two thirds, which is the whole reason the pages were drawn from the extremes; reliably below 1 means the correction is short and should be scaled, not shifted. And, from the five-page block, the between-page spread exceeding the within-page one, which is what the clustered interval has had to assume the worst about since it was written. A departure from the plan's own pre-registration, stated rather than quiet: it assumed a sitting with the residual applied. Both blocks are built on the raw 604-page correction instead. Applying it first would have measured the leftover on top of itself, where a genuine nought and a lucky cancellation look the same, and would have built an unresolved number into the instrument meant to resolve it — which the plan separately forbids under Not doing. What it still cannot answer is whether the leftover is a fact about the print or about one reader. Only a second person placing the same rectangles separates those; that block is designed and not built, so the check says in its own words that whatever it banks is one hand's. The rest is links: §⑧ of the registration record says the sitting exists and nobody has sat it, the code map gains selectPages and the two guards, the rulings README explains the second fingerprint, and both neighbouring issue rows name the new one back. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
A placing session cannot tell a print that is out by this much from a reader who puts rectangles this way. The two produce the identical number and nothing in one sitting's output separates them. Only a second person working the identical session can. So the session records whose hand it was, and the reader is the one field that never reaches a trial: the marks, their order and their starting points must be the same for both, or the two hands are not comparable and the whole reason for asking a second one is gone. It is folded into the resume key and the download name instead, because two people on the same build would otherwise share both — the second would resume into the first one's answers. The scorer's --against reads the two together. The difference is taken mark by mark, never average against average: two hands a fifth of a unit apart on every rectangle in alternating directions have identical averages and have agreed about nothing. It is read against each hand's own measured wobble, and where a hand repeated nothing there is no scale, so it declines rather than inventing one. No threshold — what two hands on this screen actually manage is the number that belongs there. sameBuild is what has to match first, and it names every mismatch rather than the first. The fifth check is the one worth having: two sittings by the same person is a hand compared with itself, which returns beautiful agreement, answers nothing, and leaves nothing odd in the printed output. 134 etl tests, two of them built to fail the tempting implementation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The runbook gains a third, optional page — the first block again, same marks, same order, same starting points, worked by somebody else — and a step for reading the two together. About ten minutes of a second person's time, and the record says explicitly what it means when nobody sits it: whatever leftover distance gets banked is one hand's leftover distance, in those words. A fifth prediction goes in before anybody places a rectangle, so it can fail: the two hands come back within about five hundredths of a unit of each other. Wider than that and the leftover belongs to whoever was sitting there — applied to nothing, no averaging the two, no splitting the difference. The design doc's §⑧ no longer says the second-reader block is designed and not built, the rulings README says why some files name a person, and the map picks up the two library functions this needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
A trial cannot be built for a page with no proposed move, so while the correction covered forty pages, every question the by-eye session could put came from one of the forty it had been fitted to. That is the same limit the placing session was rebuilt to escape, and it was left in place here on the reasoning that a forced choice is a separate instrument whose headline does not depend on the correction being right. The limit is about which pages can be asked about, not about what is estimated from them, and this is the check that actually releases the mark layer. So the builder takes --exclude and --pages, and selectPages grows a second strategy for it. The two sittings want opposite things from the same function: a placing session reads a slope, a slope is bought with leverage, and it takes the extremes. A forced choice reports a proportion, which has no leverage to gain and its representativeness to lose — ask it only about the extremes and it fills with the easiest trials and the impossible ones, then reports the result as a fact about the mus'haf. `even` walks systematically through the print, half a step off both ends, with no seed to remember. The scorer replays the recorded page list rather than choosing again. It rebuilds the trials from the seed, so a session narrowed at build time and scored unnarrowed lines every index up against a different mark and throws nothing; and recomputing instead of replaying would let a later change to selectPages silently re-score every ruling ever banked. Two guards, not one: the displacement fingerprint, then a check that the file actually has a row for every page the sitting used — a file can carry the right fingerprint and still be the wrong file for these pages. The page fingerprint also joins the resume key and the download name. Two sittings under one seed that asked about different pages would otherwise share a localStorage key, stacking one's answers onto the other's trials, and land in a downloads folder under the same name. Ten tests: five on the new strategy, verified end to end by building a held-out session and scoring a forged perfect ruling through it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
…seen The by-eye check's setup rebuilt the forty-page measurement and drew a hundred trials from it. It now builds against the committed 604-page corrections, holds out the forty the fit was made from, and keeps forty of the remaining 560. Its result line says so, and the reading gains a sentence saying the verdict is about the whole print rather than about forty pages — a percentage a year from now cannot say which question it answered. A step goes in ahead of the trials: if the same person is also going to sit the placing session, this one goes first. Dragging rectangles onto marks for twenty minutes is the most efficient way there is to learn where our correction tends to sit, and a reader who has learned it answers these hundred trials from the rule rather than from the ink. The reverse order costs nothing. This discards the five answers banked against the old build. Five in-sample answers are not worth the coverage claim they cost. The design doc's §⑩ ① says what changed and why, its command block names the corrections file at both ends, and the pointers gain the two guards. The map, the issue row and the rulings README follow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
…er lie about Nineteen hundred marks could not be placed from their own ink, and the only instrument that can settle them is a person looking at each one. This is that instrument: a built page per sitting, sixteen of them, every mark seen rather than sampled. The page shows one mark on its own crop of the print, with the rectangle we ship and the rectangle the reader drags, and asks a single question. Six answers, one of which is that nothing is wrong — the affirming answer costs exactly one tap, the same as every fault, because making a fault cheaper or dearer than an affirmation biases the one ratio the sitting exists to measure. A nudge pad moves the box in fixed steps for the corrections a thumb cannot land, and both take-it-back controls sit away from the corner the next-card button occupies, which is a lesson from two prior mis-taps rather than a preference. The rectangles are drawn in colours that are never re-themed, because the paper is never re-themed: a mus'haf page stays on white, and a dark theme that recoloured only the strokes took them to 2.49:1 and 1.70:1 on that white. A reader who cannot see the box affirms it, so that failure runs in the direction that looks like success. The reader's rectangle carries a dash pattern as well as a hue, so the distinction survives colour blindness and survives anyone re-theming the palette later. Every card mounts its paths once and then writes attributes, instead of reparsing up to twenty-three kilobytes of path data on every pointer move. A correction that stutters is a correction the reader gives up on and affirms instead — the same failure again, arriving as a performance number. Answers are banked as they are given, to a server that only ever appends, and the whole transcript is written again on every hand-over. The running log has been short of the browser's copy before, so the file the reader hands over is the record and the log is the safety net, not the other way round. And the count under the card no longer disagrees with the count the next build prints. Handing over used to change nothing a reader could see: the deal is fixed when the page is built, so the only thing that ever moved the total was building the sittings again from the answers, on a laptop the reader is not sitting at. Someone banked an hour, watched the number stay where it was, banked again, and watched it stay again. Nothing was lost on any of those presses, but an instrument that cannot show somebody their own work is one they stop believing, and this one asks for forty hours on trust. So the deal and what is left of it are two lists now, handing over retires what it handed over, and the retired marks are persisted — a reload that brought them back would be the same failure one refresh later. The transcript is deliberately not retired with them: it is written under one name, so a later smaller write would silently destroy an earlier larger one. The scorer reads one row per mark rather than one per event. Medianing increments cancels opposite-signed nudges and lets one mark with forty-four events outvote twenty-five marks with one each; it printed 0.000 across and 0.000 down while the reader's hand had moved every box. It now prints the hand, and separately where the reader landed against what ships, under two sentences that say which is which so nobody differences them. Everything the page emits lives inside a template literal, which has broken this file three times. Two assertions now say so out loud: no backtick and no interpolation marker in the emitted HTML. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
…s a page anyone can open The placement question is open and now has somewhere to send people: a page that draws each option on real pages of the print at the size it would actually be used, with the measurements beside it rather than only the argument. A wash you cannot see at that size is an answer, and it is one no paragraph would have given. The page names the script that rebuilds it, the record links both, and the row in the register carries the link and the copy because a link with no copy dies the day the host does. The design notes carry what the measurement actually found: which marks are placed from their own ink and which inherit the line's tilt, what the match threshold and the window-edge rule are for, and why the fallback population is defined the way it is — the placed set could not contain a gross error by construction, so a clean result there bounds visible error at about five per cent and not at zero. That caveat has to travel with the number or the number will be read as saying more than it can. The map gains the sitting page, the server that keeps its answers, and the scorer, and the sitting page's note is where the interaction lessons live: why the destructive controls are not in the thumb corner, why the affirming answer costs the same as a fault, why nothing drawn on the paper is themed, and why the deal and what is left of it are two lists. That last one has three load-bearing parts — the arithmetic has to match the builder's exactly or the two counts go on disagreeing, the transcript is not retired with the deck because it is written under one name, and the reader's place moves with the deck rather than resetting. The ledger's runbook is the reader's on-screen instructions, so it moves with what the page now shows, and two sittings already sat are banked beside it. The issue rows are only for the findings that distorted a measurement — the invisible rectangles and the scorer that printed zero — which is this repo's line for a review tool. The ergonomics are real and are not issues. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The nudge pad offered half a unit or a tenth, with a button to swap between them. Both numbers were defensible on their own: half a unit is a seventh of the height of a mark and about the size of the error being corrected, and a tenth is well inside it. As a pair they were the wrong shape for what a reader actually does. The first press covers most of the distance. Everything after it is spent on whatever is left over, and what is left over is different on every mark — a hair on one, most of a unit on the next. A reader with two numbers on offer rounds their intention to the nearer one, and a tenth was simply the nearer one every time. Twenty-six marks took two hundred and five nudges and drags in the sitting already banked, which is a complaint about the controls before it is anything else. So it is a slider now, a hundredth of a unit to half a unit, in hundredths. The ends are where the two sizes were. The bottom one is below the width of the stroke that draws the rectangle at the close framing, which is the point where pressing again stops changing anything the reader can see — there is nothing under it worth offering. Two things about it are worth knowing later. The bounds are written twice, once in the slider's own attributes and once in the clamp that guards what comes back out of storage, and drift between them would be silent: a value the slider offers and the clamp rejects sends every press after it back to the coarse end without saying so, changing the size of every answer banked from then on. A test asserts they are still the same two numbers, and runs the clamp rather than reading it, because it is also what stops a stored zero from turning the pad into a control that no longer does anything. And the size survives a reload, for the same reason: it is the size of every answer about to be given, and a quiet return to the default would change that size without changing anything on the screen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The nudge pad note gains the reasoning: why two sizes were the wrong shape for the correction readers actually make, where the ends of the range come from, and the two things that would go wrong quietly — bounds written in two places drifting apart, and a size that does not survive a reload changing what every later answer is worth. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
A sitting hands back every press. A reader who pushes a rectangle left, overshoots, pushes it back right and moves on has said one thing about that mark, and the file holds four. Every scheme that reads the route gets it wrong, and the two obvious ones get it wrong in the two most convincing directions: averaging the presses says it never moved, adding up their sizes says it moved twice as far as it did. Neither reads as an error. This has already cost a number once — a scorer printed nothing moved for twenty-six marks that had every one of them been dragged. So the collapse lives in one place, lib/mark-settle.mjs, imported by both the scorer and the settler rather than written twice. Position and size settle to the last thing the reader did; the words they used gather and repeat once, because being called moved eleven times is one complaint and counting it eleven lets one stubborn mark outvote a page of easy ones; and what the route leaves behind is kept apart as a count, which is a finding about the nudge controls rather than about the print. settle-mark-report.mjs turns one or more transcripts into a ruling: one row per mark, both distances under separately worded names — the reader's own hand, and where they landed measured from the uncorrected box — never differenced, because the gap between them is only the correction already applied and subtracting them looks exactly like finding a discrepancy. It refuses a sitting taken against different displacements and an answer that disagrees about which rule drew a mark, and it exits 0 for bad news: a build that failed because a sitting found everything wrong would teach everybody to stop sitting them. The count a sitting banks is fixed here too. The page was writing down how many marks were left rather than how many had been seen, and both readers now take the marks actually spoken about as a floor and print the file's name when they raise a claim to it. The direction is the durable part: a count of marks somebody looked at can only go up. The --issues draft now comes out in the shape the register actually takes, and a test holds it against docs/issues.json rather than against a shape this repo invented, so a pasted row cannot fail the gate and teach somebody to edit the gate. 276 etl tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Four registers, one skill, one ruling. What was missing was not the arithmetic — it was everything after it. A sitting ended with numbers in a terminal and nothing checked in, which means the next person who needs its answer re-sits it. The skill is the five steps in order: settle, score, read, route, rebuild. The order is not decorative. The first two produce the numbers, the third decides what they mean, and the last two are the only reason the hour was worth buying — including the four questions people skip when they read a rate, of which the third is whether this is a measurement of the print or a measurement of the sitting. The ruling is the first hour over the marks the correction cannot place from ink: 115 marks, 351 answers, 114 faulted, 209 goes to settle 114 positions, and fourteen marks a reader called odd in the print itself. It carries no scripture — page numbers, mark names, and offsets in page units — which is why it can be committed at all. Two new open questions in the placement document, and two rows indexing them. ⑬ is the count a sitting banks, closed. ⑭ is those fourteen marks, and it is open and owned by a person on purpose: every instrument here reads the same bytes the reader was shown, so a reading that is wrong about what the print contains is wrong identically in all of them. It is settled by holding the pages against another copy of the print, and by nothing in this repo. The rulings directory learns its third kind of file and why the route is thrown away when it is written. The sixteen sittings are rebuilt: 147 answered marks drop out, 1,730 remain, no part duplicates another, and nothing already answered comes back round. The check itself stays pending — one hour of sixteen is not a result, and recording it would say it was. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Two things a sitting needed and did not have. The first is a gesture. Saying our rectangle is round the wrong ink used to mean dragging a new one by hand, which is why nobody ever said it: in 564 answers that word was used exactly zero times. Now each card's window is cut into the pieces a finger can point at, and a tap moves and resizes the rectangle onto the piece that was tapped. A tapped answer is recorded as a tapped answer, not as a hand-placed one, because the two measure different things and only the second is a measurement of somebody's hand. The second is a route home. An answer leaves a sitting one of two ways -- banked by the serving side as it is given, or written into a file when the reader hands over -- and the builder has always honoured both, so a mark answered either way drops off the screen for good. The settler read only hand-overs. On the first sittings over the marks we could not place from ink that lost twenty-five marks of somebody's work: taken away from the reader, and present in no ruling. It now reads the running log too. The same statement usually arrives by both routes and is counted once, since how many goes a mark took is a finding about the controls and doubling it is a made-up finding. A log may not be settled alone -- it carries no head, so there is nothing to check it against, and it is refused in those words. 290 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The sittings over the marks nothing could place have now been read back: 160 marks, 564 answers, five sittings and the running log, settled into one ruling that supersedes the one covering 115. 158 of the 160 came back carrying a complaint. Read that as a fact about the population before reading it as a fact about the print -- this set is by construction the marks the machine could not place from ink, so a near-total fault rate is what it was selected to produce and is not news. The composition is the news. Only 15 of 160 were called odd in the print, so the leftover is overwhelmingly ours to fix rather than the printer's, which is the opposite of the comfortable reading. And where the reader left those rectangles runs the same way and roughly the same size as the ink measurement, arrived at by an instrument made of nobody's arithmetic. Two of the six things a reader can say were never said once. One of them only became cheap to say this week, so that is a reading to take again after the next few sittings rather than a licence to delete a word now. The population is 1,877, not the 1,851 every document here has been quoting. Recomputed from the rows under the builder's own rule, and it reconciles: 1,877 less the 160 settled is exactly the 1,717 still dealt out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
docs/issues.md orders everything unfinished worst-first, which is the order you want when you are choosing what to fix. It is the wrong order for the question somebody actually arrives with — I have an hour, what is waiting on me? — and it is worse than the wrong order for two of the four registers it reads: an open decision and a human-only check appear in it as a bare identifier and nothing else, so the three questions this project is holding open and the nine checks a machine cannot run were, in practice, invisible. So: the same facts, cut by owner. Nothing on the new page is authored — every title and href is read out of the register that owns the item at build time, which is why `anchor` and the title-and-href resolver moved out of the issues builder and into the shared reader. Two pages linking the same item two different ways is the failure the whole catalog exists to prevent, and three lines of GitHub-slugger trivia copied into a second file is a pair that agrees today and stops agreeing silently. The roadmap rows are parsed rather than stored, and the predicate cuts the status at its first space or bracket: several rows annotate their status in place, so comparing the whole cell files every annotated row as unfinished and comparing a prefix files the deferrals as done. Both are silent. gate:tasks is the same hash stamp the other four generated pages carry, wired into the three places gate:gates insists on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
3 decisions open, 9 checks only a person can run, 20 other items waiting on the person who owns this, 17 for whoever picks them up next, 4 loops not finished. Generated and committed like the other four; do not hand-edit it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
A tap on the rectangle could bank a placement nobody made. Two boundaries in two different units decided what a gesture was: a tap had to end within ten screen pixels AND inside 600 ms, while a drag banked a placement past 0.05 page units. At the framing that shows the whole word, ten screen pixels is about 1.5 page units — thirty times that floor — so a finger resting three-quarters of a second with a few pixels of tremor was too slow to be a tap and far too far to be nothing, and banked half a unit of correction on a mark 5.6 by 3.6. One boundary now, in one unit, with no clock. The clock never asked anything worth knowing — a slow tap is still a tap — and only distance can answer whether a finger stayed still. Screen pixels rather than page units because a finger is the same size on every card and a page unit is not. Which piece of ink a tap reaches was decided by area alone across everything within a fingertip of slack, so a finger squarely inside a large piece could be answered with a small piece it had merely come near. Landed-on now beats came-near: aiming at a piece's own centre and getting a different piece falls from 3.7% to 1.2% at the wide framing, and taps on blank paper reaching past nearer ink to something smaller further away, 2.3% of them, stop. And a tap that reaches no ink says so, because silence is indistinguishable from a control the page never received. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The registers for the tap fix. The design document gains a sixteenth open question, marked fixed: what the two boundaries did, why one boundary in one unit with no clock replaces them, and the two smaller corrections to the same gesture that the audit turned up alongside it. It earns a row in the issue index rather than an ergonomics note because it made a number wrong, which is this repo's line for a review tool. What neither the row nor the document can clear: every transcript banked before the fix carries whatever it produced, and nothing distinguishes those placements from real ones. The affected marks are the ones reached for by tapping the ink — the same population the previous row says must be read apart from hand-placed answers — so the caveat travels with that one rather than needing a denominator of its own. The code map's note on the sitting page carries the new rules and the measured before and after, so the next person to touch that gesture finds out what the old one cost before they reinvent it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The step that tells a reader how to move a rectangle described a gesture that has since changed twice. A tap on blank paper used to do nothing and say nothing; it now says there is no ink there, because silence is indistinguishable from a control the page never received. And a finger resting on the rectangle used to bank a placement nobody made; it is a tap now, however long it rests. Both are things the reader sees, so both belong in what the step tells them to expect. A runbook whose expectations do not match the device is worse than none, because it still looks authoritative — and the person walking it is the last one who could tell. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The marks a tilted line leaves badly out have something in common: each other. More than half sit on the one printed line in fifteen that has gone wrong as a whole, and a mark at either end of a line is about twice as likely to be badly out as one in the middle. That is accumulation that does not accumulate at a constant rate, and a slope splitting the difference is wrong in the same direction at both ends. So the correction is allowed to bend. Held out over all 326,515 rows and 8,702 printed lines: badly out 4.95% -> 4.12%, median leftover 0.224 -> 0.198 units. The shuffled control holds at the new rung — another line's bend costs 34.40% against 18.20% for no per-line correction at all — and a cubic was fitted the same way and refused, better trained and worse held out, so the ladder stops at the bend rather than wherever the arithmetic stops improving. A curve has three terms where a tilt has two, so the floor is stated per term rather than per group: three observations a term, written down so lowering the floor cannot quietly buy a curve nobody has the marks for. The evaluator is one copy shared by the correction and by its own shuffled control, so a new rung cannot be right in the model and wrong in the thing meant to refute it. What bending does not clear is said out loud in the same paragraph as what it does: it fixes the middle of a line and barely touches the ends, which stay at about twice the middle. That is not a shape in where along the line a mark sits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The catalog row for why the marks were ever in the wrong place ended on a sentence saying a minority of rectangles are badly out for a reason of their own, and that the reason had not been looked for. It has been. The finding, the figures, and the honest limit go into the row and into the record it mirrors. Both halves are written down. Whole lines go wrong rather than scattered marks, the ends of a line are twice the middle, and letting a line bend pays 4.95% -> 4.12% held out — but bending fixes the middle and barely touches the ends, so a mark at the end of a printed line is still twice as likely to be badly out and whatever causes that is not a shape in where along the line it sits. The row stays open. It closes when a placement is chosen and shipped, and this changes neither. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The finding that whole printed lines go wrong together, and go wrong in a shape a steady change from end to end cannot follow, is now an option a reader can look at rather than a paragraph in a record: option I, drawn on the same page of the mus'haf as the other five, at the same size, beside the same printer's ink. It sits next to F rather than last, because the two differ by exactly one thing and the point of drawing it is to see that difference. H stays at the end, where its number goes on meaning something other than the others'. The board's column list is now written by the builder from the number of options. It was a hard-coded five, and a sixth would have gone behind a horizontal scroll on the one element of the page whose whole job is comparing across — hidden from anybody who never thought to drag it. The stylesheet keeps an auto-fit fallback for the same reason. Two sentences elsewhere were false the moment I existed and are corrected rather than left: F's reservation said nobody had looked for the cause of its leftover, and section 12 said finding it could make another option. Somebody looked, and it did. What is still true, and now says so in three places, is that bending fixes the middle of a printed line and barely touches the ends. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The decision index gains the option; the record gains a section for it — where it came from, what it measures, the control it clears, the rung above it that was refused, and the reservation that it fixes the middle of a line and not the ends. It is deliberately not a row in section 7's table. That table is a 120-page scoring run and this option was fitted after it, so only the badly-out column can be reconstructed without re-scoring every page against its own ink. A row of four dashes in a table a reader scans downward reads as a worse score rather than as an unrun measurement, so the one number it honestly has is stated in a sentence instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The index over the sittings was typed by hand, so it was right on the day it was typed and wrong every day after. It was telling a reader that 26 marks were answered and 1,851 were left, cut into sixteen sittings of about 116. The true numbers were 167, 1,710, and 106 or 107 apiece, and the sittings behind the very links on that page had been rebuilt twice since. The count is the only reason to open that page rather than the sittings directly, so a stale count is the whole page being wrong. It now reads the HEAD block build-mark-report.mjs already writes into every sitting it emits and adds them up, which means the page cannot disagree with what it links to. Parts from two different deals are refused by name rather than added together, that being the one state where an index would mislead about how much work is left. The stamp is local time, because the reader compares it against a file listing on the same machine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The prose the two code commits made false or newly provable, and the decision they opened. LICENSES.md said the app code is not a derivative of the corpus data because it reads the shards at runtime. The premise is true — every asset the web app reads is fetched by URL, nothing imports the pipeline's data directory — but the conclusion did not follow, because the content that was there arrived by typing, not across that boundary. It now claims what it can prove. It also said the page artwork is reproduced unmodified; svgo runs at one decimal place, so it now says what is applied. SOURCES.md said the artwork pipeline applies two declared transforms against code that says three: 23 polygon repairs across 19 pages and 2 id repairs, the description of which omitted the polygon repairs entirely. The code was right and the register was stale. what-we-distribute.md predicted the scripture fix and now records what was done, including the second site, which it did not know about. what-we-depend-on.md is new and carries the dependency and licensing survey it had no home for. The new decision is comparison-crop: when the app shows an ayah cut out of the printed page, what should it do about the neighbouring verses that come with it. Five options, each drawn on the real artwork at the panel's real width, and the cost of choosing none of them measured across all 5,088 crops. The prior art moved the recommendation, which is the point of looking. The composite — cropping an ayah out of a page and setting it beside its look-alike — nobody appears to have attempted; the largest public library of Quran data publishes mushaf layouts and mutashabihat as separate downloads and has not joined them. But the hard part of it, marking a run of text that wraps across lines, is solved, and four independent traditions solve it the same way: per line, never by a box around the whole run. CSS says a highlight is one overlay per box fragment, which is why every text selection you have ever made looks the way it does. CSSOM View gives the two shapes two different methods and we picked the union one. The Web Annotation model is explicit that its rectangle selector cannot describe a non-rectangular region and points at an SVG selector instead. hOCR and ALTO both put geometry on the text line, and ALTO has an element documented for exactly a block whose bounding shape is not a rectangle. The IIIF image API can only cut rectangles, which is worth naming because it explains why our crop is one: a rectangle is what the tooling hands you, not what the content is. So option B — cut each line down to the ayah's own words — stops being the boldest of the five and becomes the conventional one, and its remaining objection is appearance rather than correctness. That is a question about taste and reverence, which a stranger to this code is better placed to answer than its author. Recorded as open, with what would change the answer stated: a hafiz reading the panel, and fifteen minutes finding out whether B's raggedness reads as broken to anyone but me. One search caveat is on the page rather than hidden here: WebSearch was exhausted at 200/200, so all of the above is primary-source fetching against known URLs. The PDF highlight convention, which I believe is the same one a fifth time, could not be confirmed with a link and is not counted. The registers: comparison-crop is in decisions.json with related named in both directions on mark-placement, word-selection and loop-4a; issues.json moves the four rows this work closes and gains one for the second scripture site, which no row covered; map.json gains rows for the new gate and the new generator, and records what the notices gate now enumerates; the validation ledger and use cases follow the panel's rewrite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Option F composes C and D — veil the neighbours, and mark the shared words too — and then changes what the marking means. The two tints stop saying WHICH verse you are looking at and start saying WHAT the words are: green where the pair agrees, yellow where it parts, identically on both halves of the panel. The re-assignment is the substantive half. Which verse is which was already written above each crop in words, so the colour was spending itself on something the reader could already read. Whether a phrase is shared or divergent was written nowhere, and it is the single thing a hafiz opens the panel to learn. Drawn from the same two calls every other option here is drawn from, so the geometry needs nothing new: the evenodd scrim is the frame minus bandsFor's per-line rectangles, the green band is the shared range, the yellow bands are divergentRuns. That is why the record can say the component move is proven before it happens. The green is deliberately not the app's verdigris. Verdigris is one of the two colours F retires, and reusing it would carry the old meaning into the new scheme; it is a leaf green instead, with the reason in a comment beside it so the next person does not tidy it back. The yellow is pushed to an ochre because a fifth opacity of anything lighter does not survive over ink on cream. The page also now records that the question is answered: a decided pill, the winner marked in the option list and the glance rail, and the chosen drawing repeated at two and a half times the panel's real width with a three-swatch legend — because the specimens are at the size a reader actually gets, which is the honest size and the wrong one for judging two new colours against each other. Both sizes are on the page and the caption says which is which. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
comparison-crop goes to decided: option F, omar, 2026-08-16 — veil the neighbours, mark the shared words green and the differing words yellow. The record says why the prior art lost. Four independent traditions all mark a wrapped run per line rather than boxing the whole thing, which pointed squarely at option B, and B was not chosen: every one of those precedents answers WHERE to draw and none of them answers WHAT the drawing means. B removes the ambiguity by removing the neighbours; F removes it by naming all three states outright. On a page somebody is memorising from, saying what a mark means beat inheriting a convention about its shape. What it costs is recorded rather than buried: the two halves of the panel no longer differ by colour, so a crop glanced at without its label has lost a cue; yellow at a fifth opacity over ink on cream is the hardest thing here; and F is the busiest of the six, inheriting C's objection and D's at once. It also supersedes something this record had listed as settled — the two wash colours were chosen elsewhere, and F changes not merely which they are but what they are about. That is part of the decision, not a consequence of it. The five losing options stay on the page. They are why it was a choice. The register work that follows from it: word-indexing.md gains item ⑥ for the component that has not been written yet, and issues.json gains its row — a build row rather than a question, since the choice is made and the drawing is checked in. The row carries what the fix needs, the measured cost of not doing it (5,088 crops, 69.8% mean share, 820 of them more neighbour than verse), and the fallback that must survive it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
…drawn The reader's page for the decision register is a table. A table is good for finding the row you came for and cannot answer either of the two questions people actually arrive with: what is still open, and what is this one leaning on. The second is not a shortcoming of that particular table — relatedness is a fact about a PAIR, and a table has to pick one of the two rows to write it in. So this draws it instead. Nineteen decisions on one time line, an arc between any two that constrain each other, and a card apiece carrying the question in plain words, the options as they were actually put, and the winner marked. The three nobody has chosen come first, because they are the only ones anybody can still act on. Fifteen constraints across thirteen decisions turn out to be there, and half of them run into something still open — which is the picture arguing for itself: the open questions are not off to one side, they are load bearing. Nothing on the page is typed twice. The question comes from the register, which is the only place it is stored. The answer-in-one-line is the record's own title, read at build time for exactly the reason decisions.mjs states beside titleOf() — a title living in two files is right for a while and then quietly stops being right. Counts, dates, arcs, option strips: all derived. If the page and the register ever disagree, the register is right and the page has not been rebuilt. Two things it deliberately refuses to do. Undated decisions are parked to the right of a dashed break rather than placed on the line, because putting an unanswered question on a day is the one lie a picture like this can tell. And where four decisions share a day, the month labels drop below the deepest of them rather than sitting at a fixed offset — a constant looked right until the day that got a fifth. One copy rather than two: it carries no page artwork and no external asset, so the published copy is the checked-in file. Nothing gates it, so the map row says to rebuild it in the same commit that moves a register row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Two notes written on 2026-07-20, the day the project was named, kept until now outside version control. They record the one thing the tree cannot: what was settled in the conversation that preceded it. The app was designed under the codename "Linker · رابط" and renamed Hifth that day; web-first with touch as the primary input, built in loops with each loop a vertical slice demoed on a phone, and a component architecture treated as a requirement rather than something to be retrofitted. Every one of those has held, and none of them is derivable from the code that came after — a repo can show you that the loops happened, not that somebody chose to work in loops. Committed at the owner's direction. The note carries a link to the original design conversation, which is now as public as the repository. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The four it did not know about were built in the scratchpad, published, discussed, and the scratchpad was later cleared. So a diagnosis, a comparison carrying a recommendation, a plan and a finding that settles a question now exist only as an address on a host we do not own. That is precisely what gate:decisions already refuses — a link with no copy dies the day the host does — arriving at pages nobody had thought to attach to a decision, which is the only kind that gate ever asks about. docs/artifacts.json is the inventory: every page published, what it shows in one sentence, and what owns it. For the four that belong to a decision it does not repeat where the copy lives or how to rebuild it, because decisions.json owns that. It does repeat `url`, deliberately — that is the identity, and an inventory missing four of its nine rows is not one. The `note` field is required exactly when there is no copy anywhere, so that fact is legible rather than excused. The check cannot be a gate and is not named like one. A published page's address is minted by the publish and never written back into the tree; the only record that a publish happened is the session log it happened in, and those live outside this repository on one laptop. CI cannot see them, so a gate here would pass by being unable to look — which is the failure mode gate:gates exists to catch. `pnpm artifacts` runs where the evidence is. The hook is the mechanism, not a nag on top of one. It fires the moment a page goes out, while the page, its subject and the reason for it are all still in hand; reminding an hour later is asking somebody to reconstruct. It is a shell wrapper rather than a bare node invocation because settings.json is checked in and a hook runs in whatever environment the editor has — where node is under nvm and nothing sourced it, the bare form reports 'command not found' against a publish that succeeded, and that teaches people to delete the hook. If there is genuinely no node it says nothing and leaves. It reports and never edits: a register this repo writes for itself is one nobody reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The board already answered what is open and what is leaning on what. It now also answers a third question a reader arrives with and had nowhere to ask: what has this project actually published, and can anybody still see it. Nine cards, read from the register the same way everything else on that page is read — nothing typed twice, so the shelf cannot drift from the inventory. Four of them are the drawings the decisions were made from and carry a link back to the question they were drawn for. Four have no copy anywhere and are drawn in terracotta, the same colour the open questions get, because they are the same kind of fact: something here needs a person and nothing will happen on its own. They sort first for the reason the open questions do — an inventory whose worst rows are at the bottom is one nobody scrolls to. The ninth is the board itself, and it says so rather than offering the reader a link to the file already open. That is a small wrongness, and small wrongness in a list is what makes somebody stop trusting the rest of it. Checked in both themes and at phone width; no horizontal overflow anywhere. Republished at the same address, so anything already pointing at it still lands on the current page. CLAUDE.md gains the sixth register and, beside it, the paragraph explaining why this one alone has no gate — the evidence that a publish happened does not exist inside this repository, so a check that ran in CI would pass by being unable to look. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The sweep counted a publish wherever the confirmation sentence appeared in a session log, which is not the same thing and was never going to be. Testing it put two invented confirmations into the log of the session doing the testing, and the next run reported them as two real pages nobody had registered — eleven published where nine had been. A listing of the account's pages would have done the same, twenty-four at a time. It now parses the logs instead of grepping them: collect the ids of the calls that were actually the publish tool, and read the confirmation only out of the results those calls returned. A shell command that prints the sentence, or a listing that pastes two dozen addresses, cannot forge that pairing. Back to nine published against nine registered. The reminder itself is unchanged — it reads the payload it is handed and never went near the logs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Deleting the typed table and redrawing the comparison from the page artwork changed where that feature lives, and the map did not know where it lived before either — the only mention was a note on the popover saying it showed "the diff against the current ayah", pointing at nothing. So the two files somebody would actually need to open now have rows: the arithmetic on word indices, and the panel that crops both pages through it. The note on the first one carries the reason the table went, because that is the part a later reader will otherwise undo: it is easy to look at a panel built from word boxes, find it fiddly, and think a small text table would be simpler. It would be, and it would reintroduce running scripture to a code repository, show the reader a plainer spelling than the page underneath, and cover twelve ayahs out of six thousand. A skill heading still asserted the unscoped claim — no Quran text enters this repo — three lines above quoting the scoped rule that replaced it. A heading that contradicts its own paragraph is the version people remember. Two checks the fix had promised and nobody had run: none of the deleted table's 36 distinct token strings is byte-present in the 337 KB of built JS, and adding an undeclared tree under the shipped assets folder does now fail the licence map by name, which is the behaviour that gate was extended for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Both plans this file has held are finished, and it was still written as though neither had started. A plan that claims outstanding work is worse than no plan: somebody reads it and does the work twice, or reads one stale claim and stops trusting the rest of the file. The displaced sitting plan was recovered and audited item by item against the tree rather than against its own account of itself. All eight fixes landed, the guard against the trap the page is built in landed, the registers landed, and the item it called owed and still unrecorded is recorded — saying considerably more than the plan expected, because reading that sitting turned up two further defects in the instrument. So it is not restored as a plan. What was worth keeping from it is the table of what each fix was for. What is left in both cases is reader work. 1,717 of 1,877 marks have not been sat, and two checks need a device and a person. The instrument being trustworthy was the point of that plan; the instrument existing never was. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Rebuilding the unsat sittings ends with "confirm the deal did not move — same number of parts, same total, and no mark that has been answered coming back round again". Sixteen files, by eye, at the end of an hour of somebody else's work. In practice: nobody. Both ways it goes wrong are invisible from inside a part. A rebuild skipped, or run with one hand-over left out of the list, leaves parts that still open and still count down and re-ask questions somebody already answered — a page you have seen looks exactly like a page you have not. A rebuild against a different measurement leaves every rectangle drawn from displacements that are not the ones on disk, so every answer is about a picture nobody can reconstruct. That one is worse: the answers are wrong rather than merely wasted. Neither needed a person. A built part already says what it was built from, because it has to — the reader's place is keyed on the measurements' fingerprint plus the set, slice and seed, with the answered set folded into the slice so shrinking the pool cannot strand somebody at card ninety of a hundred and seventeen. It announces itself to anything that asks. Nothing asked. audit-mark-sittings.mjs asks. It reads each part back and holds it to its own account of itself: one measurement across the deal and it is the one on disk, every part built against the same answers and those the answers on disk, nothing already answered asked again, no mark in two parts and none in nobody's, and the count the reader is shown describing the population it claims to. Eleven tests build each of those failures on purpose, because an auditor that only passes the clean case is indistinguishable from one that always says yes. --answered is required to be the same list the rebuild got, and deliberately not defaulted: an auditor that quietly reads the running log and nothing else calls a sitting stale whenever a hand-over exists, or fresh whenever the rebuild used a file it did not, and both verdicts are confident. The one reading of the word "answered" moved to lib/answered.mjs and is now shared with the builder rather than written twice — two readings drift, and the drift surfaces as a mark one drops and the other counts, with neither obviously wrong. Rebuilding part 1 after the extraction gave a byte-identical file. First run found nothing: 1,710 marks across 16 parts, 167 already answered, 1,877 in all, one measurement throughout. Worth stating because it was not knowable before, and "we assumed so" and "we checked" are different claims. Not a gate, and not named like one. The parts are build products and the answers accumulate on whichever machine served them, so in a clean checkout it would pass by being unable to look. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Entry ⑰ in the mark-registration record, indexed as fixed with the tests that close it. It says the part the code cannot: that both failures are invisible from inside a sitting, that neither needed a person because a built part already carries what it was built from, and that nothing had ever asked. The routine's last step now names the command and says to give it the same --answered list the rebuild got — grading a rebuild against a different set of answers than the rebuild used is confidently wrong in whichever direction the difference runs. Three map rows: the shared reading of "answered", the auditor, and its tests. First run: 1,710 marks across 16 parts, 167 already answered, 1,877 in all, one measurement throughout. The deal had not moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
…sked A fifth options page, and the first whose subject is the instrument rather than the app: where the checking sessions should live. Two modes, the split the skill asks for. --extract reads the sixteen built parts and the answer log out of the gitignored out directory and writes the findings; the default render reads only committed bytes, so the page rebuilds on a fresh clone. Verified: rendering twice from the same data gives a byte-identical file. The specimen is one real card lifted out of the first part, and it arrives carrying two hazards. It has two fields of Arabic, which are dropped at extract time and which the render then refuses outright — the page cannot contain an Arabic codepoint. And its paths are already coloured by two names this page uses as theme-flipping tokens, so a dark theme would have printed black on black. That is not hypothetical: it is the defect the sitting instrument was fixed for once already, reachable a second time through a name collision nobody would look for. Both names are pinned on the card itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The record, the page, the findings it renders from, and four register rows. The question is where a checking session should come from: the laptop that hands it out over the household's private network, or a link the reader can open anywhere — and if published, whether each answer is banked as it is given or held in the browser until the end. Three options, each drawn as the journey a single answer takes from the reader's finger to a line in the repository, with the steps that can fail marked. Two numbers decide most of it, and both are counted rather than remembered. 3.5 statements per mark is what a reader actually does — nudge, look, nudge again — so anything that banks one at a time does so three or four times a mark. And 1.3 MB is a whole sitting against a 16 MB ceiling, which is what makes publishing one a possibility at all rather than a technical question. The record says plainly that nobody looked at prior art, and names what would be worth looking up. It also says the thing an options page is usually written to avoid saying: nothing is blocked behind this, the status quo works, and if the remaining sixteen hours are going to happen at a desk at home anyway then the first option and no work at all is the honest answer. Related in both directions with mark placement, which is the question the sittings exist to answer and the one that would close this unmade. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Three things stood between a sitting and the phone it was meant to be sat on. Reaching it at all was an incantation nobody had written down. The server listened only to the machine it ran on unless a flag said otherwise, and no script, routine or document anywhere recorded how to start it — so every sitting on a phone depended on somebody's memory. It now works out where it is: a private network of this kind hands each machine an address in the range reserved for carrier-grade translation, and no ordinary network issues one, so an interface holding one is that network. It needs nothing installed and cannot be wrong about whether the network is up. It binds that network and not every interface, because there is a write endpoint on this thing and the network at a café is an interface too. The machine answers to two spellings of itself, and a browser keeps a reader's place per address compared as text, so the name and the number are two separate memories of the same sitting. That already cost a reader an hour. There is now one address, derived rather than typed, printed on start with the others named as the ones not to use. And pressing the same nudge twice magnified the page instead of nudging it, which on a phone is most of what nudging is. Declined for the whole page rather than per button, because two taps on adjacent buttons trigger it as readily as two on one; the stage's stricter rules still win, since a browser intersects this down the ancestor chain. The way in was rewritten around all of it. It carries each sitting's own marks, counts progress out of what this machine has heard, and points at the one to carry on with — and it ships the counting function's own source text rather than a paraphrase of it, with a test that evaluates the shipped copy in an empty scope and holds it to the module. Reading a built sitting back moved into one place the auditor and the way in now share, because a third reader of those two literals would eventually tolerate a shape the others reject and hand one caller sixteen parts and the other fifteen — invisible in both reports. 352 tests across 14 files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
The question was opened this morning: a laptop at home, or a link anyone can open anywhere. It is answered, and the answer is the laptop. Be honest on the page about what settled it, because it was not a fresh weighing of three routes. The one thing that option genuinely costs — the reader has to be somewhere that network reaches — was judged acceptable, and the two things that made it feel worse than it is turned out to be work nobody had done rather than properties of the arrangement. Both are now done. The other two options stay on the page unbeaten rather than beaten: neither was tried, the first was made good enough that neither had to be, and they are why the choice was a choice. The page also says plainly that nobody looked at what anybody outside this project does, which is a caveat on the decision and not a footnote to it. The register carries who chose and when. The way in, the one address and the double-tap are three closed items in the mark register and three rows in the issue index, each naming the test that would notice it coming back. The routine for reading a sitting back grew a sixth step, because everything in it happened on this machine and there was nothing telling anybody how to hand the result to a person holding a phone. The code map learned two new files and unlearned two symbols that the rewrite had deleted underneath it — a gap it cannot catch itself, since it only checks what is staged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Two sittings were finished and the page still showed them as untouched, beside fifteen that genuinely were. That is the whole complaint, and it is not a display bug: every number on that page except the progress bars was worked out on this machine at the moment the file was generated and then frozen into it. So the page could only be corrected by somebody remembering to regenerate it, and automating the regeneration would have moved the forgetting rather than ended it — a rebuild that nobody runs is the same page. The serving side could hand back a reader's answers and nothing else, which is precisely why the door had to be a build product: there was no way to ask what was on the disk. It can now, reading the directory on each request rather than holding a listing, because the parts get rebuilt while it is running and a server with its own idea of what is there would be the stale thing instead of the page. It reads each one through the same shared reading the builder and the auditor use, so the three cannot come to different counts. The arithmetic and the wording move into a module of their own and the builder ships those functions' own source text into the page, the way `standingIds` already worked. The page then redoes the entire census against the live listing — which sittings exist, how far each has got, every total, every sentence, the part to carry on with — rather than patching the baked numbers, so no version of the page is half of one census and half of another. The rule that makes this possible is that every function there is closed over nothing but the others, and a test re-evaluates all of them in an empty scope and makes them agree, because a module-level binding reads perfectly in Node and is undefined on a phone. Finished sittings fold away rather than disappear. A front door that silently drops rooms cannot be checked against the directory behind it. The baked half stays. Opened from a file with nothing serving it, the page still shows the numbers that were true when it was written. The half that only ever runs on a phone had nothing holding it, so the page's own script is now pulled out and run against a document small enough to read. Six cases: a sitting finished since the build folds, the totals come down, a part that has gone from the disk goes from the page, a re-deal under an open tab announces itself, parts from two different builds are refused rather than added up, and the last one finished says so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Two sittings sat, sixty marks each, in the two lowest bands of confidence. Both were settled and read; the ruling from the second is checked in beside the first. The finding they agree on is the one worth recording: every mark placed from its own ink was affirmed, and every mark the machine could not place from ink carries a complaint — not most, all of them. The check stays pending because 1,710 of 1,877 have still never been looked at, and the ledger says so rather than rounding two samples up to a verdict. Two items on the registration document and two rows in the issue index came out of the reading. The numbering script needed the fifteen circled digits after ⑳ to write them, which is a Unicode block boundary and not a decision anybody made — the comment now says which. The code map gains rows for the two new pieces of the front door and repoints the builder's row, which still described the page as counting once and painting progress over the top. It does not do that any more, and the note says why the obvious cheaper thing — a generated script restating the arithmetic — is the version that goes wrong in a month. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
A whole part of a hundred and six, all of them marks we could not place from ink, sat on the rebuilt deal. Every gradable one faulted, which is what a set selected for being unplaceable was always going to say and is not the finding. The finding is the split. The reader's hand moved a median of 1.479 page units and changed the size by 0.130 — on a box about 6.3 by 3.4, the rule that inherits from the printed line gets the size nearly right and the position badly wrong. Sorted by the name of the mark it comes apart three ways: the single marks, seventy-eight of the hundred and six, moved about a unit sideways with their size untouched; the marks that sit above the letter moved downward as well and two to three times as far, so the printed line says nothing about how high above a letter a non-vowel sits; and the ten doubled marks barely moved but were grown half a unit to one and a half in both directions, a box drawn for one of something that is two. The last of those needs no reader — extent is something the print can be asked about directly — and it is written down as its own question. Two things worth keeping beside it. The whole correction the answers imply is across -3.170 and down -2.255, against -3.3 and -2.6 from the sitting before: two independent draws from the same population agreeing on direction and size, which is what a real displacement looks like and what reader noise does not. And the both-words artefact turns out to be a property of how close the box already was rather than of the gesture — one in a hundred and six here against four in thirty in the bands, with two marks carrying one word without the other for the first time. So the word-level counts are least trustworthy exactly where the correction is already working, which is most of the book. The odd-in-the-print rate rose rather than fell as the page improved, fifteen in a hundred and six against fifteen in a hundred and sixty, so whatever a reader sees there is not something the page was doing to them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Two faults in how a rectangle is fitted to its own ink, one of which was hiding the other. The search sweeps a coarse lattice and then refines around whatever the coarse pass liked, and the refinement was never bounded by the region it was refining inside. A coarse winner sitting on the boundary let it step a quarter unit past — an answer whose surroundings had never been scored, so nothing could know it was the best one. The quarter unit is not the damage. The only way anything downstream can tell that a mark ran out of room is that its offset came back sitting exactly on the boundary, so every mark that slipped past stopped counting as a refusal: 2,252 of them, of which 1,923 were being shipped as successfully placed at a median match of 0.859 against the 0.909 a good match scores. The second is the one the reader's answers pointed at. The search looks three units and the marks it gives up on are a median 4.292 units away, 230 of 272 further out than three — it was being asked to find something it was forbidden to reach. Widening it for everything is not the fix: that moves 4.11% of the marks it already places by more than two units, onto the neighbouring mark's ink, confidently and unadjudicably. So it escalates instead. Search three as before; only where that refuses, search again at eight. What the ordinary search placed keeps its measured displacement byte for byte. The predicates that name a mark a refusal now live beside the search that produces the rows, so the rule that refuses one and the rule that rescues one cannot drift apart, and the report reads each row's own reach rather than one global number — without which an honest answer at three from an eight-unit search reads as a wall. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
A part of 106 hand-corrected marks sorted cleanly by the name of the mark: these moved sideways, those moved down twice as far, the doubled ones barely moved and wanted growing. Fitting the shipped correction over all 326,515 marks and measuring what it leaves kills two of the three in one command — every class within 0.09 units of zero on a box about 6.3 by 3.4, with 36,616 sukun against the twelve that had suggested otherwise. The pattern was the shape of the refusal criteria seen from the inside: a mark is refused for one of two unrelated reasons and those two are not evenly spread across the kinds of mark, so sorting by the name of the mark sorts mostly by which refusal it suffered. The wrong reasoning stays in the record above its own correction. It was reachable and it will be reached again. What survived is the size finding, which survived because it is not about position at all — a doubled mark is two of something and the box is drawn for one, and it shows up as a class refused at five times the base rate while matching worse even when accepted. It gets its own item, so that the refuted half and the confirmed half stop travelling together. The relocation is the useful part, and it found two defects in the rule that decides a rectangle cannot be placed at all rather than in what a rectangle knows about each kind of mark. Both are written up with the numbers, including the two things the fix does not guarantee: the correction is fitted from these displacements, so better inputs move the fit and therefore move every mark on that line by a little; and recomputing the corpus restamps it, which the scorer will refuse against rulings already given, though the answers themselves are stored against the mark and survive any re-deal. The iteration itself is now a skill, including the two lessons that cost the most to learn: a population defined by a sentinel value is only as trustworthy as the guarantee that produces the sentinel, and a claim that holds by construction is still a claim about code and wants a diff. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
At a desk the card is one gesture — tap the ink the rectangle should have been drawn around, which banks the answer where it is tapped — and then the slowest thing left is travelling to a button in the bottom corner to leave. So n does it. Only n, and only Next. The keys that would obviously pair with it each carry a risk this one does not: a mistyped letter that affirms a mark, or withdraws one, puts a wrong row in a transcript whose entire worth is that it can be trusted, with nothing on screen to show it happened. Arriving somewhere banks nothing and withdraws nothing, and Back is still there, so this is the one action a slip cannot cost anything. The test asserts that too — one keydown listener on the page, and no verdict reachable from it. Three things it gives way to: the note box, where n is a letter somebody is in the middle of typing; any focused control, including the step slider; and the browser's own shortcuts, so every modifier hands the key back. A held key is ignored rather than dealing the reader through nine cards. The hint sits on the button itself and only where there is a keyboard to press — a phone has no n to offer, and the dock is the one strip on the page that cannot spare a row to say so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
o says what the vaguest of the six odd-print reasons says: something here is wrong and I cannot name it. That answer costs two presses and a row that has to be opened before what it holds can be seen, and it is the one a reader reaches for while their eye is still on the page — so it is the one they skip. The note beside n said only Next would ever get a key, because a mistyped letter that records puts a wrong answer in a transcript whose whole worth is that it can be trusted. That was the wrong line, and it is left standing next to this one. What makes a binding dangerous is not that it records, it is that the card can leave while it records: affirming moves on, so a slip carries its own evidence off the screen. Pressing o moves nothing. The reasons row opens, two buttons light, a line appears with "take it back" beside it, and the same letter withdraws it. A slip announces itself and costs one more press. The letter lives on the reason itself rather than in the handler, so the chip prints whatever key its own row carries and the handler presses whatever button that row built — they cannot drift apart. It goes through the button rather than through the toggle underneath, so a key can never take a path pressing the button does not, including whatever the button grows later. The key hint is no longer scoped to the button strip, or the chip's own key would not have shown. Three more tests, and the one that said no key records anything now says the narrower true thing: no key both records and moves. 416 passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
…ting A reader sat the first wrong-size part off the recomputed corpus: ninety cards, sixty-one answered, sixty of them reshaped. Reading it back against the book answered one open item, corrected its heading, opened another, and killed two theories the sitting itself had suggested. The doubled marks are the population, and more so than before: 8,554 of 326,515 in the book but 160 of the 329 refused for matching badly, nineteen times over-represented, at rates of 3.15% for successive kasratan against 0.05% for fatha and 0.00% for sukun. The item's own heading is wrong and is left standing. "Drawn too small" was a prediction from the theory's shape; of the thirty-three doubled marks the reader made fourteen wider and nineteen narrower, a median width change of -0.13 against a median magnitude of 0.57. The error is four times its own average, which is another way of saying there is no average, so the arithmetic repair the item hoped for does not exist and extent has to be measured per mark. What nobody had asked is where in the rectangle the fault sits. Edge by edge, a doubled mark's near edge moves 0.209 and its far edge 0.549; a single mark's two side edges move 0.672 and 0.586 together. A single box slides whole, a doubled box runs to the wrong place. Scored against the rectangles the reader settled on, doubled marks want their size fixed and their position left alone (0.868 against 0.788 shipped) and single marks want the exact reverse (0.744 against 0.623). A rule treating the refused set as one thing spends its effort on the wrong half either way. Two theories the corpus killed, both named because both were reachable: that hamza is drawn from a constant size, and one level down, that some particular size is the one that fails. Both were the selected population describing its own selection. And an instrument defect that nearly changed the reading. The sitting reports eighty-five presses of "wrong shape" across sixty marks, which as opinions is overwhelming — but fifty-seven of those sixty also carry a tap on the ink, and a tap resizes the rectangle, which is recorded as the shape having been called wrong. Three are judgements of their own. The rectangles are exactly as good as they were; it is the tally that overstates, roughly twenty to one. Eleven more marks were called odd, and unlike every batch before them they are not spread across the vocabulary: ten of eleven are single-piece marks in a sitting that was half doubled. Either single marks carry more of it, or "odd" is the button left when a reader has no word — and a doubled mark always gives them one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
…nst the whole book Fifty-seven of the sixty-one marks in the wrong-size-part1 sitting were placed by the reader tapping the ink itself, which put their rectangles close to a simpler rule than anything the-doubled-marks-are-drawn-too-small scored: take the connected pieces of printed ink whose centre falls inside the shipped rectangle, and draw their union. Checked against what the reader chose: reproduces the reader's own tapped set on 96% of the 57 (94% doubled, 100% single), and scores 0.847/0.769 overlap against the reader's rectangle on doubled/single marks, both above what ships (0.788/ 0.623) and above every resized-guess candidate tried before. Checked against marks nobody is arguing about: run over 31,805 marks on 60 pages (about a tenth of the corpus), the rule's answer sits a median 0.077 units from what ships for the 31,773 already-accepted marks, 99.2% within half a unit. The 791 accepted doubled marks among them barely change size (width -0.075, height -0.088) against the several-unit corrections the refused doubled marks needed — answering what the earlier item left owed: the accepted doubled marks are not secretly wrong too. Not a shipped fix yet. This is a sample-scored candidate, not an escalation diffed byte-for-byte against production the way the two search fixes were. Tracked as reach-for-the-ink-rather-than-resize-toward-it (risk) and mark-H/E on the task board. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
… today The candidate from item ㉘ was scored on a sample. Ran it as an actual escalation instead: fires only where the current rule already refuses (iouBest < 0.55), over all 329 marks the corpus refuses today, not a tenth of the book. The accepted 326,186 are unchanged by construction now, not by having been diffed and found unchanged. Of the 329: 304 get an answer from the rule, 25 have no ink piece under them at all (11 of 71 hamza) and would keep today's rectangle. Matched mark-for-mark against the 33 doubled marks with reader ground truth, typical agreement is close (median combined size error 0.064 units), but one mark shows the rule sweeping in a piece of ink the reader never meant to include, proposing growth of several mark-widths where the reader barely touched the size. Four more disagree by two to four units. Not shippable yet: the rule needs a guard against a piece union that has grown implausibly large, the same shape of fallback it already has for finding no ink at all. Recorded as ㉙ in docs/design/mark-registration.md and folded into reach-for-the-ink-rather-than-resize-toward-it in docs/issues.json; task board updated to name the guard and the re-deal size (329 of 326,515 marks today, not the 1,877 an older estimate assumed — that count was stale, from before the search-radius escalation already recovered most of the queue). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
…a one-off The sitting behind ㉖/㉙ was two thirds sat when ㉙ was written. Finished it today, same report continued rather than re-dealt: ninety of ninety marks, up from sixty-one. 57 answers had already been banked live and never made it into a hand-over; settled with the running log alongside the transcript so nothing sat was lost. The doubled-mark ground truth for "reach for the ink" grew from 33 to 45. The worst case is unchanged — 274:59, the rule proposing growth of (4.79, 12.35) against a reader who barely touched the size — nothing newly sat disagreed worse. But the rate ㉙ measured undersold it: 17 of 45 doubled marks (38%) now disagree by two units or more, not 5 of 33 (15%). Singles stayed solid, 2 of 44. Recorded as ㉚ in docs/design/mark-registration.md, ㉙'s number left standing beside the correction rather than overwritten, and folded into reach-for-the-ink-rather-than-resize-toward-it in docs/issues.json. What this changes: the size-sanity guard was already the blocker before this could ship. It is no longer optional caution against a tail case — at over a third of doubled marks, the rule cannot ship without it. Four more marks were called odd in the print rather than in our rectangle, folded into the running note on item ⑭ (47 in all) — a successive dammatan, a hamza, a small yeh and a successive fathatan. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
…ally right The fix for "the search reported answers it never checked" closed a code bug: a mark whose search ran out of room could slip past the check and ship as placed from its own ink. It never asked the question it made askable — of the marks that still run out of room and still clear the match floor today, are the rectangles correct? Nothing had looked. That population is 339 marks. Two sittings landed for it, forty marks each, drawn the same way as the doubled- mark and reach-for-the-ink sittings: one finished 2026-08-17, one today. Settled together, 80 of 339 seen. Every one of the eighty carries a complaint, and most of it is real rather than the tap-sets-both-words artefact named in "counting presses stopped meaning anything" — 79 of 80 moved by a real amount, 73 of 80 were genuinely resized. But it is a small kind of wrong. Median hand distance 0.676 units, worst 3.231; median size change 0.07 to 0.09 units either way — far short of the fallback population's multi-unit corrections. Nobody called a page odd in the print. Recorded as ㉛ in docs/design/mark-registration.md and as its own row in docs/issues.json, since it is a different population from the doubled-mark and piece-union findings rather than a continuation of either. 259 of 339 remain unsat; whether the rate and size hold across the rest is what would answer it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Asked whether the design doc links to concrete examples anywhere; it did not — every item, ㉛ included, cites mark ids in backticks and nothing a reader could open. Looked for what could honestly be linked: the evidence page the doc itself describes (worst verdicts first, drawn at real size, addressable per mark) turns out to already be a real, working script rather than unbuilt tooling, but its output is deliberately never checked in or published, because it draws the mus'haf's own ink and this repo commits no scripture. There is nothing real to link a reader to a picture of. What is real and checked in is the ruling itself: the eighty settled marks behind ㉛, box we ship beside box the reader chose, in numbers with no ink and no scripture in them. ㉛ now links it directly, and says plainly that this is numbers, not a picture, and why. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
Auditing the doc for the same gap just closed in ㉛ found it was not one item's omission. ㉑, ㉒, ㉓, ㉖, ㉗, ㉘, ㉙, ㉚ and §⑦ each cite a specific sitting's numbers — how many marks, how far moved, how often a class disagreed — with a real, checked-in ruling behind every one of them, and none of the nine ever linked it. Checked, not assumed: read each ruling file's own sitting record (dates, counts, population) against what each item claims before linking it, rather than trusting filenames. One case needed care rather than a blind link — ㉖ through ㉙ were written against a sixty-one-of-ninety mid-sitting count that ㉚ later grew to ninety of ninety, and the file on disk only holds the finished state, so each of those four says as much rather than pointing silently at numbers that no longer match the prose beside them. Ran only against docs/design/mark-registration.md — audited every other design doc too, and none of them cite a sitting or a ruling at all, so the gap does not reach past this one file. gate:scripture stays clean; nothing added is ink or scripture, only paths to files that already carry none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The branch started with one complaint — unclear which symbol on which letter is
right — and the work fanned out from there into four threads. They are separable
on reading but not in history, because each one kept turning up the next.
Which mark is it, and is the rectangle around the right ink?
The original question was two questions wearing one name, and separating them was
most of the fix: a rectangle can sit in the wrong place, or it can sit in the
right place carrying the wrong label. They have different causes and different
evidence, and conflating them is why the earlier rounds went in circles.
Placements are now measured against ink rather than argued about. A resting
finger reports the ink it is actually on; a placement with nothing under it is a
well-formed answer rather than a failure; and the correction the sixty hand
placements produced is applied and re-scored on every run, so the shift file says
how much of the mus'haf it speaks for instead of implying all of it.
Somebody has to look, so the looking got an instrument
A sitting asks about one mark at a time, on the real page, at the size it is used.
It counts what is left honestly across a hand-over, saves each answer as it is
given rather than at the end, and lets a reader draw a box around the ink they
mean instead of nudging one into place. Answers come home and are read back by a
skill, so a sitting produces a change to the approach rather than a pile of
opinions.
What did we decide, and can anyone still see the page we decided it on?
The decision register grew a rendered board that draws the lines between
decisions. Then a count found that nine pages had been published and the tree
knew about five — the other four had been drawn in a scratch directory that was
later cleared, so a diagnosis, a comparison carrying a recommendation, a plan and
a finding now exist only as links on a host we do not own. That is the failure the
decision gate already refuses, reaching pages nobody had thought to attach to a
decision.
There is now a sixth register for published pages, a check that runs where the
evidence lives (session logs, outside the repo — so it cannot be a gate and is not
named like one), and a hook that says what is missing at the moment of publishing,
while the page and the reason for it are both still in hand.
The most-repeated claim in the repo was false
Twenty-two times across twenty files this project asserted some version of there
is no Quran text here, and two files held running scripture — one of which
shipped. Both are gone, the claim is now scoped to what is vendored and shipped,
and a gate refuses the next one. Alongside it, the licensing map stopped being
blind to two shipped trees, and a false "ours" declaration was corrected.
Checking
make cigreen. Gates, unit tests and the build-test mirror all pass; the bundleis 118.0 KB gz against a 150 KB budget. The reader-facing pages were checked by
eye in both themes and at phone width.
🤖 Generated with Claude Code
https://claude.ai/code/session_01EuhvbUKjGesE3uMhjCzBGt