Get in Touch
AI content toolsfrom Opace

Engineering paper · 5,558 documents

The text the model never saw

For months the tool cut documents every 340 words, on the premise that 340 words fits inside a 512-token window. On dense prose it does not. 1,348 of 23,318 sections ran over and had their ends thrown away, and the test built to catch exactly this kind of fault reported no problem at all.

On this page
  1. The finding
  2. How it was measured
  3. What the rule assumed
  4. Where it concentrated
  5. Why it hid
  6. What replaced it
  7. What the rewrite found
  8. The result nobody predicted
  9. What this does not prove
  10. Provenance
Published
30 August 2026
Measured
29 August 2026
Segmentation contract
segments-v3, the shipped rule
Corpus
5,558 long-form documents: 922 AI, 4,636 human

01 / The finding

A passing test, and text the classifier was never shown

The tool cut every document into sections of 340 words, on the stated premise that 340 words sits comfortably inside the classifier’s 512-token window. On dense prose it does not. Words are not tokens, and the sections that ran over the window did not fail loudly. They were quietly cut off at 512 and scored on what was left.

1,348 of 23,318 sections ran over the 512-token window and had their ends silently thrown away, across 684 of 5,558 documents and 276,466 of 9,287,413 tokens.

The worst single section needed 3,406 tokens and lost 2,894 of them. Source: docs/measurements/SEGMENT-TOKEN-FIX.md.
Sections truncated5.78%

1,348 of 23,318 sections under the 340-word rule ran past the window. Roughly one section in twenty was being read short.

Documents affected12.31%

684 of 5,558 documents lost the end of at least one section. Nothing in the rule bounded how much text that could be.

What the parity test saw0 of 284

Token mismatches between the browser and server implementations. Both truncated identically, so the routes agreed about the wrong text.

Recovering the lost text then changed no verdict at all on this corpus. That result is published here because it is true, not because it flatters the fix, and the section it sits in explains why maximum aggregation absorbed the defect.

Figure 1 Token coverage under the old 340-word rule and the shipped token-exact rule Three quantities, each with its denominator. Under the 340-word rule 1,348 of 23,318 sections exceeded the 512-token window; under the shipped segments-v3 rule, 0 of 21,093 do, with a measured maximum of exactly 512. Coverage is a property of a segmentation rule and a tokeniser, so no threshold and no operating point applies to any value here and none is printed. The v3 rule produces the identical 21,093 sections as the segments-v2 rule named in the source record, so these quantities are true of the shipped product.
Share of sections, documents and tokens affected by truncation under the old and shipped segmentation rules 0% 4% 8% 12% 16% Sections over the 512-token window 23,318 sections old rule · 21,093 new 5.78% · 1,348 of 23,318 0% · 0 of 21,093 Documents with an end silently dropped 5,558 documents under both rules 12.31% · 684 of 5,558 0% · 0 of 5,558 Tokens the model never saw 9,287,413 corpus tokens 2.98% · 276,466 of 9,287,413 0% · none dropped
340-word rule, as shipped until the fix Token-exact rule, contract segments-v3
Sections, documents and tokens affected by truncation under each segmentation rule
Quantity340-word ruleShipped segments-v3
Sections over the 512-token window1,348 of 23,318 (5.78%)0 of 21,093 (0%)
Documents with an end silently dropped684 of 5,558 (12.31%)0 of 5,558 (0%)
Tokens never seen by the classifier276,466 of 9,287,413 (2.98%)0 (0%)
Worst single section3,406 tokens needed, 2,894 lostmaximum measured exactly 512
Forward passes over the corpus23,31821,093 (9.4% fewer)

docs/measurements/SEGMENT-TOKEN-FIX.md §2 and §4. Tokenised with the checkpoint's own WordPiece tokeniser run without truncation, so the count is what a section would have needed rather than what it was allowed. Scoring runtime for the section counts: fp32 reference pipeline, Python onnxruntime 1.29.0, 23,318 forward passes under the old rule against 21,093 under the new.

02 / How it was measured

What produced these numbers

Every section of the corpus was tokenised with the checkpoint’s own WordPiece tokeniser, run without truncation, so what is counted is the length a section would have needed rather than the length it was allowed. That distinction is the whole measurement: with truncation on, every section reads as 512 tokens and the defect is invisible.

Measurement conditions

