- verdict GREEN: wave-3 decomposition unblocked; no blockers, no booked findings, zero core changes across wave 2 (verified by reading the three commits' file stats) - ssr proof checked line by line against emit.md posture B: machinery equality, fragment composition with render-name rule, escape and raw-slot goldens, typed error-page mapping, determinism, hygiene - code proof checked line by line against emit.md postures A/C and ADR-003: document order vs sort/reverse, no-op escaping, rebuild and registration-order determinism, two-sink equality; banner gap honest - stop-and-book ledger clean; ADR-001 generality claim holds on real code (both postures served with no core exceptions) - conformance battery verified as the standing set; AGENTS.md carries the standing wasm build and feature-matrix postures - conventions audit and full verification set re-run clean (51 tests, clippy, fmt, doc, wasm release, tree inspection, taskgraph) - inline fix: checked the battery task's completed-but-unchecked acceptance boxes Verification: cargo test (51 passed), cargo test --all-features, cargo clippy --all-targets -- -D warnings, cargo fmt --check, cargo doc --no-deps, cargo build --target wasm32-unknown-unknown --release, taskgraph validate (18 tasks), handlebars tree default-only
10 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | ||
|---|---|---|---|---|---|---|---|---|---|
| w2-conformance-battery | Wave 2: the conformance battery (strict-mode matrix, error surface, two sinks, wasm, feature matrix) | completed |
|
moderate | low | phase | implementation |
Description
The standing verification set for the core, established as code and as process: the rows the core's consumers (and every later wave) lean on, collected in one standing test binary so future waves grow rows there rather than scattering them. Rows already pinned by wave-1 unit tests may be referenced and re-exercised, not duplicated blindly; the battery's job is the standing home plus the rows not yet pinned.
- Strict-mode behavior matrix (ADR-002, core.md): a missing
context variable is a typed
Error::Renderthrough both sinks (renderandrender_to_write), never silent output: top-level variables and variables nested in{{#each}}/{{#if}}branches both covered. The write sink's behavior on failure (typed error, no partial-write contract beyond what wave 1 pinned) is stated by the matrix rows. - Error-surface completeness (core.md error model, convention
2): every engine-owned failure path maps to exactly one typed
variant, with no catch-all: malformed template text at
registration resolves to
Error::Registrationand never survives as a half-registered name; render-time failures (unknown template name, partial name as render target, strict missing variable, unknown helper) resolve toError::Render; hostile context values rendered through both sinks produce typed data or clean output, never panics. No input the API accepts reaches a panic path. - Two-sink correctness rows (core.md): for each fixture posture
(ssr-shaped and code-shaped),
renderandrender_to_writeproduce byte-identical output on the same context. - The wasm build joins the verification list (ADR-003, wave-1
gate reminder (b)): the final wave-2 dep set builds clean to
wasm32-unknown-unknownrelease, and the wasm entry becomes a standing, unconditional line in the repo's verification command list (the AGENTS.md verification section) instead of an ad-hoc gate-only run. The tree inspection posture (nodir_source/script_helperfeatures enabled, per ADR-002) is re-verified on the wave-2 tree. - The feature-matrix posture (ADR-009, plan wave-2 bullet):
cargo testwithdefaultfeatures and with--all-featuresboth pass (they coincide today sincebast-codegengates nothing), and the coincide-today posture is documented honestly where the verification list lands, to be grown when the first feature starts gating something.
The stop-and-book rule applies here too, though no core change is expected: the battery is verification, and a battery row that cannot pass without a core change is a booked finding, not a workaround.
Acceptance Criteria
- a standing conformance test binary exists
(
tests/conformance.rs); the strict-mode matrix covers missing variables through both sinks (top-level and nested contexts) and asserts typed errors, never silent output - error-surface completeness rows: registration parse failure, unknown template, partial-as-render-target, strict missing variable, unknown helper, hostile context values: each maps to exactly one typed variant; no panic paths through either sink
- two-sink correctness rows: String and
io::Writesinks are byte-identical across the ssr-shaped and code-shaped fixture postures - the wasm build of the final wave-2 dep set passes release
build to
wasm32-unknown-unknown; the tree inspection shows no off-limits handlebars features enabled - the verification command list in AGENTS.md carries the wasm build as a standing, unconditional entry, and the feature-matrix coincide-today posture is documented there honestly (both edits recorded in Notes)
cargo testwithdefaultand with--all-featuresboth pass- full verification set green:
cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --check,cargo doc --no-deps,taskgraph validate
Checkbox state corrected by the wave-2 gate (2026-11-07): the work was done and verified but the boxes were left unchecked; see review-wave-2 Notes.
References
- docs/plans/implementation.md (wave 2, the conformance battery bullet)
- tasks/implementation/review-wave-1.md (gate reminder (b): wasm pinning standing, not ad-hoc)
- docs/architecture/core.md (render entry point, error model)
- docs/architecture/emit.md (the cross-posture obligations table this battery makes standing)
- docs/architecture/decisions/002-handlebars-substrate-strict-mode.md
- docs/architecture/decisions/003-core-posture-sync-wasm-deterministic.md
- docs/architecture/decisions/009-feature-gated-template-sets.md
- src/registry.rs, src/error.rs (the wave-1 unit-test rows the battery sits beside)
Notes
Implementation landed as tests/conformance.rs, a standalone test
binary (13 rows) in the wave-1/2 convention of one fixture module per
test binary. The battery reuses the proof fixture files
(tests/fixtures/ssr_proof/, tests/fixtures/code_proof/) rather
than restating them, and does not restate the proof binaries' goldens;
its rows assert the obligations themselves against fresh public-API
renders.
What landed, per the acceptance criteria:
- Strict-mode matrix: five rows. Top-level missing variable,
missing variable inside an
{{#each}}body, missing variable inside a taken{{#if}}branch, missing variable inside a taken{{else}}branch: each fails typed (Error::Render) through both sinks, with the detail strings naming the missing variables, and the present-variable case still rendering. Plus the write-sink on-failure row: a template that streams a prefix before failing leaves those bytes in the target ("prefix-ok\n"pinned) and returns the typed error; the row states the no-atomicity contract (consumers needing atomicity buffer first), which is what wave 1 pinned and nothing more. - Error-surface completeness: six rows, each mapping to exactly
one variant of the two (the enum has no catch-all by construction,
so variant assignment is asserted): malformed
register_templateandregister_partialtext resolves toError::Registrationand the name never survives half-registered, including the failed- overwrite case where the previous registration and its kind stay intact (re-exercised from wave 1's unit rows); unknown template name (including the empty name) resolves toError::Renderthrough both sinks; partial-as-render-target resolves toError::Renderthrough both sinks; strict missing variable resolves toError::Renderthrough both sinks; unknown helper resolves toError::Rendernaming the helper, through both sinks; hostile context values (script tags, quotes, braces, entity-lookalikes, emoji, NUL/tab/newline control bytes) render typed-clean through both sinks under both escape policies with two byte-pinned expectations. No input the battery sends reaches a panic path. - Two-sink correctness: two rows, one per fixture posture. The
ssr-shaped posture (ssr-proof page + fragments + caller-supplied
card text, HTML escaper) and the code-shaped posture (code-proof
writer + validator + the composed field-write-line partial, no-op
escaper, document-ordered fields) each produce byte-identical
output through
renderandrender_to_write, with posture- identity assertions (trusted raw slot / verbatim literals and document order) proving the exercised rows are the real postures. - AGENTS.md edits (both recorded here as required): (1) the
wasm release build is now a standing, unconditional line in the
verification command list (comment changed from "if deps/wasm-
relevant code changed" to "standing, unconditional", ADR-003), with
a paragraph explaining the posture and the feature-inspection
posture (no
dir_source/script_helperin the final dep set) made explicit; (2)cargo test --all-featuresjoined the command list and the feature-matrix posture is documented honestly: default and--all-featurescoincide today becausebast-codegengates nothing yet, and the list grows the distinct postures when the first feature starts gating something (ADR-009).
Verification run on the final tree:
cargo test: 19 lib + 13 conformance + 5 code_fixtures + 7 code_proof + 3 ssr + 4 ssr_proof, all pass.cargo test --all-features: identical (the coincide holds).cargo clippy --all-targets -- -D warnings: clean (after switching the battery toexpect_errand a sharedassert_both_sinkshelper so clippy'serr_expectandsingle_element_looplints stay silent).cargo fmt --check,cargo doc --no-deps: clean.cargo build --target wasm32-unknown-unknown --release: passes.- Tree inspection:
cargo tree --all-targets -f "{p} {f}" | grep handlebarsshows onlyhandlebars v6.4.4 default(nodir_source, noscript_helper). taskgraph validate: 18 tasks valid.- The standing em-dash grep: zero hits outside the grep command itself pre-existing in AGENTS.md (convention 12).
Stop-and-book: no core change was needed; every row passed against
the shipped wave-2 tree. One substrate observation surfaced while
authoring the hostile-values row and is dispositioned as data, not a
gap: handlebars treats the expression name raw as keyword-shaped
and never resolves it as a context variable ({{& raw}} renders
empty even in strict mode). The battery avoids the name and documents
why in a doc comment; no consumer behavior change is implied and no
core change is booked for v0.1.0.
Summary
The wave-2 conformance battery landed: tests/conformance.rs stands
as the standing verification home with 13 rows across the strict-mode
matrix (top-level, {{#each}}, {{#if}} both branches, write-sink
no-atomicity contract), error-surface completeness (six failure paths
each mapping to exactly one typed variant, no half registrations, no
panic paths, hostile data through both sinks and both escape
policies), and two-sink byte-equality over the ssr-shaped and code-
shaped fixture postures, re-exercised over the proof fixture files.
The wasm release build became a standing unconditional line in
AGENTS.md's verification list together with the feature-matrix
coincide-today posture (ADR-009) and the explicit handlebars feature-
inspection posture. Full verification set green, including wasm
release and --all-features. review-wave-2 is unblocked.