Files
alkgen/tasks/implementation/w2-conformance-battery.md
glm-5.3-flash 73fdadafdd chore(tasks): wave-2 review gate passes green
- 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
2026-11-07 05:43:43 +00:00

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
w2-ssr-proof
w2-code-proof
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::Render through both sinks (render and render_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::Registration and never survives as a half-registered name; render-time failures (unknown template name, partial name as render target, strict missing variable, unknown helper) resolve to Error::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), render and render_to_write produce 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-unknown release, 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 (no dir_source / script_helper features enabled, per ADR-002) is re-verified on the wave-2 tree.
  • The feature-matrix posture (ADR-009, plan wave-2 bullet): cargo test with default features and with --all-features both pass (they coincide today since bast-codegen gates 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::Write sinks 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 test with default and with --all-features both 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_template and register_partial text resolves to Error::Registration and 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 to Error::Render through both sinks; partial-as-render-target resolves to Error::Render through both sinks; strict missing variable resolves to Error::Render through both sinks; unknown helper resolves to Error::Render naming 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 render and render_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_helper in the final dep set) made explicit; (2) cargo test --all-features joined the command list and the feature-matrix posture is documented honestly: default and --all-features coincide today because bast-codegen gates 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 to expect_err and a shared assert_both_sinks helper so clippy's err_expect and single_element_loop lints 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 handlebars shows only handlebars v6.4.4 default (no dir_source, no script_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.