From b154b65bed37971eb8039b8cd6c27ae1678cb010 Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Fri, 6 Nov 2026 18:27:28 +0000 Subject: [PATCH] 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 --- tasks/implementation/review-wave-1.md | 66 +++++++++++++ tasks/implementation/w1-engine-fixtures.md | 72 ++++++++++++++ tasks/implementation/w1-error-model.md | 63 ++++++++++++ tasks/implementation/w1-manifest.md | 81 ++++++++++++++++ .../w1-registry-construction.md | 96 +++++++++++++++++++ tasks/implementation/w1-render-entry.md | 68 +++++++++++++ 6 files changed, 446 insertions(+) create mode 100644 tasks/implementation/review-wave-1.md create mode 100644 tasks/implementation/w1-engine-fixtures.md create mode 100644 tasks/implementation/w1-error-model.md create mode 100644 tasks/implementation/w1-manifest.md create mode 100644 tasks/implementation/w1-registry-construction.md create mode 100644 tasks/implementation/w1-render-entry.md diff --git a/tasks/implementation/review-wave-1.md b/tasks/implementation/review-wave-1.md new file mode 100644 index 0000000..1b3ffe2 --- /dev/null +++ b/tasks/implementation/review-wave-1.md @@ -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. diff --git a/tasks/implementation/w1-engine-fixtures.md b/tasks/implementation/w1-engine-fixtures.md new file mode 100644 index 0000000..7e14d13 --- /dev/null +++ b/tasks/implementation/w1-engine-fixtures.md @@ -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. diff --git a/tasks/implementation/w1-error-model.md b/tasks/implementation/w1-error-model.md new file mode 100644 index 0000000..6890853 --- /dev/null +++ b/tasks/implementation/w1-error-model.md @@ -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. diff --git a/tasks/implementation/w1-manifest.md b/tasks/implementation/w1-manifest.md new file mode 100644 index 0000000..b1cd031 --- /dev/null +++ b/tasks/implementation/w1-manifest.md @@ -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. diff --git a/tasks/implementation/w1-registry-construction.md b/tasks/implementation/w1-registry-construction.md new file mode 100644 index 0000000..bca3a78 --- /dev/null +++ b/tasks/implementation/w1-registry-construction.md @@ -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. diff --git a/tasks/implementation/w1-render-entry.md b/tasks/implementation/w1-render-entry.md new file mode 100644 index 0000000..c19a74a --- /dev/null +++ b/tasks/implementation/w1-render-entry.md @@ -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.