chore(tasks): decompose wave 1 into implementation tasks

Five implementation tasks plus the gate, per the plan's decomposition
record. Deliberately sequential logistics: all tasks edit the same
small module tree and lib.rs re-exports, each consuming the previous
task's surface.

- w1-manifest: dep pins (handlebars 6.4 default features, thiserror),
  alktype removal, bast-codegen feature name declared
- w1-error-model: the one public enum (registration/parse/render)
- w1-registry-construction: set builder, forced strict mode, escaper
  policy, typed registration, helper seam; two decisions to record
- w1-render-entry: both sinks, deterministic, strict errors
- w1-engine-fixtures: ssr-shaped + code-shaped doubles, golden-pinned
- review-wave-1: the cheap-place gate; dispositions the four recorded
  interpretations

Four interpretations flagged for the gate (alktype removal vs
optional-behind-feature, context-build arms, partial render-target
enforcement, naming validation); recorded in the plan.

Verification: taskgraph validate clean (14 tasks), waves + deps
acyclic, em-dash grep clean
This commit is contained in:
glm-5.3-flash committed 2026-11-06 18:27:28 +00:00
1 parent 04e961d689
commit b154b65bed
6 files changed
+446

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
---
id: review-wave-1
name: "Wave 1 gate: the core surface"
status: pending
depends_on: [w1-manifest, w1-error-model, w1-registry-construction, w1-render-entry, w1-engine-fixtures]
scope: narrow
risk: low
impact: phase
level: 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 in `src/`, 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 fills this with the verdict, booked findings, and the
> dispositions of the interpretations above.
## Summary
> Gate fills this on completion.
@@ -0,0 +1,72 @@
---
id: w1-engine-fixtures
name: "Wave 1: engine fixture doubles (ssr-shaped and code-shaped sets)"
status: pending
depends_on: [w1-render-entry]
scope: narrow
risk: low
impact: component
level: implementation
---
## Description
Land the minimal in-crate fixture doubles that every wave-2 proof runs
through (the alkssh wave-1 doubles move: the deep harness lands wave
2; the doubles land now). They are the first full-round-trip exercise
of the shipped core: typed context in, rendered text out, through the
public API only.
- **ssr-shaped fixture set**: a small page template composing fragment
partials (header/footer shape), registered through the public API,
HTML escaper active, typed context (derived `Serialize`) built by a
Rust builder function (the ADR-004 shape: kind mapping and structure
decisions live in builder code, templates stay dumb conditionals
over context flags). Golden render tests pin the output (AGENTS.md
convention 7 discipline starts here: changing a fixture template
changes the pinned expectations, deliberately).
- **code-shaped fixture set**: a small set with the no-op escaper and
a typed context whose arrays are document-ordered: `serde_json`'s
`preserve_order` is load-bearing (convention 5); the fixture proves
insertion order drives rendered order (an ordered list context, not
a map, exercising the array path handlebars iterates).
- Fixture placement per the wave-1 conventions: fixtures live in the
repo as pinned code assets (`include_str!` files or inline
literals), versioned, never throwaway strings; test binaries own
their fixture modules so wave-2 proofs can grow them without
single-writer collisions.
- Establish the naming convention for fixture template kinds
(convention 9 posture, consistent with whatever the registry task
decided).
## Acceptance Criteria
- [ ] ssr-shaped set renders through the public API with HTML
escaping; page composes registered fragment partials; golden
tests pin output
- [ ] code-shaped set renders with no-op escaping; document-order
arrays render in insertion order (the `preserve_order` test)
- [ ] both sets use typed contexts built by Rust builder functions
per ADR-004
- [ ] fixtures are embedded code assets, goldens committed and
asserted
- [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check` pass
## References
- docs/architecture/core.md §Context conventions, §Template assets
- docs/architecture/decisions/001-one-core-template-set-targets.md
(the generality claim these fixtures will help prove)
- docs/architecture/decisions/004-typed-contexts-builders-not-templates.md
- docs/architecture/emit.md (postures B and A: the shapes these
fixtures model)
- docs/plans/implementation.md (wave 2 consumes these doubles)
## Notes
> Agent fills this during implementation.
## Summary
> Agent fills this on completion.
+63
View File
@@ -0,0 +1,63 @@
---
id: w1-error-model
name: "Wave 1: the public error model"
status: pending
depends_on: [w1-manifest]
scope: single
risk: low
impact: project
level: implementation
---
## Description
Land the crate's one public error enum (core.md §Error model; this is
the contract every later task and every consumer maps against):
- `pub enum Error` in `src/error.rs`, via thiserror (AGENTS.md
convention 2), covering the engine-owned operations core.md names:
registration, template parsing, and rendering. A malformed template
(registration-time parse failure) and a failed render are data:
typed variants wrapping the substrate's `handlebars::TemplateError`
and `handlebars::RenderError`. No panics, no `unwrap`/`expect`
outside tests.
- No catch-alls: every variant names the operation that failed, and
every engine-owned failure path maps to exactly one variant. No
lossy flattening: the underlying substrate error stays reachable
(source chain via thiserror `#[from]`/`#[source]`), for consumers
that need detail (alkforge maps render failure to an error page).
- **No context-build arms in this scope**: no engine-owned template
set ships in v0.1.0, and consumer context builders own their errors
(ADR-008). The arm lands additively with the first engine-owned
set. Recorded interpretation, flagged for the wave-1 gate.
- `src/lib.rs` re-exports the enum per convention 10; doc comments on
the public surface (no inline `//` comments per convention 1).
## Acceptance Criteria
- [ ] `pub enum Error` with registration/parse and render variants
wrapping `TemplateError`/`RenderError`; the substrate error
stays reachable through the source chain
- [ ] no catch-all variant; registration, parse, and render failures
each map to exactly one variant
- [ ] no `unwrap`/`expect` in `src/`; no panics
- [ ] re-exported from `src/lib.rs`; doc comments on public items
- [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check` pass
## References
- docs/architecture/core.md §Error model
- docs/architecture/decisions/002-handlebars-substrate-strict-mode.md
(decision 3: typed fallible registration)
- docs/architecture/decisions/008-template-set-ownership.md
(consumer builders own their errors)
- docs/plans/implementation.md (the context-build-arms interpretation)
## Notes
> Agent fills this during implementation.
## Summary
> Agent fills this on completion.
+81
View File
@@ -0,0 +1,81 @@
---
id: w1-manifest
name: "Wave 1: manifest landing (dep pins, alktype removal, feature graph)"
status: pending
depends_on: []
scope: single
risk: low
impact: project
level: implementation
---
## Description
Land the dependency posture everything else in the crate builds
inside. This is the ADR-002/ADR-009 manifest landing; no other wave-1
task starts before it.
- Pin the substrate: `handlebars = "6.4"` from crates.io on the
substrate's default features. The crate's `default = []` posture
means `dir_source` and `script_helper` are off-limits (filesystem,
rhai; ADR-002 decision 4, ADR-003) and must stay un-enabled:
verify no `walkdir` / `rhai` enters the dependency tree (`cargo
tree`), rather than assuming it.
- `serde` + `serde_json` with `preserve_order` stays as is
(load-bearing, AGENTS.md convention 5), `thiserror = "2"` for the
error model task.
- **Remove alktype from `[dependencies]`**: it is listed
unconditionally on main today, but ADR-009 decision 1 ships it only
behind the `bast-codegen` feature, and no engine-owned template set
ships in v0.1.0 (the bast-rust-codecs activation is queued behind
ssr-html and wgsl-jit; see `docs/plans/implementation.md` scope
authority). A dep behind a feature that gates nothing is a dead
entry; the optional alktype dep and the feature's payload land
together at the target's activation. This interpretation is
recorded in the plan and flagged for the wave-1 gate.
- Declare the feature graph: `[features] bast-codegen = []` (name
reserved per ADR-009; gates nothing yet; the first consuming code
arrives with the bast-rust-codecs set, a future wave).
- Update package metadata to the text-generation framing (ADR-001):
the description and keywords currently read "codegen over BAST
documents"; the crate's honest v0.1.0 description covers the text
engine. Keep `version = "0.1.0"` (the publish task verifies, never
bumps).
## Acceptance Criteria
- [ ] `handlebars` pinned `6.4`; `cargo tree` shows no `walkdir` and
no `rhai` (off-limits features provably not pulled in)
- [ ] `alktype` absent from the dependency tree; `bast-codegen`
declared under `[features]` gating nothing
- [ ] package description/keywords updated to the text-generation
framing; version still 0.1.0
- [ ] `cargo build --target wasm32-unknown-unknown --release` passes
on the landed dep set (ADR-003 validation posture)
- [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check`, `taskgraph validate` pass
## References
- docs/plans/implementation.md (scope authority; the alktype
interpretation)
- docs/architecture/overview.md §Dependencies
- 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
(especially decision 1 and item 4)
- docs/architecture/decisions/010-library-only-no-cli.md
## Notes
> Design: the task removes alktype rather than leaving it behind
> `optional = true`: with no consuming code, `optional = true` + an
> auto-created implicit `alktype` feature (or a `bast-codegen` alias
> to nothing) is the same dead entry with more moving parts. The
> re-add lands with the bast-rust-codecs template set as
> `bast-codegen = ["dep:alktype"]`. Flagged for the wave-1 gate to
> confirm against ADR-009 item 4.
## Summary
> Agent fills this on completion.
@@ -0,0 +1,96 @@
---
id: w1-registry-construction
name: "Wave 1: registry construction (strict mode, escaper policy, typed registration)"
status: pending
depends_on: [w1-error-model]
scope: narrow
risk: medium
impact: project
level: implementation
---
## Description
Land the set builder, the first and most contract-heavy component of
the core (core.md §Registry construction; ADR-002 decision 6):
- **One registry per template set**, constructed through alkgen's API:
the builder takes a set's identity options and wraps one handlebars
registry scoped to that set. Strict mode, escape policy, and the
partial namespace are registry-scoped properties; the one-registry-
per-set rule is what makes them enforceable rather than advisory
(ADR-002 decision 6). **Strict mode is forced on** at construction,
never parameterizable. A test pins the enforcement (a registry
built through the API cannot render missing variables as silent
empty output).
- **Escaper as an explicit per-registry choice** (ADR-005): the build
parameter covers the two shipped postures: handlebars HTML escaping
for ssr-shaped sets and a no-op identity posture for code sets.
Decide the mechanism against handlebars 6.4's escape-fn surface
(`register_escape_fn` vs disabling built-in HTML escape) and record
it in Notes. Both postures get a rendering test pinning their
behavior.
- **Typed fallible registration**: register templates and partials by
name + text. Malformed template text produces the wave-1 error
enum's registration variant, never a panic (ADR-002 decision 3).
Registration accepts externally supplied `&str` either as inline
literals or caller text (ADR-008 consumer path: consumers register
through the same machinery; no filesystem access anywhere, so
`dir_source` is unreachable by construction).
- **Helpers**: the registration seam exists (per-set helper
registration pass-through), but the core registers none by default
(core.md §Helper policy): helpers see text, are pure and
deterministic, and are owned by their template sets; the purity
statement is a documented convention, not runtime enforcement.
- **Decide + record (both flagged for the wave-1 gate)**:
1. How the render-name rule ("the name argument resolves to a
registered template, never a bare partial"; core.md) is enforced,
given handlebars' registry is a single namespace (registering a
partial is template registration under the hood). Choose set-
level bookkeeping (a set type that knows which registered names
are templates) or convention plus tests, and pin it.
2. Whether template naming conventions (AGENTS.md convention 9:
lowercase kebab/snake, one name per template kind) are validated
at registration or enforced by tests only.
- `src/lib.rs` re-exports the public surface.
## Acceptance Criteria
- [ ] set builder with forced strict mode; an enforcement test proves
a missing variable renders as a typed error, never empty output
- [ ] escaper choice explicit per registry (HTML and no-op postures),
each pinned by a rendering test
- [ ] template and partial registration fallible and typed; malformed
template text yields the error enum, never a panic
- [ ] externally supplied text registers identically (the ADR-008
consumer path exercised)
- [ ] helper registration seam exists; zero helpers registered by
default
- [ ] the two decisions recorded in Notes; both flagged in the task
Summary for the gate
- [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check` pass
## References
- docs/architecture/core.md §Registry construction, §Template assets,
§Helper policy
- docs/architecture/decisions/002-handlebars-substrate-strict-mode.md
(decisions 3, 5, 6)
- 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/plans/implementation.md (the partial-enforcement and naming
interpretations)
- /workspace/@alkdev/alkminer/reference/alkminer-v1/src/compute/
kernel.rs (the strict-registry precedent)
## Notes
> Agent fills this during implementation. The two decisions (partial
> enforcement mechanism, naming validation posture) must be recorded
> here.
## Summary
> Agent fills this on completion.
+68
View File
@@ -0,0 +1,68 @@
---
id: w1-render-entry
name: "Wave 1: the render entry point (both sinks, deterministic, strict)"
status: pending
depends_on: [w1-registry-construction]
scope: narrow
risk: low
impact: project
level: implementation
---
## Description
Land the render entry point (core.md §Render entry point), the
component every consumer actually calls:
- **Synchronous and deterministic**: render a registered template by
name over a serde-serializable context. Same registry contents plus
same context produce byte-identical text (ADR-003): test-pinned
here (repeated renders over cloned and rebuilt registries), deepened
by wave 2's battery. No I/O, no time reads, no hash-map iteration
order leaking into output.
- **Two sinks**: render into a String, and render into an
`io::Write` target (both wasm-clean; the substrate exposes both,
`render` and `render_to_write`; the core passes them through so a
large page or large generated file need not force intermediate
allocation). Strict errors through both sinks surfacing the
wave-1 error enum.
- **Templates are the render targets**: rendering by a partial's name
is rejected per the wave-1 registry task's recorded mechanism
(bookkeeping or convention plus tests; whatever landed there, the
render entry is where the behavior is visible and tested).
- **Strict errors**: a missing context variable in strict mode is a
typed error through the public enum, never silent-empty output
(ADR-002 decision 2).
- `src/lib.rs` re-exports the surface.
## Acceptance Criteria
- [ ] String-sink and `io::Write`-sink render entry points, both
surfacing the public error enum
- [ ] determinism pinned by tests (byte-identical repeated renders,
including across registry rebuilds)
- [ ] strict-mode missing-variable render is a typed error in both
sinks, never empty output
- [ ] render-by-partial behavior matches the registry task's recorded
decision and is pinned by a test
- [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check` pass
## References
- docs/architecture/core.md §Render entry point
- docs/architecture/decisions/002-handlebars-substrate-strict-mode.md
- docs/architecture/decisions/003-core-posture-sync-wasm-deterministic.md
- docs/architecture/emit.md §Cross-posture obligations (the
determinism row every posture leans on)
- /workspace/@alkdev/alkminer/reference/alkminer-v1/src/compute/
kernel.rs (render-then-compile precedent: purity for downstream
caching)
## Notes
> Agent fills this during implementation.
## Summary
> Agent fills this on completion.