Files
alkgen/docs/architecture/decisions/005-escape-strategy-per-template-set.md
glm-5.3-flash a91ea2ec89 docs(architecture): phase 1 base: text-generation core specs, ADRs 001-010, OQ tracker
- 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
2026-11-06 08:18:43 +00:00

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:

  1. 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.
  2. 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.
  3. 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)