Corpus
5,558 long-form documents: 922 AI across 13 models, 4,636 human from Europe PMC, GOV.UK, CRS, Global Voices, Mongabay, SEC EDGAR and PERSUADE 2.0.
Runtime
fp32 reference pipeline, Python onnxruntime 1.29.0, the container's pinned version. 23,318 forward passes under the old rule, 21,093 under the new.
Measured
29 August 2026
Also
Browser check: int8 onnxruntime-web on WASM, over a deterministic register-stratified 596-document subset (296 AI, 300 human).
Corpus, tokeniser, runtimes and source record for this page
Corpus5,558 documents: 922 AI across 13 models, 4,636 human from Europe PMC, GOV.UK, CRS, Global Voices, Mongabay, SEC EDGAR and PERSUADE 2.0
Tokeniserthe checkpoint’s own WordPiece tokeniser, run without truncation
Scoring runtimefp32 reference pipeline, Python onnxruntime 1.29.0, the container’s pinned version
Browser checkint8 onnxruntime-web, WASM, on a deterministic register-stratified 596-document subset (296 AI, 300 human)
Measured29 August 2026
Sourcedocs/measurements/SEGMENT-TOKEN-FIX.md

The shipped segmentation contract is segments-v3. It produces the identical 21,093 sections as the segments-v2 rule described in the source record, so every coverage quantity on this page is true of the shipped product. Only the contract string moved on.

03 / The assumption

What the rule assumed, and where the assumption broke

WordPiece splits unfamiliar, technical and hyphenated words into several pieces, so a 340-word passage of biomedical or policy prose can need far more than 340 tokens. The proxy fails on exactly the dense writing the tool is weakest on anyway.

Measured across the corpus, tokens per word run at a median of 1.27, a 95th percentile of 1.62 and a maximum of 3.89. The section token distribution tells the same story from the other end.

Figure 2 Section length in tokens, against the 512-token window it had to fit inside 23,318 sections of 5,558 documents under the 340-word rule, tokenised without truncation. The 95th percentile sits at 523 tokens, on the wrong side of the window: roughly one section in twenty was being cut short. A feature of the text and the tokeniser only — no model, no threshold, no operating point applies. Log axis, because the distribution runs from 135 to 3,406.
Distribution of section length in tokens under the 340-word rule, against the 512-token window 100 200 500 1,000 2,000 4,000 min 135 p25 306 median 419 p75 448 p90 483 p95 523 p99 923 max 3,406 512-token window Log scale, in tokens
Percentiles of section length in tokens under the 340-word rule
PercentileTokensAgainst the 512-token window
min 135 inside
p25 306 inside
median 419 inside
p75 448 inside
p90 483 inside
p95 523 over
p99 923 over
max 3,406 over

SEGMENT-TOKEN-FIX.md §2. The box spans the interquartile range, p25 306 to p75 448.

A 95th percentile over the limit is not a tail risk. It is the ordinary behaviour of the rule on a twentieth of its work, and it had been the ordinary behaviour for months.

04 / Concentration

Where it concentrated

It fell where you would expect: on dense academic prose and on long human-written reports. On the AI side, academic literature reviews lost 80 of 597 sections and white papers 83 of 634. On the human side, fiction lost 122 of 1,031 and white papers 327 of 3,363. Student essays lost nothing at all, 0 of 1,011, which is a measured zero rather than an absent row.

Figure 3 Sections over the 512-token window by register, AI and human kept separate Share of sections exceeding the window under the 340-word rule, with the count and denominator on every bar. The two sides are never pooled: the AI and human halves of the corpus do not share a register mix, and a pooled rate would partly measure that mix. Where a register has no row on one side the chart says so rather than drawing a zero — the only zero here, human student-essay at 0 of 1,011, is measured. No threshold or operating point applies; this is a property of text length.
Share of sections over the 512-token window by register, AI and human series shown separately 0% 4% 8% 12% 16% academic-lit-review 13.40% · 80/597 1.93% · 23/1,193 white-paper 13.09% · 83/634 9.72% · 327/3,363 story 2.57% · 17/662 11.83% · 122/1,031 longform-journalism 0.62% · 5/812 7.96% · 300/3,771 company-update 1.74% · 7/402 7.02% · 212/3,021 academic-essay 6.15% · 48/780 Human: no row in the source table research-summary 4.88% · 22/451 2.37% · 17/717 academic-discussion 4.83% · 30/621 0.70% · 13/1,867 academic-introduction AI: no row in the source table 2.21% · 32/1,449 academic-conclusion AI: no row in the source table 1.07% · 10/936 student-essay AI: no row in the source table 0.00% · 0/1,011
AI documents Human documents
Show the numbers this was drawn from
Sections over the 512-token window by register and side
RegisterAI, sections over the windowHuman, sections over the window
academic-lit-review 80 of 597 (13.40%) 23 of 1,193 (1.93%)
white-paper 83 of 634 (13.09%) 327 of 3,363 (9.72%)
story 17 of 662 (2.57%) 122 of 1,031 (11.83%)
longform-journalism 5 of 812 (0.62%) 300 of 3,771 (7.96%)
company-update 7 of 402 (1.74%) 212 of 3,021 (7.02%)
academic-essay 48 of 780 (6.15%) no row in the source table
research-summary 22 of 451 (4.88%) 17 of 717 (2.37%)
academic-discussion 30 of 621 (4.83%) 13 of 1,867 (0.70%)
academic-introduction no row in the source table 32 of 1,449 (2.21%)
academic-conclusion no row in the source table 10 of 936 (1.07%)
student-essay no row in the source table 0 of 1,011 (0.00%)

