Skip to content

Which mark, where, and who says so - #88

Open
omars-lab wants to merge 66 commits into
mainfrom
mark-nudge-identify-the-mark
Open

Which mark, where, and who says so#88
omars-lab wants to merge 66 commits into
mainfrom
mark-nudge-identify-the-mark

Conversation

@omars-lab

Copy link
Copy Markdown
Owner

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 ci green. Gates, unit tests and the build-test mirror all pass; the bundle
is 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

omars-lab and others added 30 commits August 12, 2026 13:19
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
omars-lab and others added 30 commits August 16, 2026 20:04
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant