Skip to content

fix(segment): a table cell is not a sentence — split it, like a paragraph - #2

Merged
groeimetai merged 1 commit into
mainfrom
fix/tabelcel-is-geen-zin
Aug 9, 2026
Merged

fix(segment): a table cell is not a sentence — split it, like a paragraph#2
groeimetai merged 1 commit into
mainfrom
fix/tabelcel-is-geen-zin

Conversation

@groeimetai

Copy link
Copy Markdown
Contributor

What changes

CORE.LEN.01 measures a sentence. The segmenter did not honour that inside tables: every
table cell became one unit, however much prose it held, while a paragraph has always been
collected first and only then split into sentences. A cell is now treated like a paragraph.

No rule file changed. This is a segmentation fix in tools/klaar/src/segment.js.

Why

The old behaviour did not just over-count — it made the rule unfixable.

A field-reference cell carrying five sentences was reported as one sentence of 85 words. The
author follows the rule's own fix text ("split the sentence at the conjunction"), splits neatly
at the full stop, and the number does not move, because the cell is still one unit. The only
remaining options are cutting past the point of meaning, or raising the baseline.

Both were taken in anger. In the Studio Groei platform the KLAAR baseline was raised six times
in six days
(304 → 312 → 362 → 366 → 379 → 387 warns), and the waarom on the last bump says
so itself: "TWEEDE ophoging in dezelfde sessie... dit is een gewoonte die je niet moet laten
sluipen."
Meanwhile 7 of the last 8 red CI runs on that repo were this ratchet.

A gate whose fix text does not work is worse than no gate: it teaches people to edit the
baseline instead of the text.

Evidence

Measured on a real corpus — the 39 spec documents of the Studio Groei platform, not an invented
example.

errors warns sentences density (warns/1000)
cell = 1 unit 23 387 9978 38.79
cell split 23 368 10301 35.72

The net figure is the least interesting part. The composition matters:

  • 31 warnings disappeared — cells that were only "too long" because they held several
    sentences.
  • 12 warnings appeared — real sentences over 30 words that were previously invisible,
    because their cell had already been counted once as a single unit.

So the measurement gets sharper in both directions, not merely gentler. Errors stayed at 23:
no document moves from conforming to failing.

Real example of a newly visible one, from docs/architecture/data-layer.md:

"De koppeling beeld ⇔ analyse is tweezijdig geklemd, want een designAnalyse z…"

And a representative disappearing one, from the table that triggered this work — a cell holding
five sentences of telemetry documentation, reported as a single 85-word sentence.

What deliberately does not change

  • Unit kind stays line. A cell is not a free-standing sentence in a paragraph, and the
    shape rules lean on that distinction.
  • All sentences of one cell keep the same block id, so a rule declaring unit: 'block' still
    sees the whole cell — groupByLine() groups on block id, not on line.

Only the boundaries move. maskInline is length-preserving, so an offset into the masked cell is
the same offset into the raw cell, and therefore into the source column.

Version: MINOR (0.2.0 → 0.3.0)

Part 5 asks: can a document that conformed yesterday fail today? No — conformance hangs on
errors, and those do not move. But PATCH does not cover it either: PATCH is for "tool changes
that do not change the verdict", and the verdict does change. A gate that ratchets on warning
counts can shift once because of this. Hence MINOR rather than something quieter.

Checks

  • node tools/klaar/bin/klaar.js check passes
  • node tools/klaar/bin/klaar.js conformance passes — 248 passed, 0 failed, over 30 rules
  • cd tools/klaar && node --test test/ passes — 41/41
  • npm run lint:self exit 0, 0 errors

Mutation-tested, not assumed. With only the production file reverted and the new tests kept,
the suite drops from 41/41 to 37/41 — all four new tests fail. They cover the split itself,
the shared block id, the per-sentence column offset, and a cell containing inline code (which
fails the moment maskInline stops preserving length).