SEGMENT-TOKEN-FIX.md §2. Register labels are machine-assigned, so every split here inherits that. Tokenised without truncation on the fp32 reference pipeline.

The AI literature-review figure covers 28 of 107 documents, and the human fiction figure 65 of 260. A register absent from one side’s source table is printed as absent rather than as a zero, because those are different claims.

05 / Why it hid

A test that could not see a fault both routes shared

The tool has two implementations of segmentation, one in TypeScript for the browser and one in Python for the server, and a route-parity test exists precisely to catch them drifting apart. That test saw 0 of 284 token mismatches.

Both routes truncated identically. A test that asks “do these two agree” cannot see a fault they share.

Parity is not correctness. The routes agreed, and they agreed about the wrong text.

There was a second signal, and it was misread. The server’s truncated flag carried a comment saying that a true value means the two routes have drifted apart. The flag was firing constantly, and never for that reason. A flag that fires all the time stops being read, which is how a loud warning becomes a silent one.

06 / The replacement

A token-exact split, with the exactness verified rather than assumed

Two facts make a word-granularity split exact. Whole-document tokenisation decomposes precisely across /\S+/ boundaries, checked token for token on all 5,558 corpus documents and on the eight golden texts, 5,558 of 5,558. And a WordPiece token never consumes fewer than one code unit, which bounds the one case word granularity cannot handle.

Each word is one atom, tokenised and measured. A document whose measured tokens plus the two special tokens fit in 512 is one section, verbatim. Otherwise it is cut into the fewest sections that all fit, as near equal in tokens as word boundaries allow, using integer arithmetic only so no rounding rule can drift between the two languages. Every resulting section is then re-tokenised and measured, and the split widens by one part if any section overshoots.

One word in 6,916,005 needed the oversized-word path: a 987-character run of punctuation that BERT basic tokenisation splits into 987 tokens before WordPiece runs.

The three legs of proof for the token-exact segmentation rule
LegWhat it establishes
By constructionThe loop cannot return a partition it has not tokenised and checked.
By terminationWidening cannot run away, because at one atom per section every atom is inside the budget.
By measurementAll 21,093 sections of the corpus were measured: 0 over 512, maximum exactly 512, and the widening loop never needed a second iteration.

07 / What the rewrite found

Three cross-language divergences that were sitting in shipped code

Running the two implementations over the whole corpus and diffing every section’s start offset, end offset, word count and measured token count found 5,558 of 5,558 documents identical across 21,093 sections. Getting there exposed three genuine divergences that the eight golden test texts could not reach.

Cross-language divergences found during the segmentation rewrite
DivergenceEffectFix
Python’s \S treats U+001C–U+001F and U+0085 as whitespace; JavaScript’s does not, and JavaScript treats U+FEFF as whitespace where Python does notWord counts disagreed on 6 documents (0.11%), enough for the front end’s drift guard to refuse the server’s answer on themsegments.py now spells the class out as JavaScript’s, because the browser is what ships
JavaScript’s whole-string toLowerCase() applies the Greek final-sigma rule; the Rust tokeniser the checkpoint was trained with does notA different token sequence, changing the segmentation of 1 documenttokenizer.ts now lower-cases character by character
tokenizer.ts tested whitespace as [\t\n\r \p{Zs}], missing U+2028, U+2029 and U+0085A spurious [UNK] in one GOV.UK documenttokenizer.ts now uses \p{White_Space}

All eight golden token-id sequences still match after those two tokeniser changes: 8 of 8.

08 / The result

The result nobody predicted

The obvious hypothesis is that recovering the dropped text raises scores on the documents that were being truncated. That is not what happened.

Every detection figure in this section was measured at a retired flag point.

The before-and-after comparison below is at 0.980, the single threshold that shipped on the server at the time, with a second reading at 0.984. Both are superseded by the shipped minimum-evidence pair. These numbers answer the same question at a rule this tool no longer uses, and they must not be read as a description of how it behaves now. The coverage quantities elsewhere on this page carry no operating point at all and are unaffected.

Measured at a rule that no longer ships

Corpus
The 684 documents the old rule truncated, drawn from the 5,558-document long-form corpus: 126 AI, 558 human.
Retired flag point
0.980, with a second reading at 0.984
Detector
tier3-cycle2-e5small-fp32.onnx, SHA-256 e313ab00de1fffd2…4d2788d
Runtime
Python onnxruntime 1.29.0, CPU, fp32
Measured
29 August 2026
Also
Before-and-after comparison of two segmentation rules over the same documents, with the same weights reading the same text both times.

