- Landed core verified against core.md's six components and ADR-002/003/004/005/008/009 postures, checked in code and tests rather than prose - All four wave-1 interpretations confirmed: alktype removal (ADR-009 decision 1, tree-verified), context-build arms absent by design (ADR-008), render-name rule via set-level bookkeeping, naming validation by tests only - Verification set re-run clean: cargo test (27), clippy -D warnings, fmt, doc, wasm release build, taskgraph validate (14) - Gate verdict green: wave-2 decomposition unblocked; three non-blocking reminders carried forward (freeze-inventory non_exhaustive decision, standing wasm pin, ADR-009 CI matrix)
9.9 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| review-wave-1 | Wave 1 gate: the core surface | completed |
|
narrow | low | phase | review |
Description
The cheap-place gate. The registry construction surface, the render entry semantics, and the error enum are one-way doors in practice: every later task implements against them, and the published crate freezes the public API. A shape error found here costs a task revision; found after consumers integrate it costs churn. The gate reads the landed surface against the architecture line by line and disposes of the wave-1 interpretations recorded in the plan.
Acceptance Criteria
- Landed core checked against
core.md's component map (registry construction, template assets, context conventions, helper policy, render entry point, error model); no unrecorded divergences - ADR postures verified in code: ADR-002 (strict mode forced, typed fallible registration, embedded assets, one registry per set), ADR-003 (sync, no filesystem, deterministic), ADR-004 (typed contexts, kind mapping in builders exercised by the fixtures), ADR-005 (escaper per registry)
- The four wave-1 interpretations each receive a disposition: alktype removal (confirmed against ADR-009 or overturned), context-build error arms, partial-not-render-target enforcement, template naming validation
- Conventions audit: no
unwrap/expect/panics insrc/, no unsafe, no async, no filesystem access, doc comments only on public API, no em dashes in prose - Verification set:
cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --check,cargo doc --no-deps,cargo build --target wasm32-unknown-unknown --release,taskgraph validate; all clean - Gate verdict recorded in Notes: wave-2 decomposition unblocked, or blockers + booked findings listed
References
- docs/architecture/core.md (the wave's spec)
- docs/plans/implementation.md (wave 1 section: the interpretations to disposition)
- docs/architecture/decisions/002-handlebars-substrate-strict-mode.md
- docs/architecture/decisions/003-core-posture-sync-wasm-deterministic.md
- docs/architecture/decisions/004-typed-contexts-builders-not-templates.md
- docs/architecture/decisions/005-escape-strategy-per-template-set.md
- docs/architecture/decisions/008-template-set-ownership.md
- docs/architecture/decisions/009-feature-gated-template-sets.md
Notes
Gate verdict: GREEN. Wave-2 decomposition unblocked. No blockers, no
booked findings, no inline fixes required; the landed surface matches
the architecture on every point read. Gate review performed
2026-11-06 over the four implementation commits (c678615, 02037c4,
22f32e6, e361b41, 14d8a49) by reading the full diff of the landed
surface (src/lib.rs, src/error.rs, src/escape.rs, src/registry.rs,
tests/, Cargo.toml, the task notes) against core.md and
ADR-002/003/004/005/008/009.
Component map check (core.md): all six components present and
shaped as specified. Registry construction: TemplateSet wraps one
handlebars registry per set (ADR-002 decision 6), strict mode set
unconditionally inside the constructor with no API parameter (pinned
by an enforcement test), fallible typed registration for templates and
partials, &str-only API so no filesystem is reachable by
construction. Template assets: fixtures are include_str!/inline
text versioned in-repo with goldens from day one. Context
conventions: typed Serialize structs built by Rust builders in both
fixtures, kind mapping (writer_call, rust_struct_name,
SectionKind) lives in builder code, templates condition on flags
only. Helper policy: seam exists, zero default helpers (pinned by the
missing-helper typed-error test), purity stated as a documented
convention. Render entry: both sinks, deterministic (pinned across
repeated renders and across a rebuilt registry registering in
opposite order), strict errors typed. Error model: one public enum,
two variants, no catch-all, substrate errors reachable through the
source chain (pinned by downcast tests). No unrecorded divergences
found.
ADR posture check (in code, not in prose):
- ADR-002: strict mode forced and unparameterizable; registration
propagates parse failures as
Error::Registration; assets embedded (dir_source/script_helperprovably absent:cargo treeinspection shows zerowalkdirand zerorhaihits); one registry per set; partials first-class. Verified. - ADR-003: no async, no threads, no
std::time/std::env/std::fsanywhere in the source; determinism pinned by tests including the registration-order independence row; wasm build of the landed dep set passes. - ADR-004: typed contexts; kind mapping in builders (exercised by
both fixture builders); document order driven by insertion-order
arrays, with the
preserve_orderfeature itself pinned active by a test. Verified. - ADR-005: escaper is an explicit per-set construction-time choice
(
EscapePolicy::{Html, NoOp}), not swappable throughTemplateSet; the Html posture leaves the substrate default untouched and NoOp registersno_escape; both postures pinned by rendering tests. Verified. - ADR-008: caller-supplied
Stringtext registers through the identical methods, pinned by test; no second-class consumer path. Verified. - ADR-009:
bast-codegen = []declared, gates nothing; alktype absent from the tree; package metadata moved to the text-generation framing; version stays 0.1.0. Verified.
Interpretation dispositions:
- alktype removal (vs optional-behind-feature): CONFIRMED against
ADR-009. ADR-009 decision 1 mandates default features ship the
core only with no alktype, and with no engine-owned set in scope an
optional = trueentry would sit behind a never-enabled feature (plus the auto-created implicitalktypefeature): the same dead entry with more moving parts, exactly as the recorded interpretation argues. ADR-009 item 4 anticipated the move as implementation work, and the re-add shape is already pinned by the task notes (bast-codegen = ["dep:alktype"]at target activation). Dep tree verified clean. No overturn. - Context-build error arms: CONFIRMED (absent, correctly). No
engine-owned template set ships in v0.1.0 and consumer builders
own their errors (ADR-008), so there is no failure the arm could
represent; it lands additively with the first engine-owned set,
pre-publish. The
#[non_exhaustive]question is deliberately open and recorded in the error-model task notes; the gate adds a reminder below for the wave-3 freeze inventory to decide it explicitly. - Partial-not-render-target enforcement: CONFIRMED. Set-level
bookkeeping was the right call: the substrate's
register_partialis literallyregister_template_string, so convention-plus-tests would have left the render entry unable to enforce core.md's render-name rule. The behavioral rejection (partial name and unknown name both rejected as typedError::Renderbefore the substrate is consulted) is pinned in both sinks, from the unit tests and from the consumer-shaped ssr fixture test. The mechanism is documented on the type. - Template naming validation: CONFIRMED. Tests-only is correct: validating names at registration would bake alkgen's house style into a consumer-blind API (ADR-001) and contradict ADR-008 machinery equality (consumers would get rejections the core has no mandate to police); "one name per kind" is semantic, not mechanical, per the registry task's recorded reasoning. Fixture kinds follow convention 9 and are reviewed like source.
Conventions audit: zero unwrap/expect/panics/unsafe/async/
filesystem/time/env in non-test lib code (all expect hits are inside
#[cfg(test)] modules, permitted); zero inline // comments in
src/; doc comments on all public items; no em dashes in src/, tests/
, docs/, tasks/, README, or commit messages (the plan's grep command
in AGENTS.md matches its own pattern text, a self-reference, not a
prose violation; vendored .opencode/node_modules/ is out of scope
per convention 12). serde_json preserve_order untouched and
explicitly pinned active by a test. Deterministic rendering holds (no
HashMap iteration order reaches output; the registry bookkeeping is
name-resolution only).
Verification set (all green, re-run by the gate on the landed
tree): cargo test (19 lib + 5 code-fixture + 3 ssr-fixture
passed), cargo clippy --all-targets -- -D warnings,
cargo fmt --check, cargo doc --no-deps (warning-free),
cargo build --target wasm32-unknown-unknown --release,
taskgraph validate (14 tasks valid), plus the ADR-009 tree
inspection (cargo tree: zero walkdir/rhai/alktype; handlebars
resolves 6.4.4).
Reminders carried forward (non-blocking): (a) the wave-3 freeze
inventory decides the enum's #[non_exhaustive] posture explicitly;
(b) the wave-2 conformance battery should keep pinning the wasm build
standing rather than relying on ad-hoc gate runs; (c) ADR-009's CI
feature matrix (core, core+bast-codegen, wasm of both) lands when the
first feature starts gating something.
Summary
Wave-1 gate passed green. The shipped core surface
(EscapePolicy, TemplateSet, Error) matches core.md's six
components and the ADR-002/003/004/005/008/009 postures on every
point read; strict mode, typed registration, deterministic two-sink
rendering, escaper policy, and the consumer machinery-equality path
are all pinned by tests rather than asserted. All four wave-1
interpretations confirmed: alktype removal against ADR-009 decision 1
(tree-verified), context-build arms absent by design, render-name rule
via set-level bookkeeping, naming by tests only. Conventions audit and
the full verification set clean; 27 tests pass, wasm release build
clean. Wave-2 decomposition unblocked, no blockers. Three
non-blocking reminders carried forward (noted above).