- 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.5 KiB
ADR-001: One text-generation core; targets are template sets plus emit layers
Status
Accepted
Context
Phase 0 scoped the crate as "codegen over BAST documents" with static file emission into downstream projects as the product. Two things dissolved that framing:
- The 2026-11-06 unpause arrived because alkforge (family ADR-002) needs server-side HTML generation, a runtime rendering use with no schema, no plans, and no file emission.
- WGSL generation for alkjs (alkminer-v1's ShaderBuilder precedent) and Rust codec generation (reader POC) are provably the same machinery: contexts plus templates plus partials produce text.
Three data points, one shape. Treating SSR as a special case baked into a codegen-shaped core would either fork the machinery or deform it.
Decision
alkgen's core is a text-generation engine: it owns registry construction (strict mode, first-class partials, per-registry escape policy), context conventions, a deterministic synchronous render entry point, and typed errors. A target is a template set plus a context shape plus an emit posture. Shared core types encode none of the targets: no reader/writer assumptions, no HTML assumptions, no WGSL assumptions.
Validity pinning is per template set: render tests for each set pin what "valid output" means for it (Rust compilability, HTML well-formedness, WGSL syntax are set-level property tests, never core concepts).
The dev-tool identity is demoted from "the product" to "posture A of the emit layer" (emit.md).
Consequences
- Positive: three consumers, one maintenance surface; the SSR branch acts as a standing overfitting test; the WGSL path needs no core work beyond template sets when it arrives.
- Positive: the demotion is cheap because it changes nothing required by branches 1 and 2; their needs are now a posture, not the crate's definition.
- Negative: generalization can hide real per-target needs until late. Mitigation: the first consumer is SSR (the least codegen-like), so the core is exercised against the differentiator immediately.
- Negative: core APIs must stay small enough to remain "everything-for-nothing" (no target vocabulary), which constrains helpers and conveniences that would drift toward one target.
References
- targets.md (the instance table)
- emit.md (the posture layer)
- Phase 0, section "Core generalization steer" and the framing
correction (
docs/research/phase-0.md) - ADR-006 (SSR posture), ADR-007 (codec contract)