The rule that ships today is 0.9855 / 0.9763. The figures under this stamp answer the same question at a flag point this tool no longer uses, and they are not a description of the tool as it runs now.

Documents flagged under each segmentation rule, at the retired 0.980 flag point
On the 684 truncated documents, at the retired 0.980 pointOld ruleShipped segments-v3 rule
AI flagged (n = 126)123 (97.62%)123 (97.62%)
Human flagged (n = 558)2 (0.36%)2 (0.36%)

Retired flag point 0.980. Not one verdict on that subset changed, at either threshold measured. Of the 15 AI documents newly flagged at the retired 0.984 point, none had a truncated section under the old rule.

Maximum aggregation is why. A truncated section still contributed its first 512 tokens, and on a document where any section reads as machine-written the maximum was already being set by some other section. The lost text would have had to be the only machine-looking part of the document to change anything, and on these 5,558 documents it never was.

The detection that did improve came from somewhere else. The new rule produces fewer, longer, equal-sized sections where the old one produced a full 340-word run followed by short rebalanced remainders, and detection is length-sensitive. Every newly flagged document has a lower section count under the new rule. There is a symmetric cost, recorded rather than buried: 72 documents that the old rule split needlessly are kept whole by the new one, all 72 human, and at the retired 0.980 point two of them are now flagged that were not before.

The fix removed a real, measured, unbounded coverage defect. On this corpus that defect happened not to be costing verdicts. That is luck rather than design.

Nothing in the old rule bounded how much text it threw away, and the worst case measured was 2,894 tokens from a single section.

It also costs 9.4% fewer forward passes on the same documents, 21,093 against 23,318, so the inference bill went down.

09 / Limits

What this does not prove

The coverage figures are threshold-free; the detection figures are not. Every before-and-after detection number in the source record is at 0.980 or 0.984, both superseded by the shipped minimum-evidence pair 0.9855 / 0.9763. The detection deltas are quoted above only with their own threshold named, and they must not be reprinted under a shipped-pair heading. The coverage quantities carry no operating point and publish as they stand.

The corpus is not fully held out.

Of the 922 AI documents, 268 (29.1%) appear in the cycle-2 dataset and 168 of those in the training split; the human side is effectively clean at 11 of 4,636. For a before-and-after comparison of two segmentation rules over the same documents this does not matter, because the same weights read the same text both times. It would matter for an absolute accuracy claim, and none is made here.

The browser check is a subset, and it is register-balanced rather than corpus-proportional. Its 2.33% human false-positive rate is not comparable to any corpus-wide figure: fiction, the worst register, is 10% of it against 5.6% of the corpus. Read the deltas, not the levels. At denominators of 296 and 300, one document is 0.34 percentage points.

Earlier figures for the same phenomenon are superseded. A 60-document route-parity sample reported 12.7% of sections and 21.7% of documents. The corpus here is 93 times larger and differently composed. Those figures should not appear beside these.

The Python halves of the measurement code are not reproducible from the repository. They live in a session scratchpad. The TypeScript verification scripts are committed.

Register labels are machine-assigned, so every per-register split in Figure 3 inherits that.

10 / Provenance

Where every figure on this page came from

Source file and section for every figure on this page
FigureFileSection
1,348 of 23,318 sections; 684 of 5,558 documents; 276,466 of 9,287,413 tokens; worst section 3,406 needing, 2,894 lostdocs/measurements/SEGMENT-TOKEN-FIX.md§2
Section token percentiles, min 135 to max 3,406; tokens per word 1.27 / 1.62 / 3.89SEGMENT-TOKEN-FIX.md§2
Sections over the window by register and side, both denominatorsSEGMENT-TOKEN-FIX.md§2
Route-parity test, 0 of 284 token mismatches; the misread truncated flagSEGMENT-TOKEN-FIX.md§3
21,093 sections, 0 over 512, maximum exactly 512; 5,558 of 5,558 documents identical; 8 of 8 golden sequencesSEGMENT-TOKEN-FIX.md§4
Three cross-language divergences, 6 documents and 1 document affectedSEGMENT-TOKEN-FIX.md§4
Detection before and after on the 684 truncated documents — retired flag points 0.980 and 0.984SEGMENT-TOKEN-FIX.md§5
72 documents kept whole, all human; 9.4% fewer forward passesSEGMENT-TOKEN-FIX.md§5
268 of 922 AI documents in the cycle-2 dataset, 168 in train; human 11 of 4,636corpus-reconciliation-2026-08-29/analysis.txt§2

Every file named above ships with the open measurement repository, so any figure on this page can be re-run rather than taken on trust. The Python halves of this particular measurement are the exception noted in the limits above.

Read on

A defect that cost no verdicts is still a defect. Here is how the rest of the method is checked.