Skip to content

Commit 2d24cf8

Browse files
committed
fonts gate: a SHIPPED DEFAULT that cannot resolve is a note, not a block
My own over-reach, caught by CI on the previous commit: run 34973666241 failed with test_step1_artifacts at 49 passed / 8 failed, every failure of the form "a thing that should pass". The suite passes 57/0 on a Mac with Office fonts installed, so the condition was invisible locally. Cause, reproduced exactly rather than assumed: deckkit's shipped defaults are FONT='Calibri' / MONO='Consolas' / EQFONT='Arial', and Calibri and Consolas ship with NEITHER macOS NOR Linux. The new gate therefore blocked essentially every deck built with library defaults — including the CI fixtures. Quarantining the Calibri files on this machine reproduced CI's numbers to the assertion (49/8), and restoring the downgrade produced 57/0, so the diagnosis is measured, not inferred. The condition is the same either way — a substituted measurement — but the RESPONSIBILITY is not, and only one of the two is a decision anybody made: * a face the AUTHOR set is a choice the machine cannot honour -> block * a face the LIBRARY chose is an environment fact about the host -> note, naming the one-call fix (deckkit.use_platform_fonts()) A gate that fires on the whole population is not a floor, it is an outage. The note is still a strict improvement on what existed before this batch: the condition used to be a print() in lint_layout, in no --json and gating nothing. DEFAULT_FACES is read from deckkit's SOURCE, not the imported module: a build script assigns dk.FONT before the gate runs, so reading the live globals would classify every author-set face as "shipped" and defeat the split entirely. A test pins the derived set against deckkit's live defaults, so a future re-theme fails loudly rather than silently blocking the new default. Verified on a host where Calibri genuinely does not resolve: a deck in the default face is a note and blocks nothing, while a face the author chose still blocks on that same host. test_step1_artifacts 57/0 · new suite 20/0 · template-branch 12/0 · slide_background 29/0 · deck_cycle 26/0 · codex delivery gate, gate parity and the structural guards all clean.
1 parent 8488948 commit 2d24cf8

2 files changed

Lines changed: 84 additions & 1 deletion

File tree

skills/slide-maker/scripts/check_fonts_resolve.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import argparse
4747
import json
4848
import os
49+
import pathlib
4950
import sys
5051

5152
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
@@ -55,6 +56,40 @@
5556
# wrong face is exactly the kind of thing that overflows a title band.
5657
MIN_CHARS = 8
5758

59+
# 🔴 deckkit's SHIPPED DEFAULTS are reported and never blocked, however much text they carry.
60+
# The condition is identical either way — a substituted measurement — but the RESPONSIBILITY is
61+
# not, and only one of the two is a decision anybody made:
62+
# * a face the author SET is a choice the machine cannot honour -> block, make them decide;
63+
# * a face the LIBRARY chose is an environment fact about this host. `FONT='Calibri'` and
64+
# `MONO='Consolas'` ship with neither macOS nor Linux, so blocking on them would refuse
65+
# delivery of a perfectly correct deck on essentially every stock machine — including this
66+
# repo's own Ubuntu CI, which is how the over-reach was caught: a suite that passes on a Mac
67+
# with Office fonts installed failed 8 assertions on CI, all of them "a thing that should
68+
# pass". A gate that fires on the whole population is not a floor, it is an outage.
69+
# The fix for this arm is one call (`deckkit.use_platform_fonts()`), and it is named in the note.
70+
# Read from deckkit's SOURCE, not from the live module: a build script assigns `dk.FONT = ...`
71+
# before this runs, so the imported globals are the deck's choice, not the library's default —
72+
# reading them would classify every author-set face as "shipped" and defeat the whole split.
73+
# The literal set is the documented fallback and a test pins the two against each other.
74+
_FALLBACK_DEFAULT_FACES = {"Calibri", "Consolas", "Arial", "STIX Two Math", "Cambria Math"}
75+
76+
77+
def _shipped_defaults():
78+
import re as _re
79+
try:
80+
src = (pathlib.Path(__file__).with_name("deckkit.py")).read_text(encoding="utf8")
81+
except Exception:
82+
return set(_FALLBACK_DEFAULT_FACES)
83+
out = set()
84+
for attr in ("FONT", "MONO", "DISPLAY", "EAFONT", "EADISPLAY", "EQFONT", "EQ_MATHFONT"):
85+
m = _re.search(r"^%s\s*=\s*[\"']([^\"']+)[\"']" % attr, src, _re.M)
86+
if m:
87+
out.add(m.group(1))
88+
return out or set(_FALLBACK_DEFAULT_FACES)
89+
90+
91+
DEFAULT_FACES = _shipped_defaults()
92+
5893
_A = "{http://schemas.openxmlformats.org/drawingml/2006/main}"
5994
_P = "{http://schemas.openxmlformats.org/presentationml/2006/main}"
6095

