Measure where the counters get lost, from plate ink to stored row - #551
Merged
Conversation
The author asked the right question about the `sep05` ink-clearance negative: if the plate's counters are open at the plate nib, then following the ink at the same width should keep them open — so do our lines pull toward the middle? They do, and this measures by how much and where. One quantity carries the whole chain: D0, the inscribed diameter of the region a closed CENTERLINE loop encloses. A Gleichzug pen of half width h paints the capsule union, whose counter is that region eroded by h, so the visible aperture is exactly D0 - 2h — validated against a directly rasterised union to <= 0.0013 xh before any number was read. That makes the plate's ink, its skeleton, the stored Kette v5 fit and the composed row comparable in one number. For each of the nine loop-losing glyphs and every occurrence of it in the 63 frozen word specimens: * the plate's counters are open by 0.096-0.188 xh at a measured pen half width of 0.0968 (the 0.097 confirmed independently); * the composed letter closes the same counters by 0.004-0.019 xh; * the composed centerline loop is 0.10-0.20 xh narrower in diameter than the plate's implies (median 0.135) — about 0.07 xh per side. The loss happens twice, not once. Roughly half is already in the medial axis (0.035-0.104 xh): where two pen passes merge around a tight counter, `skeletonize` returns the axis of the merged blob rather than the two paths, so the target the Lotse rides and the Kette measures against is itself too narrow. The rest (0.004-0.126 xh) appears between the occurrences and the stored row, because an elementwise median over loops that are not congruent contracts. The Kette itself sits 0.0116 xh off the skeleton, and the composer loses nothing at all (<= 0.0008 xh against the row) — so H1 as posed does not hold, H2 and H3 do. Report only: no arm, no adoption, no row written, no core/ byte moved. Four mechanism-level candidates come out of it, each with a one-line pre-registration sketch and none of them built here — an aperture gate in the Laufform estimator, a loop-faithful median, loop geometry from the evidence, and a merge indicator as a frozen report column before any opening term enters a fit loss. Explicitly not a path: thinning the pen until the holes reopen. Measured on a fresh sep05 root (own export, digest f30b700a2f3d / 135b15ce909f; a re-export can never reproduce a digest because the manifest timestamp is hashed). The proof it is the sep05 base is the headline: wordbench --set all reproduces 0.108444 / 0.148236 digit for digit, BLAS pinned to one thread. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UEScQMZFvxxNNyNJYryfa3
There was a problem hiding this comment.
🟡 Changes recommended
Key causal conclusions rely on unmatched estimator inputs and a signed median presented as absolute fit error.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Documents a measurement of where glyph counters narrow between plate ink and stored/composed geometry.
Changes:
- Introduces the
D0aperture metric and diagnostic results. - Records findings, terminology, and proposed rescue paths.
- Adds index and changelog entries.
File summaries
| File | Description |
|---|---|
docs/notes/kringel-binnenflaechen-2026-09-06.md |
Adds the diagnostic journal. |
docs/reference/messjournal.md |
Summarizes the diagnosis. |
docs/proposals/tintenfolger.md |
Adds four rescue candidates. |
docs/reference/glossar.md |
Defines the new metrics. |
docs/index.md |
Indexes the journal. |
changelog.d/kringel-counters-diagnosis.md |
Records the documentation addition. |
Review details
Suppressed comments (4)
docs/notes/kringel-binnenflaechen-2026-09-06.md:202
−0.0116is a median of signed differences, not an absolute distance from the skeleton; inward and outward errors can cancel (the per-glyph medians above already span roughly −0.067 to +0.052 xh). It supports “no consistent inward bias,” but not “practically on the skeleton” or the four-fifths attribution. Report an absolute-error statistic/quantiles and compute the inherited share per matched loop, or narrow these conclusions.
Über alle 183 zugeordneten Schleifen liegt die Kette **0,0116 xh** unter dem
Skelett (Median), und in 61 % der Fälle darunter — an `a` (+0,0083) und `sz`
(+0,0358) sogar systematisch DARÜBER. Ein Prior-Zug nach innen in der
Größenordnung des Problems ist das nicht. Gegen die Feder-Rekonstruktion liegt
dieselbe Kette bei **−0,0661 xh** und in 87 % darunter: **das Defizit der
Kette ist zu rund vier Fünfteln geerbt, nicht erzeugt.**
docs/notes/kringel-binnenflaechen-2026-09-06.md:220
- These data do not isolate H2.
v,k, andGhave no Laufform row and belong to H3, so the Laufform subset tops out at 0.0824 xh rather than 0.126; more importantly, lines 442–447 acknowledge that the measured word fits are not the occurrence stack that produced the rows, and LF11 uses a spline-basis median rather than a plain median followed by smoothing. This establishes that stored rows are narrower than this comparison set, not that the estimator caused the contraction. MeasureD0on the exact input stack, its per-anchor median, and its spline-basis median, or mark H2 as unconfirmed.
### H2 — „der Per-Anker-Median der Laufform plus Glättung zieht Schleifen zusammen": **bestätigt, in Größenordnung 0,00–0,13 xh**
Zeile gegen den Median ihrer eigenen Vorkommen (Kette-Schleifen derselben
docs/notes/kringel-binnenflaechen-2026-09-06.md:406
- The stated
+0.10 to +0.15 xhrequirement contradicts this document’s ownktotal of 0.1969 xh (line 186) and its repeated 0.10–0.20-xh headline. This understates the upper end of the redrawing requirement; use the measured 0.10–0.20 range and express the corresponding per-side displacement consistently.
Der bereits stehende Autorenweg des `sep05`-Eintrags — die acht Formen
nachzeichnen bzw. den Eigenhand-Bestand als zweite Quelle nehmen — bleibt
unberührt und ist von R1–R4 unabhängig. Dieses Blatt beziffert nur, wie weit
er gehen müsste: **+0,10 bis +0,15 xh Öffnungsweite je Schleife**, also rund
eine halbe Strichbreite je Seite.
changelog.d/kringel-counters-diagnosis.md:15
- The changelog turns two unisolated interpretations into release-note facts: the estimator comparison mixes chart-only glyphs and a different fit population, while 0.0116 is a signed median rather than absolute fit-to-skeleton error. Revise this summary after the source note reports like-for-like estimator inputs and an absolute discrepancy statistic.
than the two paths), the rest between the occurrences and the stored row
(an elementwise median over loops that are not congruent contracts). The
Kette itself sits 0.0116 xh off the skeleton and the composer loses nothing
at all — so the two places worth an arm are the estimator and the evidence,
- Files reviewed: 6/6 changed files
- Comments generated: 6
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…t shows Three findings from the review, all of them right, all of them about the inference rather than the numbers: * The Kette-vs-skeleton figure was a SIGNED median (-0.0116 xh) presented as a fit-to-skeleton distance. It is not one: |Kette - skeleton| has a median of 0.0329 xh (p25 0.0132, p75 0.0642, p90 0.0976). The signed number supports "no systematic inward pull", not "practically on the axis" — both now stand side by side, and the limits section says which question each one answers. * The "four fifths inherited" share came from comparing two aggregates. Recomputed per matched loop as (D0_pen - D0_skel) / (D0_pen - D0_kette) over the 154 loops with a deficit above 0.02 xh: median 0.75, p25 0.50, p75 1.03. Three quarters, per loop, with a stated spread — the aggregate ratio is gone. * H2 was booked as confirmed on a set that is not H2. `v`, `k` and `G` have no Laufform row, so they belong to H3 and their -0.126 xh does not bound the estimator; the pure H2 set is 0.004-0.082 xh. And the comparison set is the whole-word Kette fits, not the occurrence stack the rows came from, while the row estimator has been the spline-BASIS median since LF11 rather than a per-anchor median plus smoothing. H2 is therefore booked as confirmed in DIRECTION and not isolated in amount, with the experiment that would isolate it written into R2: D0 on the exact input stack, on its per-anchor median and on its spline-basis median. Also fixes a stale range the review caught — the author-redrawing paragraph still asked for +0.10 to +0.15 xh where the document's own totals reach 0.1969 (`k`); it now quotes the measured 0.10-0.20 with the median 0.135 and the per-side figure consistent with it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UEScQMZFvxxNNyNJYryfa3
Round two of the review, four more places where a summary claimed more than the measurement: * The protocol's 0.02 xh floor on centerline loops belongs to the OCCURRENCE matching pass alone — a loop below it cannot be matched to a plate counter in any meaningful way. The row inventory reports every loop with its width, which is where the `p` finding (a 0.0095 xh shaft loop) comes from; the protocol now says so, and warns that R1's aperture gate must not inherit the floor, or it would filter away exactly the collapse it is meant to see. * The changelog said the note follows all nine glyphs end to end. For `p` the shared counter is the large loop; the collapsed shaft loop is a separate chart-versus-Laufform reading off the stored rows and has no matched plate counter. Said plainly instead. * The index row said „drei Hypothesen entschieden" — two are, H2 only in direction. * The glossary entry for the proposed merge indicator presented 1.0 as proof of no merging. It is the capsule MODEL's expectation (both inscribed circles concentric, stroke width locally constant); real geometry scatters around it, so the entry now says the indicator must be calibrated on non-merging places before it is used. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UEScQMZFvxxNNyNJYryfa3
MarkusNeusinger
added a commit
that referenced
this pull request
Sep 6, 2026
…ingel The ring closure encloses a region the full p row does not have as a hole, so the sheet prints 0.1748 where #551 measured 0.0095. No gate depends on it -- the p is not re-derived -- but the two numbers stand next to each other in the artefacts, and the limit belongs beside them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UEScQMZFvxxNNyNJYryfa3
MarkusNeusinger
added a commit
that referenced
this pull request
Sep 6, 2026
…oops (#552) One pre-registered measurement arm on the question #551 left open: the plate holds its counters open, our composed letters close them, and #551 located the loss in three places without being able to size the middle one. This arm sizes it, builds the mechanism the sizing supports, measures it against five gates — and books an honest negative with a finding that redirects the work. **Nothing was written.** No DB write, no root re-export, no ruler touched. The frozen `sep05` roots (`eaa195aa7c84` / `0fbde2d72b64`) were used as they stand and reproduce their headline digit for digit (0.108444 · 0.148236) with `--expect-root` and BLAS pinned. ## The attribution R2 owed (§14 „Laufform LF13 `sep06` — gemessen") #551 could measure H2 only in direction, because its comparison set was the chain WORD fits rather than the stack a row was derived from. That gap is now closed: the LF12 harvest re-derives all 18 stored rows byte-identically (largest movement 0.000000000 xh), so it IS the row's input. Against its own stack the estimator is small, and the step ahead of it is not: | key | n | chain (#551) | occurrences | stored row | chain→harvest | harvest→row | |---|---|---|---|---|---|---| | `a` | 13 | 0.2236 | 0.1909 | 0.1858 | **−0.0327** | −0.0051 | | `o` | 4 | 0.1826 | 0.1654 | 0.1767 | **−0.0172** | **+0.0113** | | `sz` | 5 | 0.2705 | 0.2167 | 0.1876 | **−0.0538** | −0.0291 | | `g` | 4 | 0.2377 | 0.1809 | 0.1743 | **−0.0568** | −0.0066 | | `r` | 18 | 0.2332 | 0.2177 | 0.2091 | **−0.0155** | −0.0087 | The harvest — dissecting a chain word fit into a centered, 120-anchor, chart-parameterised occurrence — costs three to four times what the median after it costs. Splitting the median's own share (window 0.25 xh, the four ranges losing more than 0.005 xh): place −22 %, size −1 %, basis +21 %, shape spread +53 %. **And the ceiling is one letter.** A counter is open at half width 0.097 when `D0 > 0.194`. The occurrences themselves carry `a` 0.1909, `o` 0.1654, `g` 0.1809 — already shut. Only `sz` (0.2167) and `r` (0.2177) clear it. No estimator-side mechanism, an aperture floor at the occurrence median included, can do better than that, and this was not known before the round. ## The arm `core/aggregate.py` gains `loop_ranges` (where the chart row closes a loop — read off the drawn spline, mapped back by arc length, because at a crossing the two strands are spatially adjacent and proximity picks the wrong strand) and `align_loops` / `loop_faithful_median`, which bring every occurrence's loops onto the stack's median place and size before the median is taken. Neither part can invent aperture: the median shift is zero and the median factor one by construction, so the row keeps its place and can never be wider than the evidence under it. Behind `LAUFFORM_LOOP_WINDOW`, default 0 — off, and off is byte-identical. `tools/laufform/smoothrow.py --loop-window` builds the cards. ## The gates (rung 0.15 xh; the ladder {0.15, 0.25, 0.50} behaves alike) | Gate | Result | | |---|---|---| | (a) aperture ≥ occurrence median − 0.01 xh | `h[68:108]` 0.5497 < 0.5617 · `z[26:46]` 0.2022 < 0.2064 | **red** | | (b) movement ≤ 0.03 xh | max 0.0268 | green | | (c) ruler not worse | `bench_loss` 0.108444 → **0.108637** · `pair_loss` 0.148236 → 0.148239 | **red** | | (d) chain identity gate | `--candidate authored` on the candidate root: **PASS** (dtw 0, chamfer 0/0) | green | | (e) golden `pen=None` | byte-identical | green | Gate (a) is red with a direction worth naming: the BASE row set violates four ranges, the candidate two, and no range flips from green to red. Repaired are exactly the two the arm targets (`sz` 0.1876 → 0.2074, `z[50:116]` 0.7898 → 0.8084). That is not the same as "nothing gets tighter", and the difference belongs here: six of the sixteen ranges do narrow without breaking their floor — `h[8:49]` −0.0067, `a` −0.0048, `w` −0.0031, `g[72:112]` −0.0028, `g[39:52]` −0.0014, `o` −0.0011 — and the `a` lands 0.0002 xh above its floor, inside the measurement's own resolution. Where the loss is shape rather than place, the registration moves area instead of winning it. That is the argument for rescue path 3. **Headline: 26 → 23** of the 63 word specimens still close a counter the plate holds open — `muß`, `muß-2`, `muß-3` open, all through `sz`, exactly the one glyph the attribution predicted. `gleichzug_doublings` at half width 0.097 (root copy with `constant_nib_units` 0.097): 22 → 21; at the delivered nib unchanged 14 → 14. **Verdict: rejected at (a) and (c).** The switch ships off. The card lies dry in the session scratchpad as a record, not a recommendation. ## R4, built and calibrated The merge indicator `(D0_skeleton − plate) / (2·w_pen)` over the 202 measured counters: median 0.689. Its null probe is a curve rather than a threshold — the wider the counter, the less room two strokes had to fuse — and it climbs monotonically across six bands: 0.609 → 0.646 → 0.646 → 0.733 → 0.795 → 0.878, the `S` alone at 0.970, where the medial axis IS the pen path. It stays report-only; no aperture term reaches a fit loss before the sensor is frozen. ## Rescue paths (§7.9 updated in the same PR) 1. **The harvest, not the median** — the measured lever, and new. 2. **R3** (loop geometry from the evidence) is now the only path that can return the full 0.10–0.20 xh. 3. **The `sz` win as its own narrow arm** — register only where the attribution carries it, instead of charging the ruler across all rows. 4. The author path is unchanged. Explicitly no path: building the aperture floor after all (its ceiling is measured and is one letter), or thinning the nib until the holes reopen. ## Verification `/verify-core`: 2465 passed, 8 skipped; `ruff check` and `ruff format --check` clean; the §2 smoke harness gives 120 anchors, slant 90.0, mean half-width 0.08. Seven new tests pin the mechanism, including that window 0 is byte-identical and that the registration never exceeds the widest occurrence under it. Docs gates (`docs_register`, `docs_budget`) and the changelog fragment gate pass; the journal's widest-entry budget is untouched at 4500 of 4503. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01UEScQMZFvxxNNyNJYryfa3 --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
MarkusNeusinger
added a commit
that referenced
this pull request
Sep 6, 2026
LF13 closed with a rescue path it called the campaign's measured lever: the HARVEST — turning a Kette word fit into a stored occurrence — was read as costing 0.016–0.057 xh of counter aperture, more than the running-form estimator behind it. This round decomposes that step, and the decomposition strikes its own premise. **Nothing was written.** No DB write, no root re-export, no ruler touched. The frozen `sep05` roots (`eaa195aa7c84` / `0fbde2d72b64`) were used as they stand and reproduce their headline digit for digit (0.108444 · 0.148236) with `--expect-root` and BLAS pinned — as do both of this arm's own reference numbers (26 of 63 closing words with LF13's per-glyph breakdown; `bench_loss` 0.101254 and 22 doublings for the stored rows at half width 0.097). ## Part 1 — the harvest does not lose the counter Read on ONE chart-anchored range at both ends, the step from the chain solver's own curve to the stored occurrence is a median of **−0.0002 to +0.0077 xh** over the five counter keys. **40 of 44 occurrences move by less than 0.01 xh, and of the other four exactly one LOSES ground** (`g`/Sprünge −0.0883, which is Part 2); the other three gain. The `o` gains at the render-time fluent widening. Centering and rounding move a median 0.0000 with a largest single deviation of 0.0008 and 0.0005 xh — the raster's own floor, not a movement of the geometry, so "by construction" stands here as a measurement with its floor beside it. The frozen sampling operator, which the pre-registration named as the chief suspect, carries 0.0000 to 0.0080. | key | n | E0 chain | E0→E1 | E1→Ew | Ew→E2 | E2→E3 | E3→E5 | **E0→E5** | |---|---|---|---|---|---|---|---|---| | `a` | 13 | 0.1951 | −0.0018 | +0.0000 | +0.0014 | +0.0000 | +0.0000 | **−0.0002** | | `o` | 4 | 0.1695 | −0.0029 | **+0.0120** | −0.0007 | +0.0000 | +0.0000 | **+0.0077** | | `sz` | 5 | 0.2165 | −0.0027 | +0.0000 | +0.0025 | +0.0000 | +0.0000 | **−0.0001** | | `g` | 4 | 0.2198 | −0.0130 | +0.0000 | +0.0203 | −0.0002 | +0.0000 | **+0.0004** | | `r` | 18 | 0.2166 | −0.0001 | +0.0000 | +0.0003 | +0.0000 | +0.0000 | **+0.0004** | Two acceptance checks stand in front of those numbers. The chain curve is read off `seg.polyline_px` rather than rebuilt — a letter-local rebuild lands on a different sample allocation (measured: up to 2.5 xh on a two-stroke `sz`) because the run allocates over the whole chain; the px→units map is `to_pixels` inverted, its constant read off the first anchor, and the residual at the last anchor is **2.3·10⁻¹⁵ xh** over 44 letters. And every reading is taken twice, with this arm's range ruler and with #551's raster-proximity ruler: **difference 0.0000 at both ends, all five keys**. **So where LF13's number came from.** Joined occurrence by occurrence, this round's occurrence reading reproduces LF13's to ±0.0002 — same ruler. Its `kette` column does not: **21 of 39 agree to 0.0011, 18 disagree by a median 0.0673**, and there LF13's value is the larger (`Wer` `r` 0.3500 against 0.2323, `scharfen` `r` 0.3338 against 0.2164). An `r` counter of 0.35 xh where the chart form draws 0.1805 is not the `r`'s counter — the 0.45 xh proximity window matched a neighbour's loop. The letter read WITH its connectors, which is the curve #551 read, shows exactly one loop in that window and it is the letter's own. ## Part 2 — the one step that really does close a counter Not the median, not the basis: the **stranded-anchor repair**. Over all keys, of 235 accepted occurrences 20 are repaired and 12 of those carry a measurable counter — and **all four whose repair lands inside a loop range lose aperture**: median 0.0410 xh, worst 0.1068 (`g`/Sprünge, then `a`/scharfen −0.0678, `o`/Galoppieren −0.0142, `Z`/Zorn −0.0045). The **eight repaired outside a loop move it by at most 0.0021** (median −0.0002) — a clean null probe. The remaining eight sit on glyphs whose row carries no measurable loop at all. The cause is the detector's own description. "An anchor both of whose steps are three times its stroke's median step" also fits the apex a tight counter is made of; chording it to its neighbours draws the chord through the hole. New glossary term: **Schleifen-Sehne**. The arm is the narrowest honest change to that: `LOOP_AWARE_REPAIR` (default off) plus a `loop_ranges` argument carrying the CHART row's ranges, passed in so `tools/pairlab/anchors.py` keeps importing nothing project-side. Only the occurrence repair reads it. ## The gates | Gate | Target | Measured | | |---|---|---|---| | **(a) harvest fidelity** | every occurrence `≥ E0 − 0.01 xh` | base **1 of 44** (`g`/Sprünge −0.0883) → arm **0 of 44** | **green** | | **(b) movement** | ≤ 0.03 xh | **0.0486** against the control card (`Z`), 0.0372 against the stored row | **red** | | **(c) ruler** | ≤ 0.108444 · ≤ 0.148236 | 0.107322 · **0.148352** | **red** | | **(d) Kette identity** | PASS | PASS on the candidate root (dtw 0, chamfer 0/0) | green | | **(e) golden** | byte-identical | byte-identical | green | **The control card carries both red gates, and that distinction is the point.** A fresh harvest of the same root with the switch OFF reads 0.107366 · 0.148352, so the arm moves the word ruler by −0.000044 and the pair ruler by 0.000000. What fails gate (c) is the fresh derivation. Gate (b) does belong to the arm: at `Z` it keeps a real excursion, and the counter gains 0.0045 xh for that 0.0486 — the pre-registration's exception ("except where the loop's opening itself is the movement") does not cover it. **Headline: 26 → 26.** Three occurrences reopen (`a`/scharfen +0.0678, `g`/Sprünge +0.1060, `o`/Galoppieren +0.0142) but no word does: one repaired fit in a stack of four does not pull a median over 0.194 (`g` row 0.1743 → 0.1823, `o` 0.1767 → 0.1865). `gleichzug_doublings` at 0.097: stored rows 22, control card 25, arm 25. ## The side finding, which is larger than the arm The control card was supposed to reproduce the stored rows. It does not: a fresh harvest of the `sep05` root re-derives every row **0.0025–0.0283 xh** away from the one that root was written from. It is not nondeterminism — `--jobs 1` and `--jobs 4` are byte-identical — but a feedback loop, and the code names it: `derive_word` composes the word FROM the running forms, and `chain_seed="composed"` starts the chain solve on that composition. The harvest reads the rows it is about to replace. LF13's byte-exact re-derivation holds for the stack from BEFORE the write and is untouched; it does not hold for a harvest AFTER one. `werkzeuge.md` now carries the warning next to the tool. ## Verdict **Rejected at (b) and (c)**, with gate (a) green and an attributable partial success at occurrence level. The switch ships off. The dry write card lives in the session scratchpad — deliberately **five** rows, only those the switch itself moves, so the fixed-point drift is not written under this arm's name. It is a record, not a recommendation. The yield is the deletion: the harvest step is not a lever, which leaves **R3** (loop geometry from the evidence) as the only path that can return the 0.10–0.20 xh, and adds the harvest's fixed point as a question that should be answered before the next running-form write. Both are registered in `tintenfolger.md` §7.9, and LF13's rescue path 1 is marked walked and struck. ## Verification `/verify-core`: 2472 passed, 8 skipped; `ruff check` and `ruff format --check` clean over 365 files. Twelve new tests pin the mechanism — that the switch ships off, that passing ranges with it off changes nothing, that with it on the exception reaches the flagged anchors inside a range and no others, that a glyph without loops is repaired exactly as before, and that the trace repair (K-B) stays loop-blind whatever the option says. Both harvests were re-run after the memo refactor and their occurrence files are byte-identical to the measured ones. Docs gates (`docs_register`, `docs_budget`) and the changelog fragment gate pass; the journal's widest-entry budget is untouched at 4500 of 4503. --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The author's question on the
sep05ink-clearance negative was the rightone: if the plate's counters are open at the plate nib, then following the
ink at the same width should keep them open — so do our lines pull toward
the middle? They do. This measures by how much, and where along the
chain it happens.
Docs only. No arm, no adoption, no row written, no
core/byte moved —the four candidates it produces are named with a one-line pre-registration
sketch each and built nowhere.
The method, in one paragraph
One quantity carries the whole chain:
D0, the inscribed diameter of theregion a closed CENTERLINE loop encloses. A Gleichzug pen of half width
hpaints the capsule union, whose counter is that region eroded byh, sothe visible aperture is exactly
D0 - 2h. That identity was checked againsta directly rasterised capsule union (≤ 0.0013 xh over seven chart rows)
before any number was read, and it makes the plate's ink, its frozen
skeleton, the stored Kette v5 fit and the composed row comparable in one
number instead of four incomparable ones.
What came out
For each of the nine loop-losing glyphs and every occurrence in the 63
frozen word specimens (medians, xh):
aoszgrvkGThe composed centerline loop is 0.10–0.20 xh narrower in diameter
(median 0.135) than the plate's ink implies — about 0.07 xh per side, two
thirds of a stroke width.
The loss happens twice, not once:
merge around a tight counter,
skeletonizereturns the axis of theMERGED blob rather than the two paths, so it sits between the hole and
the outer edge. The target the Lotse rides — and the evidence the Kette
measures against — is itself too narrow before any fit runs.
elementwise median over loops that are not congruent contracts; the
LF11 spline smoothing pushes the same way, because a tight loop carries
the row's highest curvature.
The Kette sits only 0.0116 xh off the skeleton (median over 183 loops),
and the composer loses nothing (≤ 0.0008 xh against the row). So the two
places worth an arm are the estimator and the evidence, not the fit.
Verdicts on the three hypotheses that were put
follows the medial axis (+0.0083 on
a, +0.0358 onsz, i.e. WIDERthere); four fifths of its deficit against the plate is inherited. A
small real inward pull remains on
r/g/p/G(−0.026…−0.033).negative nine times out of nine.
term. Eight of the nine chart rows sit between
D00.1805 and 0.1907 —a 0.010 xh spread across completely different letters. They carry exactly
the counter the chart's own 0.0725 pen needs and nothing more, which is
why the class breaks together the moment the pen grows.
Two side findings worth the author's eye: the pooled plate nib is confirmed
independently (skeleton half width median 0.0968 over 39 155 pixels),
and the Laufform row for
phas already lost its Kringel entirely —D00.0095 against 0.2556 on the chart row, so the three words with ajoined
prender without it at today's nib too.Numbers that bound the problem
At today's delivered nib (0.0724326) not one of the 63 specimens loses a
counter the plate shows open; at 0.097 it is 26 of 63, including the
Pflicht-Anker
das. Thesep05entry's count of 31 came from a differentcounting rule (per stored row, projected onto words) and its verdicts all
reproduce — all nine close,
rsurvives only on the Laufform row,pisworse on the row than on the chart form.
Base
Fresh
sep05root, own export:suetterlin-1922exported_at2026-09-06T06:25:39+00:00 digest
f30b700a2f3d…, pairs135b15ce909f…. Are-export can never reproduce an earlier digest —
manifest.jsoncarriesthe timestamp and the digest hashes it — so the proof that this is the
sep05base is the headline, not the hash:wordbench.run --set all --expect-root f30b700a2f3d,135b15ce909fwith BLAS pinned to one threadgives 0.108444 · 0.148236, digit for digit. The frozen root in the main
checkout was not touched.
What is in the diff
docs/notes/kringel-binnenflaechen-2026-09-06.md— the Befund-Journal:protocol (incl. the two estimator corrections made after the first pass,
named rather than hidden), the per-glyph tables, the verdicts, the four
rescue paths, the limits of the measurement.
docs/reference/messjournal.md— a Nachtrag paragraph inside the existingsep05result entry, because the rescue paths for its part (B) change.No new §14 entry: this is a diagnosis, not a round.
docs/proposals/tintenfolger.md§7.9 — the same four candidates asnumbered rescue paths (4)–(7) on the „Ink-Clearance an die Feder" row.
docs/reference/glossar.md— „Öffnungsweite · Mittellinien-Öffnungsweite(
D0)" and „Verschmelzungs-Anzeiger", both with Schnellindex entries.docs/index.md,changelog.d/kringel-counters-diagnosis.md.The PNG sheets (plate ink · skeleton · Kette · composed, at true scale with
the 0.097 stroke ghosted) stay in the session scratchpad and out of the
repo: they render harvested Laufform geometry, which the open-core rule
keeps out of commits.
🤖 Generated with Claude Code
https://claude.ai/code/session_01UEScQMZFvxxNNyNJYryfa3