- w2-ssr-proof and w2-code-proof: the two consumer-shaped proofs, parallel by construction (own test binaries, own fixture modules per the wave-1 convention) - w2-conformance-battery: the standing verification set, ordered after the proofs so it consolidates over the final wave-2 tree and absorbs any stop-and-book findings; folds the wave-1 gate's reminder (b) (wasm build standing, not ad-hoc) - review-wave-2: the wave gate, depends on all three - decomposition record added to docs/plans/implementation.md with the logistic-ordering rationale; wave-2 status flipped to decomposed Verification: taskgraph validate (18 tasks valid); em-dash grep clean on all changed files
17 KiB
status, last_updated
| status | last_updated |
|---|---|
| open | 2026-11-06 (created at the start of implementation; wave 1 decomposed and its gate passed the same day; wave 2 decomposed immediately after) |
alkgen: Implementation plan
Wave-based decomposition of the architecture (docs/architecture/)
into units of implementable work. It follows the sibling pattern proven
on alkssh (/workspace/@alkdev/alkssh/docs/plans/implementation.md,
itself following alkgit/alkstore/alkblobs): decompose a wave, implement
it, review gate, decompose the next wave. Adopted deliberately as a
deviation from the SDD process's decompose-everything-upfront step: the
core wave's surfaces are what every later task and every consumer
implements against, the unactivated consumers (wgsl-jit,
bast-rust-codecs, call-handlers) can still reshape the queue, and
reviews sometimes change things downstream, so decomposing only the
next wave at a time keeps each session's task set reviewable and avoids
paying rework on tasks written too far ahead. The crate is smaller than
alkssh and the waves fewer, but the shape holds: the one-way doors (the
registry construction surface, the render entry semantics, the error
enum, and eventually the published API) are exactly the things worth
gating early.
Rhythm: decompose a wave, implement it, pass its review gate,
decompose the next wave. Task files live in tasks/implementation/
(frontmatter carries the categorical estimates; run taskgraph validate after touching anything under tasks/). Wave boundaries are
also review boundaries. Each wave's section carries a decomposition
record added when that wave actually decomposes, never written
upfront.
Scope authority
v0.1.0 publishes the crate: the shared text-generation core (ADR-001), proven fit for its first consumer: ssr-html, per the 2026-11-06 consumer steer (alkforge SSR jumps the queue and needs alkgen now). Concretely, v0.1.0 ships:
- the core's six components (
core.md): registry construction, template asset conventions, context conventions, the helper policy, the render entry point, the error model; synchronous, wasm-clean, and deterministic (ADR-003); strict mode always on (ADR-002); escaper choice per registry (ADR-005). - the consumer registration path (ADR-008): externally supplied template and partial text enters through the public API with machinery identical to engine-owned sets.
Engine-owned template sets stay out of these waves. The activation
queue (targets.md, per the consumer steer) runs ssr-html first, then
wgsl-jit, with bast-rust-codecs and call-handlers queued behind; none
of the three is unblocked in this scope: wgsl-jit waits on OQ-05 (the
alkjs GPU-layer work), bast-rust-codecs carries deferred OQ-01/02/03/09
plus its consumer integration story, call-handlers waits on OQ-04. The
bast-codegen feature name is declared in the manifest from the first
commit (ADR-009) but gates nothing until the set lands.
The ssr-html target is proven in-crate, not shipped. The target's template set is consumer-owned (ADR-008): alkforge's page templates live in alkforge, and alkforge's web-view design does not exist yet. This plan therefore proves the target through consumer-shaped fixture sets registering through the public API, the ADR-008 machinery-equality path: if the shipped core serves a consumer-shaped set with no core exceptions, the generality claim (ADR-001) holds on the real code, not just the POC. The real templates and their golden pins land in alkforge (family ADR-002 no-deformation rule: alkforge's asks flow in here as design input, never as forks).
The assembly stays out. alkforge's linking of the crate, its page design, its eager-cache-vs-render-per-request choice, its error-page mapping are downstream composition (ADR-006). The crate ships the engine, not a deployment. Library-only (ADR-010): no CLI, no binaries, single crate, no workspace split.
Version note: the manifest already carries 0.1.0; the publish task verifies rather than bumps.
The waves
Dependency logic in one line: core foundations (consumer-shaped proofs ∥ conformance battery) release.
| Wave | Contents | Depends on | Status |
|---|---|---|---|
| 1 | The core: manifest landing (dep pins, alktype removal, feature graph); the public error model; registry construction (strict mode, escaper policy, typed registration, helper seam); the render entry point (two sinks); engine fixture doubles | none | decomposed |
| 2 | The generality proofs on shipped code: the full ssr-shaped (posture-B) fixture through the public API; the code-shaped (posture-A/C) fixture with ordering and determinism invariants; the conformance battery (strict-mode matrix, error surface, two-sink correctness, wasm, feature matrix) | wave 1 | decomposed |
| 3 | Release readiness (README, CHANGELOG, rustdoc, dry-run, freeze inventory); the release gate; the publish | waves 1 + 2 | planned |
Wave 1: The core
The consumer-blind contract surface in code: everything later waves and every consumer registers and renders through. Nothing here needs a real consumer to test.
- Manifest landing (ADR-002, ADR-009): the dependency set lands
with implementation start:
handlebars = "6.4"from crates.io on default features (the crate'sdefault = [];dir_sourceandscript_helperare off-limits and must stay un-enabled, verified by tree inspection),serde+serde_jsonwithpreserve_order(load-bearing, convention 5),thiserror = "2". alktype leaves the dependency tree: it is listed unconditionally on main today, ADR-009 decision 1 ships it only behind the feature, and no engine-owned set lands in v1, so a dep behind a feature that gates nothing is a dead entry (the alkssh wave-1 rule: sibling deps land with their first consuming wave; interpretation recorded below). The feature graph is declared from the first commit:bast-codegennamed, gating nothing. Package metadata (description, keywords) updates to the text-generation framing (ADR-001). The landed dep set is verified wasm-clean (ADR-003's validation posture, re-landed for the new tree). - Error model: the one public enum for the crate (core.md), via thiserror, covering registration, template parsing, and rendering: malformed templates and malformed inputs are data (convention 2; alkcall accepts BAST from arbitrary peers, alkforge serves hostile traffic). No catch-alls; every engine-owned failure maps to exactly one variant. Context-build arms wait for the first engine-owned set (interpretation recorded below; consumer builders own their errors per ADR-008).
- Registry construction: the set builder (ADR-002 decision 6: one
registry per template set, what makes escaper policy and partial
namespaces enforceable rather than advisory): strict mode forced on
(never parameterizable), the escaper as an explicit per-registry
choice (HTML for ssr-shaped sets, no-op for code sets; ADR-005),
typed fallible registration of templates, partials (first-class from
day one, ADR-002 decision 5), and helpers, externally supplied text
accepted (ADR-008 consumers register through the same machinery), no
filesystem anywhere (the API takes
&str;dir_sourceunreachable; ADR-003). - Render entry point: synchronous deterministic render of named
templates over serde-serializable contexts (core.md): both sinks
(String and
io::Write; the substrate exposes both,renderandrender_to_write), strict errors surfaced as data (a missing variable is a typed error, never silently-empty output), templates as the only render targets (see the interpretation below), deterministic byte-identical output pinned by tests. - Engine fixture doubles: the minimal in-crate template sets every
wave-2 proof runs through (the alkssh wave-1 doubles move: the
harness that exercises them deeply lands wave 2): a small ssr-shaped
set (page composing fragment partials, HTML escaper, typed context
built by a Rust builder per ADR-004) and a small code-shaped set
(no-op escaper, typed context with document-ordered arrays, the
preserve_orderexercise). Fixtures are pinned by golden render tests from day one (convention 7).
The wave-1 review gate is the cheap-place gate: the registry surface,
the render entry semantics, and the error enum are one-way doors in
practice (every later task implements against them; the published crate
freezes the public API). The gate reads the landed surface against
core.md's component map and ADR-002/003/004/005 line by line.
Decomposition record (2026-11-06): wave 1 decomposed into five
task files under tasks/implementation/ plus the gate. Shape:
w1-manifest first (the dep pin posture, the alktype removal, and the
feature-graph declaration everything lands inside); then a deliberately
sequential chain: w1-error-model (the public enum over the manifest),
w1-registry-construction (the set builder over the error model),
w1-render-entry (the deterministic strict render over the registry),
w1-engine-fixtures (the two minimal doubles over the shipped entry
points); then review-wave-1 (the gate) depending on all five. The
chain is sequential logistically: all tasks edit the same small
module tree and lib.rs re-exports, and each consumes the previous
task's surface (the registry returns the error enum; the render entry
rides the registry; the fixtures ride both). Nothing here is parallel
enough to justify worktree separation.
Interpretations recorded (flagged for the wave-1 gate):
- alktype removal vs optional-behind-feature: ADR-009 item 4 says
moving alktype behind the feature is an implementation task (of what
it calls phase 3, the implementation phase now starting); but the
consumer steer queues bast-rust-codecs activation behind v1, so
there is nothing for
bast-codegento gate in this scope, and a feature with no dep behind it is a dead entry.w1-manifestremoves alktype from the tree; the optional dep and the feature's payload land together at target activation. Flagged for the gate to confirm against ADR-009. - No context-build error arms in the v1 enum: no engine-owned set ships, and consumer builders own their errors (ADR-008). The arm lands additively with the first engine-owned set (an additive change pre-1.0; post-publish the freeze inventory carries the enum's evolution policy). Flagged.
- Partials-not-render-targets needs bookkeeping above the
substrate: handlebars' registry is a single namespace (registering
a partial is template registration under the hood), so
core.md's rule ("the name argument resolves to a registered template, never a bare partial") cannot be delegated to the substrate. The registry task decides between set-level bookkeeping (a set type that knows which names are templates) and convention plus tests, and pins the choice. Flagged. - Template naming validation: convention 9 (lowercase kebab/snake, one name per kind) is either validated at registration or enforced by tests only; the registry task decides and records. Flagged.
Wave 2: Consumer-shaped proofs
The generality claim re-proven on shipped code. Wave 1 landed the core and minimal doubles; nothing here opens new engine surface unless a proof finds a gap: the stop-and-book rule applies (a needed core exception is booked for the gate, never silently absorbed; ADR-001's falsifiability discipline).
- The ssr-html proof (posture B): a full consumer-shaped fixture
set through the public API only: consumer-supplied page templates
and partials registered from outside the crate (the alkforge
posture, ADR-008 machinery equality), pages composing fragment
partials, fragments as registered render targets, HTML-escape golden
pins (interpolated output escaped, deliberate raw-output exercise),
typed render failure mapped toward the error-page shape (posture-B
error posture,
emit.md). No banner, no filesystem; determinism is what alkforge's caching rests on (ADR-003). - The code-shape proof (posture-A/C shape): a code-shaped fixture
set exercising the codec/JIT context shape on shipped code: typed
context with document-ordered arrays (the
preserve_orderload-bearing test, convention 5), no-op escaping, and the determinism invariant that generated-artifact hygiene rests on: byte-identical output across repeated renders and registry rebuilds (ADR-003). The DO-NOT-EDIT banner is an emit-layer obligation with no engine-owned set to exercise it in v1; the proof notes that gap honestly instead of implying coverage (convention 8,emit.mdposture A). - The conformance battery: the standing verification set for the
core: the strict-mode behavior matrix (missing variable means a
typed error in both sinks, never silent output; ADR-002),
error-surface completeness (every engine-owned failure path maps to
exactly one typed variant; malformed template text at registration
and malformed context input at render are data with no panic paths),
the two-sink correctness rows, the wasm build of the final dep set
joining the verification list (ADR-003), and the feature-matrix
posture (
defaultand--all-features, which coincide today; documented honestly and grown when the first feature ships).
Wave 2 decomposes after the wave-1 gate, absorbing whatever it corrects.
Decomposition record (2026-11-06, immediately after the wave-1
gate passed green; that gate absorbed no corrections, so nothing above
changes): wave 2 decomposes into three implementation tasks plus the
gate under tasks/implementation/. Shape: w2-ssr-proof and
w2-code-proof carry the two consumer-shaped proofs and are parallel
by construction: each lives in its own test binary owning its fixture
modules per the wave-1 convention, so there is nothing to sequence
between them and no shared-module collisions. Then
w2-conformance-battery depends on both proofs. The dependency line
above reads proofs ∥ battery and the wave releases when both groups
hold; the battery ordered after the proofs is a logistic choice with
the wave-1 precedent (a chain is sequential logistically, not
necessarily in working time): the battery consolidates the standing
verification set over the final wave-2 tree (strict-mode matrix,
error-surface rows, two-sink rows, feature-matrix posture, the wasm
build joining the verification list), which is only meaningful once
the proofs it stands beside have landed, and if a proof books a
finding under the stop-and-book rule, the battery would order itself
over the corrected surface rather than pin rows twice. Finally
review-wave-2 (the wave gate) depends on all three. Carried
reminders from the wave-1 gate: (b) folds into the conformance task
(the wasm build becomes a standing, unconditional entry in the
verification command list rather than an ad-hoc gate run); (a) and
(c) remain wave-3 concerns and are deliberately unassigned here. The
stop-and-book rule applies throughout this wave: a proof that finds a
needed core exception books it for the gate with written rationale
and never silently absorbs it (ADR-001's falsifiability discipline).
Wave 3: Validation & release
- Release readiness: public-API rustdoc warning-free; README
landing (the engine framing, the integration patterns: posture-B
usage for alkforge, the documented posture-A build-script pattern
per ADR-010, the posture-C note); CHANGELOG 0.1.0; the feature
matrix checked and documented (
defaultand--all-featurescoincide today); publish dry-run with a package file-list check (verify theexcludelist against what actually ships: SDD process docs, research, and plans are working documents, not crate payload); the freeze inventory recorded for the gate (the public API shape, the strict-mode and determinism postures, the error enum, the handlebars 6.4 pin's behavior). - review-wave-3: the release gate, a pure review over the readiness task and everything before it. It closes green only when the crate is honestly shippable.
- w3-publish: the publish checklist: manifest sanity, the full
verification set including the wasm build, the dry-run, the
crate-qualified
alkgen-v0.1.0tag (the family tag convention), andcargo publish.
Wave 3 decomposes after the wave-2 gate, absorbing whatever it corrects.
Plan closure
The plan closes at the publish of alkgen 0.1.0: the publish
commit, tagged alkgen-v0.1.0, pushed to origin/main, status flipped
to closed. From the publish, the freeze inventory is one-way: the
public API shape, the strict-mode posture, the determinism guarantee,
and the error model are semver events from then on. Engine-owned
template sets (bast-codegen at its activation, wgsl at OQ-05's) land
through future waves of this document or a successor plan, each gated
by its own review.
References
docs/architecture/: README (index), overview, core, emit, targets, open-questions, decisions/ADR-001 through 010docs/research/phase-0.md,docs/research/reader-poc-summary.md(the POC findings the wave-2 proofs build on)/workspace/@alkdev/alkssh/docs/plans/implementation.md(the wave pattern this follows)- Family ADR-002 (
/workspace/@alkdev/alkfamily/docs/architecture/ decisions/002-alkforge-gitea-replacement.md): why this crate unpaused; the no-deformation rule docs/sdd_process.md: task file format, categorical estimates, phases, the deferred-OQ convention