feat(text): author policy programs through a typed expression DSL - #64
Open
thejustinwalsh wants to merge 8 commits into
Open
feat(text): author policy programs through a typed expression DSL#64thejustinwalsh wants to merge 8 commits into
thejustinwalsh wants to merge 8 commits into
Conversation
size-limit report 📦
|
thejustinwalsh
force-pushed
the
refactor/policy-dsl
branch
from
August 11, 2026 22:35
3698aa6 to
fe65ede
Compare
thejustinwalsh
force-pushed
the
refactor/policy-dsl
branch
from
August 11, 2026 23:33
77e54be to
d822798
Compare
thejustinwalsh
marked this pull request as ready for review
August 12, 2026 00:30
The upcoming policy DSL must be a pure authoring-layer change; these sha256 digests over all four transform/allocation variants make byte drift in the compiled wire records a loud failure.
Policy programs are written against named values instead of register numbers (D-250): policyProgram() exposes semantic handles — including color, which replaces the foreground fiction at the authoring layer; the engine has no background — and declared binding fields by name, while addF32/subtractF32/multiplyF32/u32ToF32 and typed constants build an expression graph that compile() lowers to the same forward-only PolicyOperation records, allocating registers automatically with use-before-write and exhaustion as errors and deduplicating reused values. The four Three programs port to the DSL with per-technique named buffer ids. The wire format, validator, and interpreter are untouched, and value types remain a wire-level property of each operation and buffer schema; the DSL brands exist only at authoring time. A decoded-bytes equivalence test proves the port preserves the input tables, buffer schemas, program metadata, and per-lane store dataflow against the hand-numbered fixtures; the byte goldens re-pin once over that proof.
The scalar tail costs ~50 ns per record; overlap would save ~100 ns per tailed span. Below the D-245 admission bar at current draw-span distributions; recorded so the next profile that blames the packing pass starts from numbers.
The DSL authoring layer rides the Three bundle and the core subpath; raw grew by comments and names while minified, gzip, and Brotli stayed inside their ceilings. Size evidence regenerates with the budgets.
thejustinwalsh
force-pushed
the
refactor/policy-dsl
branch
from
August 12, 2026 00:32
d822798 to
c7658dd
Compare
Each raster technique declares its physical shape once, colocated and exported: defineTechniqueSchema in core; bitmapSchema, msdfSchema, and slugSchema beside their techniques; decorationSchema and the Three policy's system buffers in the Three policy. Programs build with techniqueProgram(schema) and store through schema buffer handles, the plan executor looks buffers up by declared id — including two literal lookups the new repository gate caught in the decoration material path — and the gate forbids literal buffer identity anywhere outside the declaration sites from now on. The compiled policy bytes are byte-identical across the change: the goldens did not move, proving the authority layer is pure naming. The full contract plan (D-251) records the remaining layers: shader interfaces derived from schemas, the data-origin axis with the reserved pretext fallback technique, the external example rebuilt on the construct, and tsdown bundling with per-subpath size re-basing.
Policy-DSL values carry session provenance: storing a value loaded from another program's input table throws instead of silently reading a shifted input. Technique schemas deep-freeze at definition, making the documented immutability true. The remaining restatement sites derive from schemas: schemaPolicyBuffers replaces hand-rolled width lists in the Three programs, schemaFieldTable orders binding readers by declared names so a missing or misspelled reader is a compile error, the executor resolves draw buffers by schema name instead of literal id ranges, and the plan-program registry references the transform system buffer instead of restating its id. Glyph-origin augmentation is now schema-declared opt-in metadata rather than assuming Bitmap's layout for every technique. The structural gate also rejects literal-width buffer builders, literal id ranges, and restated system ids. Every policy and binding byte golden stayed pinned: the derivations reproduce the hand-rolled bytes exactly. Core and Three raw budgets re-priced (+1.8 KB / +1.9 KB, comment- and name-dominated); compressed ceilings unchanged.
Session provenance is stamped at node construction and combined in O(1):
shared expression DAGs no longer trigger an exponential graph walk in
store(), and mixing two authoring sessions fails at the combinator.
Schema definition validates the caller's input first and returns an
owned, deeply frozen copy — rejection leaves caller data untouched,
hostile accessors cannot change a validated width afterwards, and only
declared fields are carried. Size-budget notes record the measured
deltas instead of claiming comment-dominated growth, and the package
reference documents /core and /tsl with /three/{bitmap,msdf,slug} as
compatibility aliases.
# Conflicts: # docs/log.md # docs/packages/benchmarks.md # docs/packages/glyph.md # docs/planning/benchmark-plan.md # docs/planning/decision-register.md # docs/planning/font-baker-implementation.md # docs/planning/tooling-fixtures.md # packages/glyph/src/core/policy-program.ts # packages/glyph/src/core/technique-schema.ts # packages/glyph/tests/integration/render-policy-equivalence.test.mjs # packages/glyph/tests/integration/render-policy-golden.test.mjs # packages/glyph/tests/package/policy-program-provenance.test.mjs # packages/glyph/tests/package/schema-authority.test.mjs # packages/glyph/tests/package/technique-schema.test.mjs # packages/glyph/tests/types/policy-program-dsl.test.ts # packages/glyph/tests/types/technique-schema.test.ts
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.
Policy authoring DSL (D-250)
Policy programs are written against named values instead of register numbers.
policyProgram()exposes the semantic handles —inlineOrigin,blockOrigin,fontSize, andcolorwith.red/.green/.blue/.alphachannels (the engine has no background; "foreground" contrasted with nothing) — plus binding fields declared by name (bearingX,uvOriginX,page, …), andaddF32/subtractF32/multiplyF32/u32ToF32with typed constants build an expression graph.compile()lowers it to the same forward-onlyPolicyOperationrecords, allocating registers automatically — use-before-write and register exhaustion are compile errors with names in the message — and deduplicating reused values.The hand-numbered bitmap fragment:
becomes:
Wire contract unchanged: value types stay a wire-level property of each operation and buffer schema; the DSL's branded
PolicyF32Value/PolicyU32Valueexist only at authoring time in TypeScript. The four Three programs port to the DSL with per-technique named buffer ids (BITMAP_UV_ORIGIN,SLUG_BAND_TRANSFORM, …).Proof: a decoded-bytes equivalence test parses old and new policy bytes and asserts identical input tables, buffer schemas, capability sets, program metadata, and per-lane store dataflow against fixtures captured from the hand-numbered programs (register numbering is program-private; the interpreter requires only forward-only writes). The byte goldens re-pin once over that proof and freeze the DSL output from here. 180 package tests and the integration suite are green.
Deferred: renaming the ABI JSON keys (
foregroundRed→colorRed, indices unchanged) in the Rust ABI emitter — the DSL already presentscolorat the authoring surface.Stack created with GitHub Stacks CLI • Give Feedback 💬
Adversarial review closure
Two passes against Codex Sol High's findings, both on this branch:
da67013— session provenance on DSL values, deep-frozen schemas,schemaPolicyBuffers/schemaFieldTable/schemaDrawBuffersderivations replacing every literal id range, positional field table, and width list; schema-declaredglyphOriginopt-in replacing the Bitmap-layout assumption; three new structural gate patterns. All byte goldens stayed pinned.efe2f44— re-review follow-ups: provenance stamped at node construction (O(1) per store, no exponential DAG walk, cross-session mixing fails at the combinator); schema definition validates first and returns an owned deeply-frozen copy (rejection never touches caller data, hostile accessors cannot change validated widths); size-budget notes record measured deltas; package reference documents/coreand/tslwith/three/*as compatibility aliases.Re-review disposition: session provenance and glyph-origin closed; schema authority and freezing closed at this stack's scope. The remaining structural finding — TSL shader lane meaning and the external example still restate schema knowledge — is migration layers 3–4 of
docs/planning/raster-technique-contract.md, scheduled with the technique-contract audit.