Clean-room declaration

  • I have not copied rule text, vocabulary entries, examples, numbering or section structure
    from ASD-STE100 or from any other licence-restricted specification.
  • Every lexicon term I added carries a source from the allowlist in CLEANROOM.md.
    (No lexicon terms were added.)
  • Every example I added is my own, written about software delivery.
  • I have not compared a KLAAR lexicon against another standard's vocabulary.

CORE.LEN.01 begrenst een ZIN: "Een zin ZOU NIET langer MOETEN zijn dan de grens van
zijn artefactsoort." De segmenteerder hield zich daar niet aan voor tabellen. Elke cel
werd één unit, hoeveel proza er ook in stond, terwijl een alinea al sinds jaar en dag
eerst wordt verzameld en dan in zinnen gesplitst.

Waarom dat meer is dan een schoonheidsfoutje: het maakt de regel ONREPAREERBAAR. Een
veld-referentiecel met vijf zinnen werd gemeld als één zin van 85 woorden. De schrijver
volgt de reparatietekst ("splits de zin op het voegwoord"), splitst netjes op de punt,
en het getal beweegt niet — want de cel blijft één unit. Wat overblijft zijn twee
slechte uitwegen: snijden tot voorbij de betekenis, of de baseline ophogen. Allebei zijn
in de praktijk gebruikt.

## De wijziging

Een cel wordt nu behandeld als een alinea: verzamelen, maskeren, splitsen. Twee dingen
veranderen BEWUST niet.

- De unit-soort blijft `line`. Een cel is geen vrijstaande zin in een alinea, en de
  vormregels leunen op dat onderscheid.
- Alle zinnen van één cel houden hetzelfde block-id, dus een regel met `unit: 'block'`
  ziet nog steeds de hele cel — `groupByLine()` groepeert op block-id, niet op regel.

Alleen de GRENZEN verschuiven. `maskInline` is lengte-behoudend, dus een offset in de
gemaskeerde cel is dezelfde offset in de ruwe cel en dus in de bronkolom.

## Gemeten op een echt corpus, niet op een voorbeeld

39 spec-bestanden van het Studio Groei-platform:

|            | errors | warns | zinnen | dichtheid |
|------------|--------|-------|--------|-----------|
| cel = 1 zin |    23 |   387 |   9978 |     38.79 |
| cel gesplitst | 23 |   368 |  10301 |     35.72 |

Belangrijker dan het saldo is de samenstelling: **31 warns verdwenen en 12 kwamen erbij.**
De 31 waren cellen die alleen te lang waren omdat er meer zinnen in stonden. De 12 zijn
échte zinnen boven de 30 woorden die eerst wegvielen doordat hun cel al als één eenheid
was geteld. De meting wordt dus in twee richtingen scherper, niet alleen milder. Errors
bleven op 23: geen enkel document gaat hierdoor van conformerend naar falend.

## Versie: MINOR

Het criterium uit deel 5 is "kan een document dat gisteren conformeerde, vandaag falen?"
Nee — conformiteit hangt aan errors, en die bewegen niet. Maar PATCH dekt het evenmin:
dat vraagt "wijzigingen in het gereedschap die het oordeel niet veranderen", en het
oordeel verandert wel degelijk. Een poort die op wárn-aantallen ratelt kan hierdoor
eenmalig verspringen. Vandaar 0.2.0 → 0.3.0, en niet stiller.

## Tests

Vier nieuwe tests in `segment.test.js`, mutatie-getoetst: met alleen de productiecode
teruggedraaid zakt de suite van 41/41 naar 37/41 — alle vier vallen om. Ze dekken het
splitsen zelf, het gedeelde block-id, de kolom-offset per zin, en een cel met inline
code (die valt om zodra `maskInline` ooit niet meer lengte-behoudend is).

`npm test` 41/41 · `npm run conformance` 248/248 over 30 regels · `npm run lint:self`
exit 0, 0 errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@groeimetai
groeimetai merged commit bec2fbb into main Aug 9, 2026
2 checks passed
@groeimetai
groeimetai deleted the fix/tabelcel-is-geen-zin branch August 9, 2026 09:28
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