@@ -164,7 +199,16 @@ def check(pptx):
164199
if good:
165200
continue
166201
facts["unresolved"].append(face)
167-
sev = "block" if chars >= MIN_CHARS else "note"
202+
shipped = face in DEFAULT_FACES
203+
sev = "note" if (shipped or chars < MIN_CHARS) else "block"
204+
if shipped:
205+
findings.append(("note", face,
206+
"carries %d character(s) and does not resolve here, but it is one of "
207+
"deckkit's SHIPPED DEFAULTS rather than a face this deck chose — an "
208+
"environment fact about this host, not a defect in the deck. Every "
209+
"wrap/fit number for it was still measured in a stand-in: fix with "
210+
"deckkit.use_platform_fonts(), or install the face." % chars))
211+
continue
168212
findings.append((sev, face,
169213
"carries %d character(s) of this deck's text but does NOT resolve here — "
170214
"every wrap, fit and overflow number computed for it was measured in a "

skills/slide-maker/tests/test_template_profile_and_fonts.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,45 @@ def _with_registry(entries, fn):
199199
ck([f for f in finds if f[0] == "block"] == [],
200200
"a deck set in an installed face (%s) produces no blocking finding" % real)
201201

202+
print("\n— fonts: a SHIPPED DEFAULT that cannot resolve is a NOTE, not a block")
203+
ck(CF.DEFAULT_FACES and "Calibri" in CF.DEFAULT_FACES,
204+
"the shipped-default set is derived from deckkit's source: %s" % sorted(CF.DEFAULT_FACES))
205+
_real = CF._resolver
206+
CF._resolver = lambda: (lambda face: False) # a host with NO fonts at all — CI's situation
207+
try:
208+
with tempfile.TemporaryDirectory() as tmp:
209+
deck = os.path.join(tmp, "nofonts.pptx")
210+
_deck(deck, face="Calibri")
211+
finds, _ = CF.check(deck)
212+
sev = {f: s_ for s_, f, _ in finds}
213+
ck(sev.get("Calibri") == "note",
214+
"on a font-less host, a deck in deckkit's DEFAULT face is a note (%r) — blocking it "
215+
"would refuse delivery on essentially every stock machine" % sev.get("Calibri"))
216+
ck([f for f in finds if f[0] == "block"] == [],
217+
"…and nothing in that deck blocks")
218+
deck2 = os.path.join(tmp, "chosen.pptx")
219+
_deck(deck2, face="Some Deliberately Chosen Face")
220+
finds2, _ = CF.check(deck2)
221+
ck(any(s_ == "block" and f == "Some Deliberately Chosen Face" for s_, f, _ in finds2),
222+
"…while a face the author CHOSE still blocks on the same host — the split is "
223+
"responsibility, not severity of the condition")
224+
finally:
225+
CF._resolver = _real
226+
227+
print("\n— fonts: the derived default set matches deckkit's live module defaults")
228+
import importlib, subprocess, json as _json
229+
_out = subprocess.run([sys.executable, "-c",
230+
"import sys;sys.path.insert(0,'scripts');import deckkit,json;"
231+
"print(json.dumps([deckkit.FONT, deckkit.MONO, deckkit.EQFONT]))"],
232+
capture_output=True, text=True, cwd=os.path.dirname(HERE))
233+
if _out.returncode == 0:
234+
live = set(_json.loads(_out.stdout))
235+
ck(live <= CF.DEFAULT_FACES,
236+
"every live deckkit default (%s) is in the derived set — if deckkit re-themes, this fails "
237+
"rather than silently blocking the new default" % sorted(live))
238+
else:
239+
ck(False, "could not read deckkit's live defaults: %s" % _out.stderr[-120:])
240+
202241
print("\n— fonts: `ea` and `cs` typefaces are counted, not only `latin`")
203242
ck("ea" in open(os.path.join(os.path.dirname(HERE), "scripts",
204243
"check_fonts_resolve.py")).read(),

0 commit comments

Comments
 (0)