Typed Hygienic Generation v1
September 10, 2026 ยท View on GitHub
Audience: agent and tool authors, plus compiler contributors.
Status: implemented read-only, single-file generation projection; HOSTED GREEN under the
v0.4.0 release baseline.
Schema semaprax.hygienic-gen.v1 and diagnostic family SPX-Y remain unchanged.
Command
semaprax hygienic-gen <file> [--templates default-constructor,field-accessors] [--max-bytes N]
The library entry point is hygienic::generate(&path, &options). Options are
validated before any work: the template selection must be non-empty,
duplicate-free, and drawn only from the closed registry; max_bytes must lie
in the shared agent-context bounds (SPX-Y100 otherwise). The default
selection is the full registry and the default budget is 65536 bytes.
Model
Generation is a typed AST-to-AST transform followed by a real verifier pass โ never text templating:
- Parse and verify the base program with the ordinary pipeline. Any parse or verification diagnostic surfaces unchanged (no wrapper code); a failed base program generates nothing.
- Build the admission inventory. Records admit only when they are
non-generic, non-empty, and every field is
i64orbool; resources, variants, interfaces, generic records, empty records, and non-scalar records exclude with stable reasons. Function bodies are scanned under one step budget for unsupported shapes (floats, record construction/update/ projection, variant construction, match,try, generic calls, unsupported callees); excluded functions are reported but remain usable as admitted callee context for scans of other bodies. - Enforce hygiene before synthesis (
SPX-Y102/SPX-Y103fail closed). - Synthesize typed
ast::Functionnodes whose tails are realConstructRecord/Projectexpressions over zero literals. - Verify the combined program with the same verifier (
SPX-Y104on any rejection) and project both programs through the real Graph module. Every generated function identity must resolve in the combined projection (SPX-Y106), proving graph visibility.
Hygiene and derived names
The reserved prefix is __gen_. Existing program symbols may not use it
(SPX-Y103). Derived names are a pure function of the owning record's
persistent stable ID: __gen_<h8>_default and __gen_<h8>_get_<field> where
h8 is the first four bytes of
SHA256("semaprax.hygienic-gen.v1:name-digest.v1\0" || stable_id) in
lowercase hex. Renaming a record while keeping its @id therefore keeps every
derived name, moving code changes nothing, and accessor names additionally
bind the field identifier they project. A derived name that collides with an
existing symbol or another derived name fails closed (SPX-Y102) instead of
mangling. Generated declarations carry auto identities
(auto:<module>.<name>, explicit_id = false) so no persistent namespace is
claimed.
Generated functions have no effects, no contracts, value-mode scalar parameters, and canonical synthesized spans.
Output
Canonical compact JSON with fixed key order, ending in an authenticated
envelope: the payload's final brace is trimmed and
,"outer_sha256":"sha256:<64 hex>"} is appended, where outer_sha256 is
SHA256("semaprax.hygienic-gen.v1:outer-digest.v1\0" || payload_bytes) over
the full payload including its final brace. Key groups: source
(path, base revision, domain-separated source digest), registry,
templates, limits, types (total/admitted/excluded with closed reasons),
functions (total/admitted/excluded), generated entries (template, record,
record stable id, field, name, resolved id, formatted_sha256 of the
canonical formatter output of the generated function, and an ast summary),
budget (generated_total/generated_emitted), combined
(base/combined Graph schema and revision, function-node delta), truncation,
and fixed-order nonclaims.
Byte-budget truncation drops generated entries from the canonical tail while
preserving prefix order, records byte_budget in truncation.reasons, and
keeps the JSON valid. If even the zero-entry envelope cannot fit, generation
fails closed with SPX-Y105. The bounded-output accounting charges nested
strings conservatively, so the emitted envelope is always within the
requested budget.
Nonclaims
Fixed order: no_unrestricted_textual_rewriting, no_macro_system,
no_cross_file_scope, read_only_no_source_mutation,
no_persistent_artifacts, no_target_execution. Generation reads exactly
one source snapshot, writes nothing, executes nothing, and grants no ambient
authority.
Evidence
tests/projections/hygienic_gen.rs pins the known-answer identities and formatted
digests, cross-run byte determinism, rename-with-same-id name stability,
move/comment stability, semantic field-order sensitivity, hygiene collisions
(SPX-Y102), reserved-prefix preemption (SPX-Y103), verifier passthrough,
closed exclusion reasons, template selection, budget truncation and the
fail-closed floor (SPX-Y105), source immutability, nonclaim surface, and
the CLI grammar. Unit tests in src/hygienic.rs cover registry closure,
option validation, digest derivation, envelope exactness, and domain
separation.