- 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
3.1 KiB
3.1 KiB
ADR-002: handlebars-rust 6.4 substrate: strict mode, typed errors, embedded templates
Status
Accepted
Context
The render substrate was settled in Phase 0 (validated stack, wasm playground shipped by handlebars itself), but the substrate posture was distributed across conventions, phase-0 prose, and two precedent codebases (typebox-rs and the reader POC). It needs one decision record, because posture is what typebox-rs got wrong (unwrap-based registration, no strict mode, silent empty output) and what the POC validated as right.
Decision
- Substrate: handlebars-rust pinned to
6.4from crates.io. The vendored checkout (/workspace/handlebars-rust, one commit past v6.4.4) is reading material and an emergency patch source only, not the dependency. - Strict mode always: every registry the library constructs or exposes sets strict mode on. A missing context variable is a render error, never silently-empty text (AGENTS.md convention 6; alkminer-v1 ShaderBuilder precedent; the POC caught a stale context key through strict mode at build time).
- Registration is fallible and typed: template and partial registration surfaces propagate parse errors as typed errors (no unwrap). Malformed templates and malformed inputs are data, not crashes (AGENTS.md convention 2).
- Assets are embedded code: templates come from
const &strorinclude_str!;dir_source(filesystem) andscript_helper(rhai) features are off-limits in lib code; templates are pinned by golden render tests (AGENTS.md convention 7). - Partials are first-class: registration supports partials alongside templates from day one (branch-3 (template-JIT) requirement priced in while free; also the SSR fragment unit).
- One registry per template set: strict mode, escape policy (ADR-005), and partial namespaces are registry-scoped properties, so a single per-set registry is what makes them enforceable rather than advisory. Cross-set asset reuse is a source-level include, never registry surgery. Consumer sets use the same construction path (machinery equality, ADR-008).
Consequences
- Positive: strict mode turns silent-output bugs into build-time errors at the generator, close to the cause, instead of compile or runtime bugs in consumers; typed registration keeps the no-panic posture intact for hostile inputs.
- Positive: the embedded-asset rule keeps lib code wasm-clean and reproducible.
- Negative: strict mode can reject templates for missing-but-defaulted
keys; the remedy is context shape and template guards (
{{#if}}), the pattern F-3 validated, not a strict-mode escape hatch. - Negative: pinning to a crates.io version means substrate bug fixes ride the handlebars release train; the vendored checkout bounds the emergency case.