Skip to content

feat(core,cli): font-coverage and shaping-broken rules — M4 4.3 - #34

Merged
aenawi merged 2 commits into
mainfrom
font-rules
Sep 5, 2026
Merged

aenawi merged 2 commits into
mainfrom
font-rules

Conversation

@aenawi

@aenawi aenawi commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Turns 4.1's and 4.2's facts into findings. PLAN §4.3.

Two rules, two defects, opposite advice

  • font-coverage — the font has no glyph for the Arabic; it renders as empty boxes. Names the exact characters (U+067E ARABIC LETTER PEH). Error when the font answers for none of the text, warning when it misses part of it.
  • shaping-broken — the font has every letter and no shaping tables; the Arabic renders as disconnected letters.

Neither is fixable. The repair for both is a different typeface, which is an authoring decision — and unlike complex-font-missing, which fills an empty slot, these would be overwriting a font the author chose.

Off by default, and that is the design

These are the first rules that ask about the machine rather than the document. An audit that resolved fonts unasked would report differently on a laptop and on a CI runner looking at one file. So:

  • mirsam audit --fonts asks for them
  • --font-dir <DIR> pins the directories searched, which is what makes the answer reproducible
  • every report says which of the two audits it is — fonts NOT RUN, or "fonts": {"checked": false}

The claim runs one way only. A font that is here and cannot draw the text will not draw it anywhere — a cmap and a GSUB travel with the font. A font that is here and draws it perfectly proves nothing about the reader's machine, and silence is not a promise that the document renders.

The threshold ADR 0008 left open

Four required joins, counted over letters the font actually has. A final form exists only because the letter before it joined forwards, so every final has a companion initial or medial — the dual-joining letters a font like Arial does shape. Two joins are one such letter, which is exactly the design choice the ADR forbids concluding from, and is the whole of a two-letter word. Four are two of them, independently silent.

Unmapped letters are excluded so a Latin-only font cannot raise shaping-broken beside its font-coverage finding — the tool arguing with itself about one paragraph.

Validation

latin.ttf joins the fixtures: printable ASCII, no Arabic, no GSUB. Beyond the fixtures, on macOS, against a corpus deck retyped to each face:

font result
Helvetica, Comic Sans MS every Arabic character of every paragraph reported
Times New Roman, Geeza Pro, Mishafi, Al Nile, Baghdad silent
Arial silent — ADR 0008's reh, four joins of five, still not a defect

Corpus

The six committed reports gained one fonts key each and zero findings changed. make verify and make validate-fixtures are green.

Bookkeeping

M1 and M3 had every sub-item done and a header nobody flipped. Tatweel-as-padding has sat in ROADMAP.md's M4 since it was written with no plan item carrying it, so it is now 4.4 and M4 stays open until it lands.

4.1 and 4.2 built two modules that refuse to draw a conclusion. This draws
them, and there are exactly two, because they are two defects with opposite
advice. font-coverage says the font has no glyph for the Arabic: it renders as
empty boxes, and no shaping table would have saved it. shaping-broken says the
font has every letter and no shaping tables: it renders as a row of
disconnected letters, and more coverage would change nothing. One rule
reporting both would be a rule nobody could act on.

These are the first rules that ask about the machine rather than the document,
and that is why they are off by default. Every other check gives the same
answer on any computer; which fonts a computer has is not a property of the
deck, so an audit that resolved them unasked would report differently on a
laptop and on a CI runner looking at one file, and neither run could be
reproduced. mirsam audit --fonts asks; --font-dir pins the directories, which
is what makes the answer reproducible at all; and the report says which of the
two audits it is — fonts NOT RUN for a human, "fonts": {"checked": false} for
an agent. That is standing rule 4 spent where it was always going to be
needed. The golden corpus never passes the flag, so the six committed reports
gained one key each and not a single finding.

The claim runs one way only. A font that is here and cannot draw the text will
not draw it anywhere, because a cmap and a GSUB travel with the font — that is
what makes a finding worth making. A font that is here and draws it perfectly
proves nothing about the reader's machine, and silence from these rules is not
a promise that the document renders. AGENTS.md says so where an agent reads it.

Three ways to say nothing, all deliberate. A paragraph naming no
complex-script font is complex-font-missing's; guessing which font the
application substitutes is the invention this design refuses. A family this
machine does not have is the tool losing the ability to say what the reader
will see — a fact about the computer, not a defect in the deck, and reporting
it would fire on every runner with no fonts installed. A file the shaper
cannot parse is a fact about the file.

ADR 0008 left the shaping threshold to this item and it is four required
joins, counted over letters the font actually has. A final form exists only
because the letter before it joined forwards, so every final has a companion
initial or medial, and those are the dual-joining letters a font like Arial
does shape. Two joins are therefore one such letter, which is exactly the
design choice the ADR forbids concluding from and is the whole of a two-letter
word. Four are two of them, independently silent, and no font that shapes
Arabic looks like that. Unmapped letters are excluded so a Latin-only font
cannot raise shaping-broken beside its font-coverage finding: the tool would
be arguing with itself about one paragraph, with one repair and two
explanations.

Severity is what 4.2 built missing_occurrences for. A font answering for none
of the Arabic was not the font this text needed — the paragraph goes blank,
and that is an error. A font missing some of it is a sound pairing meeting an
unusual letter, which Mishafi and Persian peh is, and is a warning. Neither is
fixable: the repair for both is a different typeface, an authoring decision the
text cannot supply, and unlike complex-font-missing these would be overwriting
a font the author chose rather than filling a slot they left empty.

latin.ttf joins the fixtures: printable ASCII, no Arabic, no GSUB, written by
the same struct-and-nothing-else generator as the other four. Helvetica under a
complex-script slot reduced to its principle, so the acceptance is a committed
test rather than a manual check. Validated beyond the fixtures on macOS
against a corpus deck retyped to each face: Helvetica and Comic Sans MS report
every Arabic character of every paragraph; Times New Roman, Geeza Pro, Mishafi,
Al Nile, Baghdad and Arial report nothing — ADR 0008's reh, four joins of five,
still not a defect.

Bookkeeping: M1 and M3 had every sub-item done and a header nobody flipped.
Tatweel-as-padding has been in ROADMAP.md's M4 since it was written with no
plan item carrying it, so it is now 4.4 and M4 stays open until it lands.
CI's clippy is ahead of the toolchain this was written on and reads
chunks_exact over a constant size as a missed as_chunks. The array type
carries the record length, so the field reads below are checked once at the
split rather than on every one.
@aenawi
aenawi merged commit dca4d53 into main Sep 5, 2026
7 checks passed
@aenawi
aenawi deleted the font-rules branch September 5, 2026 06:27
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