- overview.md, core.md, emit.md, targets.md: modular spec set for the generalized engine (contexts + templates + partials -> text), with the three targets (ssr-html, bast-rust-codecs, call-handlers, wgsl-jit) as template sets plus emit postures - ADRs 001-010: one core/template-set split, handlebars 6.4 strict substrate, sync/wasm/deterministic posture, typed contexts with builders, per-registry escaper, SSR library posture, typed direct codecs output contract, template-set ownership, feature gating, library-only v1 - open-questions.md: OQ-01..OQ-08 centralized tracker carrying phase-0 residue (GN-01/02/07/09) with three deferred(scope) entries plus a resolved disposition for phase-0 OQ-GN-04 - tasks/architecture/: external-trigger tracker tasks paired with the deferred OQs (taskgraph validate passes) - phase-0.md checklist dispositioned for OQ-GN-04; AGENTS.md lifecycle updated to phase 1 Verification: taskgraph validate (4 tasks OK), cargo fmt --check, cargo test, em-dash grep clean; architecture-reviewer pass returned zero critical issues
2.4 KiB
2.4 KiB
ADR-005: Escape strategy is a per-registry policy
Status
Accepted
Context
handlebars applies HTML entity escaping to {{expr}} output by
default (handlebars 6.4: register_escape_fn sets a registry-wide
escaper; {{& }} and triple-brace skip escaping). For HTML that is
correct and safety-relevant. For generated Rust or WGSL text it is a
corruption vector: type names and string literals containing &, <,
or quotes (doc comments, literals with entities) would be silently
mangled into invalid code. Escaping policy therefore cannot be a
core-wide constant, and cannot be left to template authors choosing
between {{ }} and {{& }} ad hoc.
Decision
The escaper is a property of the template set, set at registry construction:
- ssr-html sets: handlebars' default HTML escaper stays on as the
registry default. Template authors opt out per expression with
{{& }}only for deliberately-trusted markup (rare, reviewed like source). This is a rendering-safety posture for user-supplied page data, aligned with alkforge serving hostile traffic. - Code target sets (bast-rust-codecs, call-handlers, wgsl): the
registry registers a no-op escaper; templates use plain
{{ }}throughout. Codegen output is escaped at the source (literals are quoted properly by the builder), never by entity mangling. - The core provides both policies; choosing one is part of defining a template set (targets.md table).
Consequences
- Positive: codegen correctness is by construction; SSR safety is by construction. The one-registry-per-set rule (core.md) makes the policy non-negotiable per set instead of per-template.
- Positive: matches handlebars' actual API shape (escaper is registry-scoped), so no wrapper machinery.
- Negative:
{{& }}in code sets becomes unnecessary and its use in engine-owned code templates is forbidden by posture (it would double-escape nothing but signals confusion); reviewers of template changes watch for it. - Negative: an HTML set with a no-op escaper would be a silent XSS surface; mitigated because posture 1 is fixed by this ADR and consumer sets (alkforge) build through the same documented policy.
References
- core.md (registry construction)
- ADR-006 (SSR consumer posture)
- targets.md (escaper column of the target summary)
- handlebars-rust registry API (vendored checkout,
register_escape_fn)