From a91ea2ec89f4899f0427582cd51fb64496f47b7b Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Fri, 6 Nov 2026 08:18:43 +0000 Subject: [PATCH] docs(architecture): phase 1 base: text-generation core specs, ADRs 001-010, OQ tracker - overview.md, core.md, emit.md, targets.md: modular spec set for the generalized engine (contexts + templates + partials -> text), with the three targets (ssr-html, bast-rust-codecs, call-handlers, wgsl-jit) as template sets plus emit postures - ADRs 001-010: one core/template-set split, handlebars 6.4 strict substrate, sync/wasm/deterministic posture, typed contexts with builders, per-registry escaper, SSR library posture, typed direct codecs output contract, template-set ownership, feature gating, library-only v1 - open-questions.md: OQ-01..OQ-08 centralized tracker carrying phase-0 residue (GN-01/02/07/09) with three deferred(scope) entries plus a resolved disposition for phase-0 OQ-GN-04 - tasks/architecture/: external-trigger tracker tasks paired with the deferred OQs (taskgraph validate passes) - phase-0.md checklist dispositioned for OQ-GN-04; AGENTS.md lifecycle updated to phase 1 Verification: taskgraph validate (4 tasks OK), cargo fmt --check, cargo test, em-dash grep clean; architecture-reviewer pass returned zero critical issues --- AGENTS.md | 31 +-- docs/architecture/README.md | 81 ++++++++ docs/architecture/core.md | 186 ++++++++++++++++++ .../001-one-core-template-set-targets.md | 64 ++++++ .../002-handlebars-substrate-strict-mode.md | 68 +++++++ ...03-core-posture-sync-wasm-deterministic.md | 58 ++++++ ...4-typed-contexts-builders-not-templates.md | 64 ++++++ .../005-escape-strategy-per-template-set.md | 56 ++++++ .../decisions/006-ssr-library-posture.md | 69 +++++++ .../007-bast-codec-output-contract.md | 78 ++++++++ .../decisions/008-template-set-ownership.md | 60 ++++++ .../009-feature-gated-template-sets.md | 57 ++++++ .../decisions/010-library-only-no-cli.md | 56 ++++++ docs/architecture/emit.md | 131 ++++++++++++ docs/architecture/open-questions.md | 158 +++++++++++++++ docs/architecture/overview.md | 161 +++++++++++++++ docs/architecture/targets.md | 146 ++++++++++++++ docs/research/phase-0.md | 10 +- .../architecture/oq-04-call-handlers-scope.md | 42 ++++ tasks/architecture/oq-05-wgsl-requirements.md | 41 ++++ .../oq-06-jit-core-requirements.md | 42 ++++ tasks/architecture/oq-07-html-primitives.md | 41 ++++ 22 files changed, 1685 insertions(+), 15 deletions(-) create mode 100644 docs/architecture/README.md create mode 100644 docs/architecture/core.md create mode 100644 docs/architecture/decisions/001-one-core-template-set-targets.md create mode 100644 docs/architecture/decisions/002-handlebars-substrate-strict-mode.md create mode 100644 docs/architecture/decisions/003-core-posture-sync-wasm-deterministic.md create mode 100644 docs/architecture/decisions/004-typed-contexts-builders-not-templates.md create mode 100644 docs/architecture/decisions/005-escape-strategy-per-template-set.md create mode 100644 docs/architecture/decisions/006-ssr-library-posture.md create mode 100644 docs/architecture/decisions/007-bast-codec-output-contract.md create mode 100644 docs/architecture/decisions/008-template-set-ownership.md create mode 100644 docs/architecture/decisions/009-feature-gated-template-sets.md create mode 100644 docs/architecture/decisions/010-library-only-no-cli.md create mode 100644 docs/architecture/emit.md create mode 100644 docs/architecture/open-questions.md create mode 100644 docs/architecture/overview.md create mode 100644 docs/architecture/targets.md create mode 100644 tasks/architecture/oq-04-call-handlers-scope.md create mode 100644 tasks/architecture/oq-05-wgsl-requirements.md create mode 100644 tasks/architecture/oq-06-jit-core-requirements.md create mode 100644 tasks/architecture/oq-07-html-primitives.md diff --git a/AGENTS.md b/AGENTS.md index cee1550..4f46dd7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -171,9 +171,11 @@ semantics live in `docs/sdd_process.md` §"Task File Format" / ## Lifecycle Status -The project is in **SDD phase 0** (exploration; no architecture -committed, no implementation on main). It was paused 2026-09-08, after -the reader POC (branch 1), and unpaused 2026-11-06. +The project is in **SDD phase 1** (architecture). Phase 0 completed +with the reader POC; the project was paused 2026-09-08 and unpaused +2026-11-06. The architecture base (spec docs, ADRs 001-010, OQ +tracker in `docs/architecture/`) was committed 2026-11-06; docs are +in `draft` status pending OQ resolution and review finalization. The unpause reason: alkforge (family ADR-002, gitea replacement; see `/workspace/@alkdev/alkfamily/docs/architecture/decisions/002-alkforge-gitea-replacement.md`) @@ -190,12 +192,13 @@ Family ADR-002 also records the no-deformation rule for consumers: downstream asks file upstream gaps rather than forking or patching the core, so alkforge's SSR needs flow into alkgen's design, not around it. -Phase 0 output lands in `docs/research/` (the phase 0 document is the -current work item); `docs/architecture/` + ADRs come with phase 1. POC -code never merges to main; two modes exist: branch mode (POC builds on -repo code; git branch, findings merged into research docs, branch -dropped) or standalone mode (POC independent of repo code; scratch -project at `/workspace/`). POCs have relaxed constraints +Phase 0 output lands in `docs/research/` (completed: phase-0 findings +and the reader POC summary); Phase 1 output lands in +`docs/architecture/` (see Architecture Context). POC code never +merges to main; two modes exist: branch mode (POC builds on repo +code; git branch, findings merged into research docs, branch dropped) +or standalone mode (POC independent of repo code; scratch project at +`/workspace/`). POCs have relaxed constraints (comments/unwrap acceptable) since they are exploration tools, not production code. @@ -209,9 +212,13 @@ production code. answers questions. The branch list there reflects the 2026-11-06 unpause (alkforge SSR first, then BAST readers, then call-protocol handlers, then the template-JIT core). -- `docs/architecture/`: will exist after Phase 1; the authoritative - spec once present (ADRs numbered, OQs tracked in open-questions.md). -- `tasks/`: task graph after decomposition. +- `docs/architecture/`: the authoritative architecture spec (started + 2026-11-06, docs in `draft` status). `README.md` there is the index + (doc table, ADR table, lifecycle); ADRs are numbered under + `decisions/`; OQs are tracked in `open-questions.md` with stable + OQ-IDs. Extend it rather than re-litigating recorded decisions. +- `tasks/architecture/`: architecture-side taskgraph tasks, including + the external-trigger tracker tasks paired with deferred(scope) OQs. ## Reference Material (outside this repo) diff --git a/docs/architecture/README.md b/docs/architecture/README.md new file mode 100644 index 0000000..d526da9 --- /dev/null +++ b/docs/architecture/README.md @@ -0,0 +1,81 @@ +--- +status: draft +last_updated: 2026-11-06 +--- + +# alkgen Architecture + +This directory is the authoritative architecture specification for the +alkgen crate. It was created at the start of Phase 1 (2026-11-06) from +the Phase 0 findings in `docs/research/phase-0.md` and +`docs/research/reader-poc-summary.md`. + +## Current State + +Phase 1 (architecture) has started; Phase 0 is complete. The project +was paused 2026-09-08 after the reader POC and unpaused 2026-11-06 +because alkforge (family ADR-002) needs server-side HTML generation, +which alkgen provides. No implementation exists on main yet; `src/` +holds the validated dependency skeleton only. + +The crate is a **text-generation engine**: typed contexts plus +templates plus partials produce text. Targets (HTML SSR, Rust codecs, +WGSL) are template sets plus emit layers over one shared core. The +generalization is enforced by the consumers themselves, not asserted: +HTML is handlebars' native case, Rust codecs were validated end-to-end +by the reader POC, and WGSL is proven by alkminer-v1's `ShaderBuilder`. + +Branch numbering below follows Phase 0 conventions: branch 0 is the +SSR addition from the unpause; branches 1, 2, 3 retain their historical +meanings. Target names (ssr-html, bast-rust-codecs, call-handlers, +wgsl-jit) are preferred in these documents; historical branch numbers +may appear alongside them, and the mapping lives in `targets.md`. + +## Architecture Documents + +| Document | Scope | Status | +|---|---|---| +| [overview.md](overview.md) | Purpose, engine model, dependencies, constraints | draft | +| [core.md](core.md) | Shared text-generation core: registry, contexts, rendering, errors | draft | +| [emit.md](emit.md) | How rendered text is consumed: the three emit postures | draft | +| [targets.md](targets.md) | Per-target template sets, context shapes, validity pinning | draft | +| [open-questions.md](open-questions.md) | Centralized OQ tracker | draft | + +## ADR Table + +ADRs live in [decisions/](decisions/). + +| ADR | Title | Status | +|---|---|---| +| [001](decisions/001-one-core-template-set-targets.md) | One text-generation core; targets are template sets plus emit layers | Accepted | +| [002](decisions/002-handlebars-substrate-strict-mode.md) | handlebars-rust 6.4 substrate: strict mode, typed errors, embedded templates | Accepted | +| [003](decisions/003-core-posture-sync-wasm-deterministic.md) | Core posture: synchronous, wasm-clean, deterministic | Accepted | +| [004](decisions/004-typed-contexts-builders-not-templates.md) | Contexts are typed Rust structs built by context builders; kind mapping in Rust | Accepted | +| [005](decisions/005-escape-strategy-per-template-set.md) | Escape strategy is a per-registry policy | Accepted | +| [006](decisions/006-ssr-library-posture.md) | SSR: alkgen is an in-process rendering library for alkforge | Accepted | +| [007](decisions/007-bast-codec-output-contract.md) | BAST codec template set: typed direct codecs, zero-dependency output | Accepted | +| [008](decisions/008-template-set-ownership.md) | Engine-owned template sets ship in alkgen; consumer page templates live in the consumer | Accepted | +| [009](decisions/009-feature-gated-template-sets.md) | Template sets are feature-gated; default features ship the core only | Accepted | +| [010](decisions/010-library-only-no-cli.md) | alkgen is library-only; build-script integration is a documented pattern, no CLI | Accepted | + +## Open Questions + +All open questions are tracked in [open-questions.md](open-questions.md) +with stable OQ-IDs. Spec documents reference OQs by number and ADRs by +number; rationale and unresolved questions are not inlined. + +## Document Lifecycle + +| Status | Meaning | +|---|---| +| `draft` | Under active development; may change significantly | +| `reviewed` | Architecture final; implementation may begin; changes require review | +| `stable` | Implementation complete and verified; changes warrant an ADR | +| `deprecated` | Superseded; kept for reference | + +Docs carry YAML frontmatter (`status`, `last_updated`). A doc moves to +`reviewed` when its open questions are resolved and review passes. +The centralized tracker is `open-questions.md`; deferred questions +follow the two-half convention in `docs/sdd_process.md` (visible OQ +entry here plus an external-trigger tracker task under +`tasks/architecture/`). diff --git a/docs/architecture/core.md b/docs/architecture/core.md new file mode 100644 index 0000000..ad509d4 --- /dev/null +++ b/docs/architecture/core.md @@ -0,0 +1,186 @@ +--- +status: draft +last_updated: 2026-11-06 +--- + +# alkgen Architecture: Shared Core + +## What and why + +The core is everything in alkgen that produces text without knowing +what the text is for. It exists because three consumers (alkforge SSR, +BAST codecs, WGSL) share one shape, and because the first consumer is +an SSR use whose needs contain no codegen concepts: if registry, +context, and render machinery stayed clean under HTML, they are clean +(ADR-001). + +The core has six components: registry construction, template asset +conventions, context conventions, the helper policy, the render entry +point, and the error model. All are synchronous, wasm-clean, and +deterministic (ADR-003). + +## Component map + +| Component | Role | +|---|---| +| Registry construction | Builds one strict registry per template set; registration is fallible and typed | +| Template assets | Embedded templates and partials, named and versioned as code | +| Context conventions | Typed context structs built by Rust context builders | +| Helper policy | Minimal, pure textual utilities; semantic mapping stays in builders | +| Render entry point | Deterministic render of named templates over context values | +| Error model | One typed public error enum; failures are data | + +## Registry construction + +- **One registry per template set** (rationale recorded in ADR-002, + decision 6: it is what makes the escape policy and partial + namespaces enforceable rather than advisory). A template set is a + coherent unit (an HTML set for a consumer, the BAST codec set, a + future WGSL set). Each set gets its own registry with its own strict + mode, escaper, and partial namespace. Registries do not share + templates across sets; cross-set reuse is a source-level include, + not registry surgery. +- **Strict mode is always on** (ADR-002, AGENTS.md convention 6). A + missing context variable is a render error. The reader POC caught a + stale context key through strict mode at build time; silent gaps in + SSR would surface as blank page regions. +- **Registration is data, not side effects.** Registering a template, + partial, or helper is fallible (malformed templates produce a typed + error) and happens at set-construction time. This applies to + alkgen's own sets and to the API exposed to consumers (alkforge + registers its page templates through the same machinery; ADR-008). +- **No filesystem in lib code.** Templates come from `const &str` + inline literals or `include_str!` files. handlebars' `dir_source` + and `script_helper` features are off-limits (ADR-002, ADR-003). + +## Template assets + +- **Templates are code.** Assets are versioned in-repo, embedded by + reference (`include_str!`) or inline (`const &str`), and pinned by + render tests asserting golden output (AGENTS.md convention 7). + Changing a template is a source change. +- **Naming**: lowercase kebab or snake; one name per template kind + (convention 9). Generated file naming conventions are per-emit + posture and live in emit.md. +- **Partials are first-class.** Registration supports partials + alongside templates from day one (alkminer-v1 precedent). This is + the only branch-3 (template-JIT) requirement that reaches v1, and it + is also the natural SSR unit: header, footer, and nav fragments are + partials (ADR-006). +- **Consumers register their own assets.** The registry construction + API accepts externally supplied template and partial text so that + consumer-owned sets (alkforge pages) need no alkgen source access + (ADR-008). + +## Context conventions + +- **Contexts are typed Rust structs** (`serde::Serialize`, usually + derived) built by Rust context builders (ADR-004). This is the + typebox-rs shape with its weaknesses removed. Free-form + `serde_json::Value` contexts are not the convention; where a + document tree is passed through (schema fragments), it is passed as + a value inside a typed context. +- **Kind mapping happens in builders, not templates.** Mapping schema + kinds to target-language type strings (or HTML structures) is Rust + code in the context builder (the `schema_to_rust_type` precedent). + Templates stay dumb: conditionals over context flags, no lookup + logic of their own. This is what keeps template sets portable to a + second language target without template rewrites (phase-0 OQ-GN-04). +- **Ordering is document order.** `serde_json::preserve_order` is + load-bearing in contexts built from schema JSON: field order + determines packed byte order downstream, and rendering iterates + context arrays in insertion order. Sort only where a template + explicitly requires sorted output (convention 5). +- **Plan semantics, document names** (branch-1 refined rule, carried + from the reader POC): semantic content (offsets, strides, dispatch + tables) comes from alktype plans; names, docs, and descriptive + metadata come from the source document, because plans eagerly + resolve `$ref`s and lose def names. Whether a branch-1-style + projection exists at all is a template-set property, and the core + imposes nothing about it (ADR-001). +- **Pure-cursor model for code contexts.** Where a codec context + models addressing, it is uniform cursor semantics; no static/dynamic + offset-phase tracking (reader POC finding F-1). + +## Helper policy + +- Custom helpers exist to serve text-level utilities that are awkward + as template logic or context fields (padding, repetition, small + formatting). They must be pure and deterministic; no environment + reads, no time, no randomness, no I/O. +- Semantic decisions stay in builders (see above). A helper never + interprets a schema or a document; helpers see text. +- Each helper is registered explicitly by the owning template set. + The core registers none by default beyond handlebars' built-ins. + +## Render entry point + +- **Synchronous and deterministic.** The entry point takes a registry, + a template name, and a context value; produces the rendered text. + Same registry contents plus same context produces byte-identical + text (ADR-003). Rendering performs no I/O, no time reads, no + ordering that depends on hash maps leaking into output. +- **Two sinks**: render into a String, or into an `io::Write` target + (both wasm-clean; streaming a large page or a large generated file + should not force intermediate allocation). Which is exposed first + is an implementation choice; both are part of the concept. +- **Strict errors**. Render failures (missing variable in strict mode, + template error) are typed errors, not silent empty output. +- **Purity for downstream caching.** Because rendering is pure and + deterministic, consumers may cache by content hash (SSR) or compile + the output (template-JIT). The core does not cache; emit.md and + consumers do. + +## Error model + +- One public error enum for the crate, via thiserror, covering the + engine-owned operations: registration, template parsing, and + rendering, plus context-build failures raised by engine-owned + set builders. Consumer-side context construction is the consumer's + code (ADR-008): engine sets fold their errors into the crate enum; + consumer builders surface their own types and wrap or translate at + the boundary if they choose. Render and registration failures are + data: malformed templates or malformed BAST input are typed errors, + never panics (AGENTS.md convention 2, family posture: alkcall + accepts BAST from arbitrary peers, alkforge serves hostile traffic). +- Generated code carries its own error type(s), decided per template + set (branch-1 decision recorded in ADR-007). The core's error model + stops at rendering. +- No `unwrap`/`expect` outside tests; no panics in lib code. + +## Constraints + +- Synchronous; no async, no tokio, no threads (convention 3). +- Wasm-clean: no filesystem, no `std::time`, no environment reads, no + platform-specific dependencies in lib code (convention 4). Verified + by building the actual dep set to `wasm32-unknown-unknown`. +- No unsafe (convention 11). +- Determinism as above; generated-artifact hygiene rules (banner, + reproducibility) are emit-layer rules specified in emit.md, but they + are enabled here by deterministic rendering. + +## Design Decisions + +All design decisions are documented as ADRs in +[decisions/](decisions/). + +| ADR | Decision | Summary | +|---|---|---| +| [001](decisions/001-one-core-template-set-targets.md) | One core, targets as template sets | No target assumptions in shared types | +| [002](decisions/002-handlebars-substrate-strict-mode.md) | handlebars 6.4, strict mode, embedded templates | Substrate, typed registration | +| [003](decisions/003-core-posture-sync-wasm-deterministic.md) | Sync, wasm-clean, deterministic | Platform posture of the whole core | +| [004](decisions/004-typed-contexts-builders-not-templates.md) | Typed contexts, mapping in builders | Context conventions | +| [005](decisions/005-escape-strategy-per-template-set.md) | Escaper per registry | HTML-escape policy for SSR, no-op for code | +| [008](decisions/008-template-set-ownership.md) | Template set ownership | Consumer-owned assets register through the core API | + +## Open Questions + +Open questions are tracked in [open-questions.md](open-questions.md). +Key questions affecting this document: + +- **OQ-07**: whether alkgen ships generic HTML layout primitives for + SSR consumers (deferred(scope)) +- **OQ-05**: WGSL template set requirements (deferred(scope)) +- **OQ-06**: template-JIT core requirements (deferred(scope); partials + are already first-class and rendering already pure, so nothing in + this doc is expected to change) diff --git a/docs/architecture/decisions/001-one-core-template-set-targets.md b/docs/architecture/decisions/001-one-core-template-set-targets.md new file mode 100644 index 0000000..447d482 --- /dev/null +++ b/docs/architecture/decisions/001-one-core-template-set-targets.md @@ -0,0 +1,64 @@ +# ADR-001: One text-generation core; targets are template sets plus emit layers + +## Status + +Accepted + +## Context + +Phase 0 scoped the crate as "codegen over BAST documents" with static +file emission into downstream projects as the product. Two things +dissolved that framing: + +1. The 2026-11-06 unpause arrived because alkforge (family ADR-002) + needs server-side HTML generation, a runtime rendering use with no + schema, no plans, and no file emission. +2. WGSL generation for alkjs (alkminer-v1's ShaderBuilder precedent) + and Rust codec generation (reader POC) are provably the same + machinery: contexts plus templates plus partials produce text. + +Three data points, one shape. Treating SSR as a special case baked +into a codegen-shaped core would either fork the machinery or +deform it. + +## Decision + +alkgen's core is a text-generation engine: it owns registry +construction (strict mode, first-class partials, per-registry escape +policy), context conventions, a deterministic synchronous render entry +point, and typed errors. A target is a template set plus a context +shape plus an emit posture. Shared core types encode none of the +targets: no reader/writer assumptions, no HTML assumptions, no WGSL +assumptions. + +Validity pinning is per template set: render tests for each set pin +what "valid output" means for it (Rust compilability, HTML +well-formedness, WGSL syntax are set-level property tests, never core +concepts). + +The dev-tool identity is demoted from "the product" to "posture A of +the emit layer" (emit.md). + +## Consequences + +- Positive: three consumers, one maintenance surface; the SSR branch + acts as a standing overfitting test; the WGSL path needs no core + work beyond template sets when it arrives. +- Positive: the demotion is cheap because it changes nothing required + by branches 1 and 2; their needs are now a posture, not the crate's + definition. +- Negative: generalization can hide real per-target needs until late. + Mitigation: the first consumer is SSR (the least codegen-like), so + the core is exercised against the differentiator immediately. +- Negative: core APIs must stay small enough to remain + "everything-for-nothing" (no target vocabulary), which constrains + helpers and conveniences that would drift toward one target. + +## References + +- [targets.md](../targets.md) (the instance table) +- [emit.md](../emit.md) (the posture layer) +- Phase 0, section "Core generalization steer" and the framing + correction (`docs/research/phase-0.md`) +- [ADR-006](006-ssr-library-posture.md) (SSR posture), + [ADR-007](007-bast-codec-output-contract.md) (codec contract) diff --git a/docs/architecture/decisions/002-handlebars-substrate-strict-mode.md b/docs/architecture/decisions/002-handlebars-substrate-strict-mode.md new file mode 100644 index 0000000..0a29b7c --- /dev/null +++ b/docs/architecture/decisions/002-handlebars-substrate-strict-mode.md @@ -0,0 +1,68 @@ +# ADR-002: handlebars-rust 6.4 substrate: strict mode, typed errors, embedded templates + +## Status + +Accepted + +## Context + +The render substrate was settled in Phase 0 (validated stack, wasm +playground shipped by handlebars itself), but the substrate posture +was distributed across conventions, phase-0 prose, and two precedent +codebases (typebox-rs and the reader POC). It needs one decision +record, because posture is what typebox-rs got wrong (unwrap-based +registration, no strict mode, silent empty output) and what the POC +validated as right. + +## Decision + +1. **Substrate**: handlebars-rust pinned to `6.4` from crates.io. The + vendored checkout (`/workspace/handlebars-rust`, one commit past + v6.4.4) is reading material and an emergency patch source only, + not the dependency. +2. **Strict mode always**: every registry the library constructs or + exposes sets strict mode on. A missing context variable is a + render error, never silently-empty text (AGENTS.md convention 6; + alkminer-v1 ShaderBuilder precedent; the POC caught a stale context + key through strict mode at build time). +3. **Registration is fallible and typed**: template and partial + registration surfaces propagate parse errors as typed errors + (no unwrap). Malformed templates and malformed inputs are data, + not crashes (AGENTS.md convention 2). +4. **Assets are embedded code**: templates come from `const &str` or + `include_str!`; `dir_source` (filesystem) and `script_helper` + (rhai) features are off-limits in lib code; templates are pinned + by golden render tests (AGENTS.md convention 7). +5. **Partials are first-class**: registration supports partials + alongside templates from day one (branch-3 (template-JIT) + requirement priced in while free; also the SSR fragment unit). +6. **One registry per template set**: strict mode, escape policy + (ADR-005), and partial namespaces are registry-scoped properties, + so a single per-set registry is what makes them enforceable rather + than advisory. Cross-set asset reuse is a source-level include, + never registry surgery. Consumer sets use the same construction + path (machinery equality, ADR-008). + +## Consequences + +- Positive: strict mode turns silent-output bugs into build-time + errors at the generator, close to the cause, instead of compile or + runtime bugs in consumers; typed registration keeps the no-panic + posture intact for hostile inputs. +- Positive: the embedded-asset rule keeps lib code wasm-clean and + reproducible. +- Negative: strict mode can reject templates for missing-but-defaulted + keys; the remedy is context shape and template guards (`{{#if}}`), + the pattern F-3 validated, not a strict-mode escape hatch. +- Negative: pinning to a crates.io version means substrate bug fixes + ride the handlebars release train; the vendored checkout bounds the + emergency case. + +## References + +- [core.md](../core.md) (registry construction, template assets) +- [ADR-003](003-core-posture-sync-wasm-deterministic.md) (platform + posture incl. the off-limits features) +- Phase 0, sections "What is already settled" and typebox-rs prior art + (`docs/research/phase-0.md`); reader POC findings F-3, F-4 + (`docs/research/reader-poc-summary.md`) diff --git a/docs/architecture/decisions/003-core-posture-sync-wasm-deterministic.md b/docs/architecture/decisions/003-core-posture-sync-wasm-deterministic.md new file mode 100644 index 0000000..8370153 --- /dev/null +++ b/docs/architecture/decisions/003-core-posture-sync-wasm-deterministic.md @@ -0,0 +1,58 @@ +# ADR-003: Core posture: synchronous, wasm-clean, deterministic + +## Status + +Accepted + +## Context + +alkgen links into wasm-bound runtimes (alkjs), wasm browsers via +handlebars' own playground precedent, and Rust services serving +hostile traffic (alkforge, alkcall consumers). Three properties are +prerequisites for all of those and far cheaper to keep than to +retrofit: synchronous execution, wasm-cleanliness, and deterministic +output. + +## Decision + +The core (and everything engine-owned) is: + +1. **Synchronous**: no async, no tokio, no threads. Template rendering + and context building are CPU-bound; there is no await anywhere. +2. **Wasm-clean**: compiles to `wasm32-unknown-unknown`. No filesystem + access in lib code (templates embedded, `dir_source` off-limits), + no `std::time`, no environment reads, no platform deps; the dep + set is validated by an actual wasm build of every relevant + dependency combination (skeleton verified 2026-11-06). +3. **Deterministic**: same registry contents plus same context values + produce byte-identical text. No hash-map iteration order leaks into + output; contexts preserve document order where order is semantic + (serde_json `preserve_order`, AGENTS.md convention 5); no time or + randomness in rendering. +4. **Panic-free on inputs**: typed errors instead of panics for + malformed templates and malformed inputs. + +## Consequences + +- Positive: determinism is what makes consumer-side caching safe to + build (SSR content hashing), regeneration diff-quiet (posture A), + and render output compilable (posture C); it is one property paying + three ways. +- Positive: wasm-cleanliness is a supply-chain and deployment + posture for the family (minimal, auditable build), not just a + capability. +- Negative: no background refresh, no async caching, no wall-clock + features. Consumers wanting those build them on their side; the + engine's purity is the point, not a gap. +- Negative: `preserve_order` costs allocation and must be preserved + by every dependency that touches schema JSON; it is pinned because + packed byte order depends on it downstream. + +## References + +- [core.md](../core.md) (constraints section) +- [ADR-002](002-handlebars-substrate-strict-mode.md) (substrate + posture, off-limits features) +- [emit.md](../emit.md) (postures consuming determinism) +- AGENTS.md conventions 2, 3, 4, 5, 8 +- Family ADR-002 decision 5 (security posture) diff --git a/docs/architecture/decisions/004-typed-contexts-builders-not-templates.md b/docs/architecture/decisions/004-typed-contexts-builders-not-templates.md new file mode 100644 index 0000000..b447c6b --- /dev/null +++ b/docs/architecture/decisions/004-typed-contexts-builders-not-templates.md @@ -0,0 +1,64 @@ +# ADR-004: Contexts are typed Rust structs built by context builders; kind mapping in Rust + +## Status + +Accepted + +## Context + +Templates need a context. Two precedent paths existed: free-form +`serde_json::Value` contexts assembled by templates or helpers, and +typed context structs built by Rust code (typebox-rs +`#[derive(Serialize)]` structs; the reader POC's `FnCtx` tree). The +second is what the POC validated, and the first is what F-3's +anti-patterns showed to be fragile (offset expression threading, +dynamic partial dispatch). A related boundary needed recording: where +semantic mapping (schema kind to target-language type, plan node to +statement kind) belongs. + +## Decision + +1. **Contexts are typed Rust structs**, `serde::Serialize` (usually + derived), built by Rust context builders. Free-form Value contexts + are not the convention; document trees are passed as values inside + typed contexts where needed. +2. **Kind mapping lives in builders, not templates or helpers.** + Mapping schema kinds to type strings, plan nodes to statement + kinds, and similar semantic decisions are Rust functions in the + context layer. Templates carry conditionals over context flags + only; helpers never interpret documents. +3. **Ordering is document order.** Context arrays and schema-derived + maps preserve insertion order (`preserve_order`); sorting happens + only where a template explicitly requires it. +4. **Plan semantics, document names** (for plan-driven targets): the + plan is the semantic source (offsets, strides, dispatch); the + source document supplies names and descriptive metadata, because + plans eagerly resolve `$ref`s and lose definition names (reader + POC finding F-6). +5. **Pure-cursor model for addressing contexts**: uniform cursor + semantics with no static/dynamic offset phases (reader POC finding + F-1). + +## Consequences + +- Positive: templates stay dumb and portable to other language targets + (phase-0 OQ-GN-04 hunch: contexts language-neutral, template sets + differ); strict mode catches builder/template drift at build time. +- Positive: builders own the semantic walk once; the interpreter and + compiler duplication (phase-0 OQ-GN-03) stays a clean split. +- Negative: context structs are code that must track alktype's public + plan API on upgrades; this is an ordinary dependency concern, and + was validated as such by the 0.3.0 to 0.4.0 POC migration needing no + context-builder changes. +- Negative: typed contexts are more Rust code per target than a + loose Value soup; accepted because the POC showed the alternative + is where template fragility concentrates. + +## References + +- [core.md](../core.md) (context conventions) +- Phase-0 OQ-GN-02 resolution and residues + (`docs/research/phase-0.md`); reader POC findings F-1, F-3, F-6 + (`docs/research/reader-poc-summary.md`) +- [ADR-007](007-bast-codec-output-contract.md) (the plan-driven + target's output contract) diff --git a/docs/architecture/decisions/005-escape-strategy-per-template-set.md b/docs/architecture/decisions/005-escape-strategy-per-template-set.md new file mode 100644 index 0000000..e60adf8 --- /dev/null +++ b/docs/architecture/decisions/005-escape-strategy-per-template-set.md @@ -0,0 +1,56 @@ +# ADR-005: Escape strategy is a per-registry policy + +## Status + +Accepted + +## Context + +handlebars applies HTML entity escaping to `{{expr}}` output by +default (handlebars 6.4: `register_escape_fn` sets a registry-wide +escaper; `{{& }}` and triple-brace skip escaping). For HTML that is +correct and safety-relevant. For generated Rust or WGSL text it is a +corruption vector: type names and string literals containing `&`, `<`, +or quotes (doc comments, literals with entities) would be silently +mangled into invalid code. Escaping policy therefore cannot be a +core-wide constant, and cannot be left to template authors choosing +between `{{ }}` and `{{& }}` ad hoc. + +## Decision + +The escaper is a property of the template set, set at registry +construction: + +1. **ssr-html sets**: handlebars' default HTML escaper stays on as the + registry default. Template authors opt out per expression with + `{{& }}` only for deliberately-trusted markup (rare, reviewed like + source). This is a rendering-safety posture for user-supplied page + data, aligned with alkforge serving hostile traffic. +2. **Code target sets** (bast-rust-codecs, call-handlers, wgsl): the + registry registers a no-op escaper; templates use plain `{{ }}` + throughout. Codegen output is escaped at the source (literals are + quoted properly by the builder), never by entity mangling. +3. The core provides both policies; choosing one is part of defining + a template set (targets.md table). + +## Consequences + +- Positive: codegen correctness is by construction; SSR safety is by + construction. The one-registry-per-set rule (core.md) makes the + policy non-negotiable per set instead of per-template. +- Positive: matches handlebars' actual API shape (escaper is + registry-scoped), so no wrapper machinery. +- Negative: `{{& }}` in code sets becomes unnecessary and its use in + engine-owned code templates is forbidden by posture (it would + double-escape nothing but signals confusion); reviewers of template + changes watch for it. +- Negative: an HTML set with a no-op escaper would be a silent XSS + surface; mitigated because posture 1 is fixed by this ADR and + consumer sets (alkforge) build through the same documented policy. + +## References + +- [core.md](../core.md) (registry construction) +- [ADR-006](006-ssr-library-posture.md) (SSR consumer posture) +- [targets.md](../targets.md) (escaper column of the target summary) +- handlebars-rust registry API (vendored checkout, `register_escape_fn`) diff --git a/docs/architecture/decisions/006-ssr-library-posture.md b/docs/architecture/decisions/006-ssr-library-posture.md new file mode 100644 index 0000000..5a99631 --- /dev/null +++ b/docs/architecture/decisions/006-ssr-library-posture.md @@ -0,0 +1,69 @@ +# ADR-006: SSR: alkgen is an in-process rendering library for alkforge + +## Status + +Accepted + +## Context + +alkforge (family ADR-002, the gitea replacement) needs server-side +HTML generation before the codegen branches matter to it. Its +no-deformation rule routes the ask into alkgen's design rather than +around it. During the unpause discussion three candidate postures +existed for SSR rendering, and the choice shapes alkgen's public API +and dependency weight inside alkforge: + +1. pre-rendered static assets (the dev-tool posture reused); +2. in-process library rendering, render-per-request; +3. in-process library rendering with consumer-side caching. + +A separate quickjs-based SSR route (preact) was floated and rejected: +pure Rust rendering is faster and removes an interpreter from the +attack surface, which matters for a git-serving web product. + +## Decision + +1. **In-process library rendering** (posture B in emit.md): alkforge + links alkgen and renders HTML on demand. Static pre-rendering + remains possible later (same machinery, posture A), but the + design-time posture is in-process. +2. **Render-per-request vs cached rendering is alkforge's choice.** + alkgen guarantees pure, synchronous, deterministic rendering + (ADR-003), which is the property that makes consumer-side caching + and content hashing safe to build; alkgen provides no cache. +3. **Output units are fragments and full pages.** Partials make + fragments natural; pages compose fragments. The phase-0 fragment + exclusion was scoped to the code branches and does not apply here + (phase-0 OQ-GN-09). +4. **Render failures are typed errors** mapped by alkforge to its + error surface; no panics; no silently-blank regions (strict mode, + ADR-002). +5. **No DO-NOT-EDIT banner on rendered HTML**: it is the served + product, not a checked-in generated artifact. Determinism still + applies, and golden render tests pin pages in alkforge. + +## Consequences + +- Positive: no filesystem, no build step, and no staleness discipline + needed for SSR content; templates can change with alkforge's + builds safely against live data. +- Positive: the same API shape (build registry, render template) is + the dev-tool integration shape, so posture A and posture B share + the core's surface rather than forking it. +- Negative: rendering cost sits in the request path when uncached; + consumers who need zero request-path cost can still pre-render + (posture A) with the same sets. +- Negative: alkgen gains an always-deployed dependency weight in + alkforge; ADR-009 keeps that weight to the core by keeping alktype + out of SSR builds. + +## References + +- [emit.md](../emit.md) (posture B) +- [targets.md](../targets.md) (ssr-html) +- [ADR-001](001-one-core-template-set-targets.md), + [ADR-005](005-escape-strategy-per-template-set.md), + [ADR-008](008-template-set-ownership.md), + [ADR-009](009-feature-gated-template-sets.md) +- Phase-0 OQ-GN-09 (`docs/research/phase-0.md`); family ADR-002 + (`/workspace/@alkdev/alkfamily/docs/architecture/decisions/002-alkforge-gitea-replacement.md`) diff --git a/docs/architecture/decisions/007-bast-codec-output-contract.md b/docs/architecture/decisions/007-bast-codec-output-contract.md new file mode 100644 index 0000000..cd37aae --- /dev/null +++ b/docs/architecture/decisions/007-bast-codec-output-contract.md @@ -0,0 +1,78 @@ +# ADR-007: BAST codec template set: typed direct codecs, zero-dependency output + +## Status + +Accepted + +## Context + +The reader POC (2026-09-08, re-validated on alktype 0.4.0 on +2026-11-06) answered the branch-1 output questions with executable +evidence: + +1. A `Value`-materializing generated reader runs ~1.9x faster than the + alktype interpreter at identical output shape, and the decomposition + showed the celebrated ~20x interpreter-vs-handwritten gap is + `Value` allocation cost, not dispatch: hand-rolled code at the same + output shape is only ~10 to 20% faster. +2. A second render with typed output (plain structs, direct + cursor-based codecs) runs 291 to 322 ns: roughly 10x the Value shape + and 15 to 20x the interpreter, within ~4x of the no-allocation + hand-rolled floor (the residual is inherent String/Vec + allocation). +3. The typed direction was set at the 2026-09-08 debrief (russh-sftp + protocol-type shape), leaving the (a) serde-derive vs (b) direct + codec fork open. The debrief resolved it for (b) on speed, size, + and wasm grounds: direct codecs are zero-dependency (no serde, no + serde_json, no alktype in generated code), while serde-derive would + reintroduce a serde dep and wrapper-heavy handling of BAST's + per-field endianness and TUnion byte-discriminators. + +## Decision + +The bast-rust-codecs template set emits **typed direct codecs**: + +1. Generated output is plain structs and enums with direct + `decode`/`encode` functions over a generated cursor core + (`fn decode(buf) -> Result` shape). No `Value` + materialization, no serde machinery. +2. Generated code has **zero dependencies**; alktype appears only in + the generator. Serde bridges (or a serde feature) may be added + later if a consumer requires interop; starting dependency-free is + the irreversible-safe direction (adding serde is additive, + removing it is breaking). +3. Generated code is **self-contained** (no alktype runtime calls) and + **error-returning end to end**: bounds checks, UTF-8 validation, + and union dispatch limits return typed errors, never panic + (reader POC finding F-2; AGENTS.md convention 2; alkcall accepts + BAST from arbitrary peers). The generated error type is + generated-code-local; its exact shape (mirroring + `AlkTypeError::Access`'s variants without the alktype dependency) + is a branch-1 detailed-spec decision constrained by this ADR. +4. Addressing is the uniform cursor model; compile-time `fixed_size` + and dispatch tables from plans are emitted as data, and constant + cursor chains rely on LLVM folding rather than source offset + algebra (finding F-1). + +## Consequences + +- Positive: the success metric (recovering most of the ~20x gap at + equal output contract) is achieved at the typed contract, with + parity discipline proving semantic agreement byte-for-byte. +- Positive: zero-dep generated code keeps consumers light and the + wasm story simple. +- Negative: no serde interop out of the box; consumers needing + `serde`-compatible output wait for a bridge feature (accepted trade + recorded above). +- Negative: generated-code error type duplicates alktype error + semantics without alktype; parity and drift tests (three-way + benchmark discipline) are the standing mitigation. + +## References + +- [targets.md](../targets.md) (bast-rust-codecs) +- [ADR-004](004-typed-contexts-builders-not-templates.md) (contexts: + plans for semantics, document for names) +- `docs/research/reader-poc-summary.md` (executive summary, typed + output resolution, follow-ups 1-4; phase-0 OQ-GN-05/06 resolutions) +- `/workspace/russh-sftp/src/protocol/` (typed-structure precedent) diff --git a/docs/architecture/decisions/008-template-set-ownership.md b/docs/architecture/decisions/008-template-set-ownership.md new file mode 100644 index 0000000..fdc747d --- /dev/null +++ b/docs/architecture/decisions/008-template-set-ownership.md @@ -0,0 +1,60 @@ +# ADR-008: Template set ownership: engine-owned sets ship in alkgen; consumer page templates live in the consumer + +## Status + +Accepted + +## Context + +A target is a template set, but who owns the assets? The codegen +branches made this obvious: the BAST codec templates are alkgen's +intellectual content (they encode the plan-to-code translation), and +consumers should not copy them. The SSR branch made it non-obvious: +alkforge's page templates are application UI. If alkgen ever became +a home for application layouts, it would drift into a web framework, +which it is not (overview.md). + +## Decision + +1. **Engine-owned template sets ship in alkgen** (behind features per + ADR-009): bast-rust-codecs now, call-handlers on its deferral path + (OQ-04), and any future set whose ownership resolves to + engine-owned. The wgsl set's ownership (alkjs vs alkgen) is + explicitly open under OQ-05; this ADR decides only where + engine-owned sets live when they exist. Their assets, builders, + and golden tests are alkgen's, versioned and pinned like source. +2. **Consumer-owned template sets live in the consumer**: alkforge's + page templates, partials, and helpers are alkforge assets + registered into a core-built registry via the alkgen API. alkgen + ships no application HTML assets in v1; whether generic layout + primitives ever join the engine is deliberately unresolved (OQ-07) + and starts empty. +3. **The registration API is the same machinery for both**: consumer + assets register through the same typed, strict, policy-bearing + construction path as engine sets (no second-class registry; + ADR-002). + +## Consequences + +- Positive: clear supply chain and pinning story (consumers upgrade + engine sets by dependency version; their own templates by git); + clear review boundaries (template edits touching parity-pinned + output are gated by the codegen templates' golden tests). +- Positive: alkgen does not accrete an application-layer UI + vocabulary. +- Negative: alkforge re-inventing layout structure that another + consumer might also want is possible; that is what OQ-07 tracks, + and the remedy path (file the ask upstream; ADR if shared assets + become real) is pre-decided. +- Negative: the registration API must be ergonomic enough that owning + templates externally is pleasant; a bad API here would push + consumers toward requesting in-crate placement for convenience. + The API shape is an implementation-time concern constrained by this + ADR's machinery-equality rule. + +## References + +- [targets.md](../targets.md) (owner column of the target summary) +- [core.md](../core.md) (registration machinery) +- [ADR-006](006-ssr-library-posture.md) (SSR consumer posture), + [ADR-009](009-feature-gated-template-sets.md) (feature gating) diff --git a/docs/architecture/decisions/009-feature-gated-template-sets.md b/docs/architecture/decisions/009-feature-gated-template-sets.md new file mode 100644 index 0000000..2634be5 --- /dev/null +++ b/docs/architecture/decisions/009-feature-gated-template-sets.md @@ -0,0 +1,57 @@ +# ADR-009: Template sets are feature-gated; default features ship the core only + +## Status + +Accepted + +## Context + +alkforge links alkgen for SSR (ADR-006). alktype is needed only by the +codec (and future handler) template sets and their context builders; +the SSR consumer inherits no BAST machinery. Family ADR-002's security +posture treats consumer dependency surface as a standing constraint: +pulling alktype into every alkgen consumer would widen cargo +supply-chain surface for no benefit to SSR builds. The symmetric +concern exists for the codegen sides only if the core itself grew +optional platform deps, which ADR-003 forbids. + +## Decision + +1. **Default features ship the core only**: registry construction, + render entry point, error model, escape policies. No engine-owned + template set, no alktype. +2. Engine-owned template sets are cargo features: + - `bast-codegen`: the BAST codec templates plus their context + builders; pulls `alktype`. + - Future engine-owned sets land as additional features (naming per + the set name; call-handlers may join `bast-codegen` or take its + own, decided when OQ-04 resolves; the wgsl direction tracks + OQ-05). +3. Consumer-owned sets (alkforge pages) need no feature: they ride the + default core, registering their own assets (ADR-008). +4. Current dependency state vs target state: `Cargo.toml` on main + lists alktype unconditionally today; moving alktype behind the + feature is an implementation task of phase 3, not a further + architecture decision. + +## Consequences + +- Positive: alkforge's dependency graph carries only the substrate + (handlebars, serde_json, thiserror, serde); alkcall consumers + explicitly opt into alktype, which matches whose problem it is. +- Positive: the wasm-clean story per feature stays testable + individually. +- Negative: feature-combination testing multiplies; mitigation is a + small fixed matrix in CI (core, core+bast-codegen, wasm build of + both), not exhaustive. +- Negative: a slight discoverability cost (codegen users must opt in); + acceptable because each consumer type is homogeneous. + +## References + +- [overview.md](../overview.md) (dependencies table) +- [ADR-006](006-ssr-library-posture.md) (SSR dependency weight), + [ADR-008](008-template-set-ownership.md) (ownership), + [ADR-003](003-core-posture-sync-wasm-deterministic.md) (why the + core carries no optional platform deps) +- Family ADR-002 decision 5 (security posture) diff --git a/docs/architecture/decisions/010-library-only-no-cli.md b/docs/architecture/decisions/010-library-only-no-cli.md new file mode 100644 index 0000000..711f153 --- /dev/null +++ b/docs/architecture/decisions/010-library-only-no-cli.md @@ -0,0 +1,56 @@ +# ADR-010: alkgen is library-only; build-script integration is a documented pattern, no CLI + +## Status + +Accepted + +## Context + +Phase 0 demoted the dev-tool identity (ADR-001), and OQ-GN-01 resolved +the output unit as module-level files generated into a downstream +project. The reader POC validated the integration shape end-to-end as +a build script (`build.rs` calling schema plus templates plus context +builder, emitting into `OUT_DIR` or the source tree; finding F-4). An +open surface remained: whether alkgen should also ship a CLI +generator binary (a sibling to codegen CLIs in other template tooling). + +## Decision + +alkgen v1 is **library-only**. Posture-A consumers integrate by +calling the library from their build scripts; the build.rs pattern is +documented usage, not a binary. No CLI ships in v1. + +Rationale for recording the negative: + +1. Every current integration point is either in-process (alkforge, + posture B) or a build script on repo code (codec consumers, + posture A); a CLI adds packaging, argument, and workspace-layout + decisions the crate does not need to make yet. +2. The dev-tool identity is demoted; a CLI is the strongest form of + re-promoting it, and would pull workspace-layout questions + (where generated files land, how regeneration is triggered) into + alkgen that belong to consumers. +3. If a consumer wants a CLI later, the library API supports one by + composition, and adding a binary to the same crate is additive. + +## Consequences + +- Positive: no argument grammar, no output-path policy, no process + model to stabilize; the API surface stays a Rust surface. +- Positive: build scripts get exactly the schema-to-source-tree + semantics they need (the POC shape), including `OUT_DIR` + integration that a CLI cannot replicate cleanly. +- Negative: non-Rust or non-cargo consumers have no entry point; if + such a consumer ever appears (none exists in the family today), + that is the moment to revisit this decision via ADR. +- Negative: "documented pattern" means examples in docs; the pattern + must be maintained as the API evolves (a doc cost, not a design + cost). + +## References + +- [emit.md](../emit.md) (posture A integration shape) +- [ADR-001](001-one-core-template-set-targets.md) (dev-tool demotion), + [ADR-009](009-feature-gated-template-sets.md) (feature surface) +- Phase-0 OQ-GN-01; reader POC findings F-4 and the POC structure note + (`docs/research/reader-poc-summary.md`) diff --git a/docs/architecture/emit.md b/docs/architecture/emit.md new file mode 100644 index 0000000..a03ae35 --- /dev/null +++ b/docs/architecture/emit.md @@ -0,0 +1,131 @@ +--- +status: draft +last_updated: 2026-11-06 +--- + +# alkgen Architecture: Emit Postures + +## What and why + +A template set renders text; an emit posture is how that text reaches +its consumer. The three postures correspond to the three real +consumers, and the differences between them are the honest answer to +"what differs per target": not the render machinery, but where the +text goes and what obligations follow. + +ADR-001 demoted the dev-tool from "the product" to one posture among +three. This document specifies all three: posture A (build-time +static emit), posture B (in-process runtime render), and posture C +(render-to-compile feed, future). + +## Posture A: build-time static emit + +Used by the BAST codec template set and its future siblings +(call-protocol handlers; branch numbering per phase 0: branches 1 and +2). + +- **Consumer**: a downstream project's build script. alkgen is a + generator dependency of the build, not a runtime dependency of the + generated output (ADR-007: generated code is zero-dependency; + phase-0 OQ-GN-06). +- **Output unit**: module-level files (phase-0 OQ-GN-01 resolution): + one document produces one or more self-contained modules dropped + into the consumer's source tree. Crate and fragment generation are + out for v1. The exact file partitioning policy is OQ-01 and the + regeneration posture is OQ-02. +- **Output hygiene, because files get checked in** (AGENTS.md + convention 8): + - every artifact starts with the "Auto-generated by alkgen. + DO NOT EDIT." banner; + - rendering is deterministic, so regeneration is diff-quiet: + same document, same templates, same options produce byte-identical + files; + - generated code is convention-conformant: no panics on input + (error-returning), no unsafe, no dependence on alktype. +- **Integration shape**: the build script calls the API directly + (documented pattern; the reader POC validated the build.rs shape + end-to-end, finding F-4). No CLI exists in v1 (ADR-010). + +## Posture B: in-process runtime render + +Used by SSR consumers, currently alkforge (ADR-006). + +- **Consumer**: alkforge links alkgen as a library and renders HTML on + demand: per request, or cached by the consumer and re-rendered on + content change. The choice between eager cache and + render-per-request is alkforge's; determinism of rendering is what + makes consumer-side caching safe to build (ADR-003). +- **Output unit**: fragments and full pages. Partials make fragments + natural; pages compose fragments (ADR-006). The phase-0 fragment + exclusion (OQ-GN-01) was scoped to posture A and does not apply + here. +- **Output hygiene**: rendered HTML is the product, not a checked-in + generated artifact, so the DO-NOT-EDIT banner does not apply. + Determinism still applies (caching, hashing, and alkforge's render + tests all lean on it), and alkforge's page templates are pinned by + golden render tests in alkforge, per the templates-are-code + convention. +- **Assets**: alkforge page templates live in alkforge and register + into a core-built registry (ADR-008). alkgen ships no HTML assets + for posture B beyond the registry API itself; the open question + about generic layout primitives is OQ-07. +- **Errors**: render failure is a typed error mapped by the consumer + to its error surface (an alkforge error page). No panics; alkforge + serves hostile traffic (AGENTS.md convention 2). +- **Performance**: alkforge owns its latency budget; the architecture + obligation here is only the shape that keeps it achievable: pure, + cheap, repeated rendering with no hidden I/O. The reader POC's + numbers are research data for the codec target, not acceptance + thresholds for this posture; page-template budgets are set by + alkforge's web design when it exists. + +## Posture C: render-to-compile feed + +Future (branch 3, template-JIT; OQ-06). The render step is the JIT +step: alkgen renders shader (WGSL) text, and downstream compiles and +caches the compiled artifact, exactly the alkminer-v1 +ShaderBuilder shape. + +- Nothing new is required from the core today. The posture-critical + properties were fixed up front because retrofitting them is costly: + partials are first-class (core.md), rendering is pure, synchronous, + and deterministic (ADR-002, ADR-003), and output carries no banner + or file-system assumptions. ADR-010's library-only rule also applies: + the wasm-bound alkjs runtime uses the library, not a tool. + +## Cross-posture obligations + +| Obligation | A | B | C | +|---|---|---|---| +| Deterministic byte-identical output | yes | yes | yes | +| Typed errors, no panics | yes | yes | yes | +| DO-NOT-EDIT banner | yes | no | no | +| Golden render tests pin output | yes | yes (in owning crate) | yes | +| Validity pinning by template set | yes | yes | yes | + +The last row is the general rule (ADR-001): each template set's render +tests pin what "valid output" means for it. The core cannot know and +must not pretend to: WGSL syntax, HTML well-formedness, and Rust +compilability are property tests owned by each set, not core concepts. + +## Design Decisions + +All design decisions are documented as ADRs in +[decisions/](decisions/). + +| ADR | Decision | Summary | +|---|---|---| +| [003](decisions/003-core-posture-sync-wasm-deterministic.md) | Sync, wasm-clean, deterministic | Underwrites caching and hashing in every posture | +| [006](decisions/006-ssr-library-posture.md) | In-process SSR library | Posture B's shape for alkforge | +| [007](decisions/007-bast-codec-output-contract.md) | Typed direct codecs | Posture A's generated-code contract | +| [010](decisions/010-library-only-no-cli.md) | Library-only v1 | Build-script integration, no CLI | + +## Open Questions + +Open questions are tracked in [open-questions.md](open-questions.md). +Key questions affecting this document: + +- **OQ-01**: file partitioning policy for posture A artifacts (open) +- **OQ-02**: regeneration posture for posture A artifacts: wholesale + overwrite vs partial regeneration (open) +- **OQ-06**: template-JIT core requirements (deferred(scope)) diff --git a/docs/architecture/open-questions.md b/docs/architecture/open-questions.md new file mode 100644 index 0000000..5d57b31 --- /dev/null +++ b/docs/architecture/open-questions.md @@ -0,0 +1,158 @@ +--- +status: draft +last_updated: 2026-11-06 +--- + +# alkgen Architecture: Open Questions + +Centralized tracker. Phase-0 questions carried forward note their +legacy IDs (`OQ-GN-NN` in `docs/research/phase-0.md`) so traceability +survives the renumbering. Spec documents reference OQs by number. + +**Status taxonomy**: `open` marks a question that is decidable now and +resolves when its owning spec or decision is written (typically a +phase-1/branch-detail artifact). `deferred(scope)` marks one waiting +on an external condition (a consumer, a crate spec); each follows the +two-half convention from `docs/sdd_process.md`, naming its unblocking +condition here and pairing with an external-trigger tracker task under +`tasks/architecture/`. Re-triage between the two statuses is an +architect action, not a default. + +## Theme: Emit (code branches) + +### OQ-01: File partitioning policy for posture-A artifacts + +- **Origin**: emit.md (phase-0 OQ-GN-01 residue) +- **Status**: open +- **Priority**: medium +- **Impacts**: blocks the branch-1 detailed spec (the emit layer's + file layout section) and the consumer-facing regeneration story. +- **Resolution**: Not yet decided. One file per document vs per-type + files, and where the reader/writer/validator split lands, interacts + with module size and the consumer's `include` layout. Decided when + the branch-1 detailed spec is written. +- **Cross-references**: emit.md posture A, ADR-007 + +### OQ-02: Regeneration posture for posture-A artifacts + +- **Origin**: emit.md (phase-0 OQ-GN-01 residue) +- **Status**: open +- **Priority**: low +- **Impacts**: blocks only the final shape of OQ-01's answer (regen + follows partitioning); nothing else. +- **Resolution**: Not yet decided. Wholesale overwrite is the likely + landing (determinism makes it diff-quiet); partial regeneration + would only matter if consumers hand-edit generated files, which the + DO-NOT-EDIT banner forbids. Resolve together with OQ-01. +- **Cross-references**: OQ-01 + +## Theme: Generated validation + +### OQ-03: Full BAST validation strategy for generated validators + +- **Origin**: targets.md (phase-0 OQ-GN-07; reader POC follow-up 5) +- **Status**: open +- **Priority**: medium +- **Impacts**: blocks the validator template arm set and settles + whether generated code carries a jsonschema dependency (an + ADR-007 surface, since ADR-007 records zero dependencies except + serde bridges). +- **Resolution**: Materially informed, not closed. Read-path + validation (bounds checks, UTF-8, union dispatch limits) is proven + generatable as error-returning code with no jsonschema dependency + (reader POC). The open half: do generated validators re-express + `ValidationPlan` semantic checks in template-generated code (drift + risk, mitigated by the parity discipline) or call into jsonschema + (heavyweight dependency in generated code)? Decided in the branch-1 + detailed spec. +- **Cross-references**: targets.md bast-rust-codecs, ADR-007 + +## Theme: Future targets + +### OQ-04: call-handlers template set scope + +- **Origin**: targets.md (phase-0 branch 2) +- **Status**: deferred(scope) +- **Door type**: two-way +- **Priority**: medium +- **Impacts**: blocks nothing currently active; the branch-2 consumers + arrive after branch 1. Adds a template set and possibly an input + path (JSON Schema/OpenAPI) beyond BAST when it activates. +- **Blocked on**: branch-1 convergence plus a concrete consumer ask + naming the protocol surfaces to generate (tracker task + `architecture/oq-04-call-handlers-scope`). +- **Resolution**: Not yet decidable. The adapter precedent + (`alkhttp/src/adapters`) frames the shape; no concrete protocol + use cases exist to design against. +- **Cross-references**: targets.md call-handlers, ADR-001, ADR-009 + +### OQ-05: wgsl template set requirements + +- **Origin**: targets.md (phase-0 branch 3; alkjs codegen need) +- **Status**: deferred(scope) +- **Door type**: two-way +- **Priority**: medium +- **Impacts**: blocks the alkjs WGSL codegen path only; nothing in v1. + Template-set ownership (alkjs vs alkgen), feature name, and context + shape are all downstream of the alkjs crate spec. +- **Blocked on**: alkjs crate spec naming the shaders to generate and + its wgpu pipeline shape (tracker task + `architecture/oq-05-wgsl-requirements`). +- **Resolution**: Not yet decidable. alkminer-v1's ShaderBuilder is + the structural precedent; the requirements cannot be read from it + alone. +- **Cross-references**: targets.md wgsl-jit, OQ-06 + +### OQ-06: template-JIT core requirements + +- **Origin**: targets.md (phase-0 branch 3, JIT core) +- **Status**: deferred(scope) +- **Door type**: two-way +- **Priority**: low +- **Impacts**: blocks the render-then-compile cache concept (branch 3) + only. Everything v1 needed from it is already decided and built in: + first-class partials, pure sync deterministic rendering (ADR-002, + ADR-003), wasm-clean (ADR-003). +- **Blocked on**: concrete alkjs runtime integration (wgpu pipeline + caching needs) plus a measured motivation that the v1 + render-entry-point shape obstructs JIT use (tracker task + `architecture/oq-06-jit-core-requirements`). +- **Resolution**: Not yet decidable. A far-timeline consumer; v1's + posture is designed so that no retroactive core change is expected. +- **Cross-references**: targets.md wgsl-jit, emit.md posture C, + ADR-002, ADR-003 + +## Theme: Shared core + +### OQ-07: Generic HTML layout primitives in alkgen + +- **Origin**: emit.md posture B, targets.md ssr-html +- **Status**: deferred(scope) +- **Door type**: two-way +- **Priority**: low +- **Impacts**: blocks nothing in v1; only relevant if a demonstrated + shared need emerges across SSR consumers. +- **Blocked on**: alkforge's SSR experience demonstrating a concrete + shared need for engine-owned layout primitives (tracker task + `architecture/oq-07-html-primitives`). +- **Resolution**: Not yet decidable. ADR-008 puts all page templates + in the consumer; whether a shared need for alkgen-owned generic + primitives (base layouts, form partials) exists is exactly what the + consumer experience would show. If it arrives, alkforge files it + upstream (family ADR-002 no-deformation rule) and the decision + becomes an ADR plus a new engine-owned feature. +- **Cross-references**: targets.md ssr-html, ADR-006, ADR-008 + +### OQ-08: Target-language scope for v1 template sets + +- **Origin**: phase-0 OQ-GN-04; dispositioned by adoption in Phase 1 +- **Status**: resolved (2026-11-06, by Phase-1 adoption) +- **Priority**: n/a (resolved) +- **Resolution**: The core is target-language-agnostic by + construction (ADR-001, ADR-004: contexts are language-neutral, + kind mapping lives in builders), and v1 template sets are written per consumer need with no + second-language template work speculatively: ssr-html (HTML) first, + bast-rust-codecs (Rust) second, wgsl-jit deferred (OQ-05). The + SSR branch arriving with no core change is the hunch's predicted + data point. Revisiting costs are low while ADR-004 holds. +- **Cross-references**: ADR-001, ADR-004, OQ-05 diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md new file mode 100644 index 0000000..6cd2965 --- /dev/null +++ b/docs/architecture/overview.md @@ -0,0 +1,161 @@ +--- +status: draft +last_updated: 2026-11-06 +--- + +# alkgen Architecture: Overview + +## Purpose + +alkgen is the text-generation engine of the alk* stack. The core takes +typed contexts, templates, and partials and produces text. Targets +differ only in the template set, the context shape, and the posture by +which the text is consumed. Everything underneath that is shared, and +the shared part is what this architecture specifies. + +This framing is a decision, not a description of intent. Phase 0 began +with "codegen over BAST documents" and generalized through three real +consumers until the general claim held: HTML is handlebars' native +case, Rust codecs were validated end-to-end by the reader POC, and +WGSL is proven by alkminer-v1's ShaderBuilder (three data points, one +shape). ADR-001 records the decision and its constraint: shared core +types must not bake reader/writer, HTML, or WGSL assumptions in. + +## The consumers, one shape + +| Consumer | Output text | When | +|---|---|---| +| alkforge (family ADR-002) | HTML fragments and pages, rendered in-process | first, active now | +| alkcall channels, via BAST codec templates | Rust reader/writer/validator modules, emitted at build time | next; POC-validated | +| alkjs | WGSL shader text feeding a render-then-compile cache | far out; shapes core requirements today | + +A fourth consumer class (call-protocol handlers, the static counterpart +of alkhttp's `from_openapi` / `from_jsonschema` adapters) is deferred +until the codec templates converge; see targets.md and OQ-04. + +The queue order is alkforge's need, not a preference: the SSR branch is +the simplest consumer and the generality test. If the core renders +HTML cleanly with no target-specific exceptions, the codegen paths are +not overfitted. + +## Engine model + +``` +inputs: BAST document, or page data + │ + ▼ + context builder template set + (Rust code; typed (templates, partials, + contexts out) helpers, escaper) + │ │ + ▼ ▼ + contexts ───────────────► registry + │ + ▼ + render (sync, deterministic) + │ + ▼ + text artifacts (one per template kind) + │ + ▼ + emit posture (see emit.md) +``` + +The vertical layers in this diagram are the crate's three concerns +(two terms from sibling crates appear in the pipeline: a BAST document +is alktype's binary abstract syntax tree document model, the wire +schema format of the alk* stack; and plans are alktype's runtime +walk structures over such documents, from which offset, size, and +dispatch semantics are read out): + +1. **Shared core** (spec: [core.md](core.md)): registry construction + (strict mode, first-class partials, per-registry escape policy, + where an escaper is the function handlebars applies to + interpolated output), context conventions, a deterministic + synchronous render entry point, and the typed error model. All + wasm-clean. +2. **Template sets** (spec: [targets.md](targets.md)): the per-target + assets and their context shapes. Engine-owned sets ship in alkgen + behind features (ADR-008, ADR-009); consumer-owned sets (alkforge + page templates) live in the consumer. +3. **Emit postures** (spec: [emit.md](emit.md)): how rendered text + reaches its consumer: build-time static files, in-process runtime + render, or a render-to-compile feed. + +Context building straddles 1 and 2: the conventions (typed structs, +document order, mapping logic in Rust) are core; the actual context +shapes are per-target. + +## What alkgen is not + +- Not an interpreter. alktype walks BAST documents at runtime and owns + dynamic-schema semantics; alkgen is the compile-time counterpart for + the static case (phase-0 OQ-GN-03). The two are complements, and the + measured interpreter-vs-generated gap is codegen's value: roughly + 1.9x at the `Value` output contract, 15 to 20x at the typed contract + the POC validated. +- Not a validator. Generated validators are a branch-1 + (bast-rust-codecs) template-set concern (OQ-03); the core has no + validation vocabulary. +- Not a web framework. SSR consumers get rendering; routing, headers, + lifecycle, and caching are theirs (ADR-006). +- Not a CLI. v1 is library-only; build scripts integrate by documented + pattern (ADR-010). + +## Dependencies + +| Dependency | Role | Notes | +|---|---|---| +| handlebars 6.4 | render substrate | strict mode, partials, per-registry escaper; pinned to `6.4` from crates.io; the vendored `/workspace/handlebars-rust` checkout is reading material and an emergency patch source only | +| serde, serde_json | context serialization, schema documents | `preserve_order` is load-bearing: schema-JSON key order determines packed byte order downstream | +| alktype 0.4 | BAST document model and plans | used by codec template sets; feature-gated (ADR-009) so SSR consumers do not pull it | +| thiserror 2 | typed errors | convention 2 posture | + +Wasm-cleanliness is validated, not assumed: the skeleton (the exact dep +set above) compiles to `wasm32-unknown-unknown` release, re-verified +2026-11-06, and handlebars-rust ships its own wasm playground. The +optional handlebars features `dir_source` and `script_helper` are +off-limits in lib code (filesystem access, rhai scripting). + +## Inherited constraints + +The crate conventions in `AGENTS.md` bind all architecture work: no +comments in code, thiserror-only error handling, no async, wasm-clean, +preserve_order, strict template mode, templates as pinned code assets, +generated-code hygiene, standard naming, one module per file, zero +unsafe, no em dashes in prose. Where a convention is load-bearing for +the architecture, the relevant spec references it explicitly (strict +mode and error posture in core.md; banner and determinism in emit.md). + +The family security posture (family ADR-002 decision 5) applies +downstream-facing constraints here: minimal dependency surface for +consumers (ADR-009 exists for this), deterministic output safe to +cache and hash, and typed errors instead of panics on hostile or +malformed input, since alkcall accepts BAST from arbitrary peers. + +## Design Decisions + +All design decisions are documented as ADRs in +[decisions/](decisions/). + +| ADR | Decision | Summary | +|---|---|---| +| [001](decisions/001-one-core-template-set-targets.md) | One core, targets as template sets | The generalized text engine; no target assumptions in core types | +| [002](decisions/002-handlebars-substrate-strict-mode.md) | handlebars 6.4, strict mode, embedded templates | Substrate and registration posture | +| [003](decisions/003-core-posture-sync-wasm-deterministic.md) | Sync, wasm-clean, deterministic | Core platform posture | +| [004](decisions/004-typed-contexts-builders-not-templates.md) | Typed contexts, mapping in builders | Context conventions | +| [005](decisions/005-escape-strategy-per-template-set.md) | Escaper per registry | HTML-escape for SSR sets, no-op for code sets | +| [006](decisions/006-ssr-library-posture.md) | In-process SSR library | alkforge links alkgen; fragments and pages | +| [007](decisions/007-bast-codec-output-contract.md) | Typed direct codecs | Branch-1 output contract from the POC debrief | +| [008](decisions/008-template-set-ownership.md) | Template set ownership | Engine-owned sets in-crate; consumer page templates outside | +| [009](decisions/009-feature-gated-template-sets.md) | Feature-gated template sets | Default features ship core only | +| [010](decisions/010-library-only-no-cli.md) | Library-only v1 | Build-script integration pattern, no CLI | + +## Open Questions + +Open questions are tracked in [open-questions.md](open-questions.md). +The ones shaping this overview's scope: + +- **OQ-04**: call-protocol handler template set scope (deferred(scope)) +- **OQ-05**: WGSL template set requirements (deferred(scope)) +- **OQ-06**: template-JIT core requirements (deferred(scope)) diff --git a/docs/architecture/targets.md b/docs/architecture/targets.md new file mode 100644 index 0000000..019ec5c --- /dev/null +++ b/docs/architecture/targets.md @@ -0,0 +1,146 @@ +--- +status: draft +last_updated: 2026-11-06 +--- + +# alkgen Architecture: Targets + +## What a target is + +A target is a concrete instantiation of the engine (ADR-001): a +template set (templates, partials, helper registrations, escaper +choice), a context shape, an emit posture, and a validity-pinning +strategy. The core knows nothing about targets; this document is the +registry of instances and the place each target's remaining +decisions are named. + +Branch numbering follows phase-0 conventions (branch 0 = SSR addition +from the 2026-11-06 unpause; 1, 2, 3 historical). The table gives the +mapping; everything else in this doc uses names. + +| Branch | Target | Status | +|---|---|---| +| 0 | ssr-html | active, first consumer | +| 1 | bast-rust-codecs | POC-validated; template set not yet in-crate | +| 2 | call-handlers | deferred(scope) | +| 3 | wgsl-jit | deferred(scope) | + +## Target summary + +| Target | Template set owner | Escaper (ADR-005) | Context shape | Emit posture | Feature (ADR-009) | +|---|---|---|---|---|---| +| ssr-html | consumer (alkforge) | HTML | page/data views | B (in-process render) | none (core) | +| bast-rust-codecs | alkgen | none (no-op) | plan projection + document names | A (build-time emit) | `bast-codegen` | +| call-handlers | alkgen | none (no-op) | deferred (OQ-04) | A | `bast-codegen` or own | +| wgsl-jit | alkjs or alkgen, open (OQ-05) | none (no-op) | shader contexts | C (render-to-compile) | open (OQ-05) | + +## ssr-html + +alkforge's server-side web view. The engine's first consumer and the +generality test: if this target needs no core exceptions, the core is +not overfitted to codegen (ADR-001, ADR-006). + +- **Posture**: alkforge links alkgen; pages render in-process, on + demand or cached by alkforge (ADR-006). +- **Contexts**: page/data views defined by alkforge; no layout math, + no plan projection. The simplest context shape of the three, which + is the point. +- **Escaper**: handlebars' HTML escaping as the registry default + (ADR-005). +- **Validity pinning**: alkforge pins page and fragment renders with + golden tests in alkforge; rendered HTML is served, not checked in + as generated code, so no banner (emit.md posture B). +- **Ownership**: all page templates, partials, and helpers live in + alkforge and register through the core API (ADR-008). Whether + alkgen should ever ship generic HTML layout primitives is OQ-07. +- **Remaining decisions**: minimal; this target is where + decomposition into implementation tasks starts when alkforge's web + view design names its pages. + +## bast-rust-codecs + +Binary reader/writer/validator modules generated from alktype BAST +documents for alkcall channels consumers. The branch the reader POC +validated end-to-end: plan-driven contexts, parity-exact generated +code, typed output at 291 to 322 ns versus the interpreter's 5.4 us +(re-validated 2026-11-06 on alktype 0.4.0). + +- **Posture**: build-time emit into the consumer's source tree + (emit.md posture A); build-script integration (ADR-010). +- **Contexts**: plan-driven projection (ADR-004). The plan is the + semantic source (offsets, strides, dispatch tables); the source + document is the name source, since plans eagerly resolve `$ref`s and + drop def names (phase-0 OQ-GN-02 refined by reader POC finding + F-6). +- **Output contract**: typed direct codecs over plain structs, zero + dependencies in generated code, self-contained (no alktype runtime + calls), pure cursor addressing, error-returning end to end (ADR-007; + reader POC findings F-1, F-2, F-6; phase-0 OQ-GN-05/06 resolutions). +- **Escaper**: no-op; generated Rust must not be HTML-escaped + (ADR-005). +- **Validity pinning**: parity discipline against the alktype + interpreter plus compilation of the generated modules; template + inventory enumerates the full kind arm surface (records, + field-disc unions, offset-indirect, big-endian, maxLength + reservations, writers; reader POC follow-ups 3 and 4; phase-0 + OQ-GN-05 POC coverage). +- **Remaining phase-1 residue, not yet specced**: the exact + plan-to-context projection (which plan structures become which + context shapes), generated-code error type shape, writer-side plan + survey, and full-validation strategy (OQ-03). These are the branch's + detailed spec, to be written when this target activates; the + decisions above are the constraints it cannot violate. + +## call-handlers + +The static counterpart of alkhttp's `from_openapi` / +`from_jsonschema` adapters: generated handler tables and types from +schema documents (BAST or JSON Schema/OpenAPI inputs). + +Status: deferred(scope). The consumer asks that would pin its shape +(the specific protocol surfaces to generate against) have not arrived; +branch 1 converges first. See OQ-04. No template set, context shape, +or feature decision is made here beyond the constraint that the core +must not assume it (ADR-001). + +## wgsl-jit + +WGSL generation for alkjs, the quickjs-based wgpu runtime, feeding a +render-then-compile cache: the alkminer-v1 ShaderBuilder shape. + +Status: deferred(scope). The alkjs crate spec does not exist yet, so +the template set's requirements (which shaders, which parameters, +which compile pipeline) cannot be read. Everything v1 needs from this +branch has been decided instead: partials first-class, pure sync +deterministic rendering (ADR-002, ADR-003), and wasm-clean so the +render step itself can run inside the wasm-bound runtime (phase-0 +"Core generalization steer"). See OQ-05 and OQ-06. + +## Design Decisions + +All design decisions are documented as ADRs in +[decisions/](decisions/). + +| ADR | Decision | Summary | +|---|---|---| +| [001](decisions/001-one-core-template-set-targets.md) | One core, targets as template sets | The instantiation model | +| [005](decisions/005-escape-strategy-per-template-set.md) | Escaper per registry | HTML for ssr-html; no-op for code sets | +| [006](decisions/006-ssr-library-posture.md) | In-process SSR library | ssr-html posture | +| [007](decisions/007-bast-codec-output-contract.md) | Typed direct codecs, zero-dep output | bast-rust-codecs output contract | +| [008](decisions/008-template-set-ownership.md) | Template set ownership | alkforge owns its page templates; codec set ships in-crate | +| [009](decisions/009-feature-gated-template-sets.md) | Feature-gated template sets | alktype and engine-owned sets behind features | + +## Open Questions + +Open questions are tracked in [open-questions.md](open-questions.md). +Key questions affecting this document: + +- **OQ-01**: posture-A file partitioning policy (open) +- **OQ-03**: full BAST validation strategy for generated validators + (open) +- **OQ-04**: call-handlers scope (deferred(scope)) +- **OQ-05**: wgsl template set requirements (deferred(scope)) +- **OQ-06**: template-JIT core requirements (deferred(scope)) +- **OQ-07**: generic HTML layout primitives in alkgen + (deferred(scope)) +- **OQ-08**: target-language scope for v1 template sets (resolved) diff --git a/docs/research/phase-0.md b/docs/research/phase-0.md index e7dc8c1..ef3e775 100644 --- a/docs/research/phase-0.md +++ b/docs/research/phase-0.md @@ -559,9 +559,13 @@ is a place the compiled path stays interpreter-shaped. refined by the POC (names from BAST, typed output resolved to direct codecs). Residue: exact plan→context projection (Phase 1 spec task) -- [ ] OQ-GN-04 (language scope): low-stakes; core language-agnostic, - Rust-only code templates for v1 (half-hunch; the SSR branch's - HTML template set is an in-family data point the hunch predicts) +- [x] OQ-GN-04 (language scope): dispositioned 2026-11-06 at Phase 1 + start: core language-agnostic by construction (architecture + ADR-001/ADR-004), template sets written per consumer need (SSR + HTML first, Rust codecs second, WGSL deferred); tracker entry + OQ-08 in `docs/architecture/open-questions.md` records the + adoption. The SSR branch arriving with no core change is the + hunch's predicted data point - [x] OQ-GN-05/06 (delegation boundary, dependency shape): resolved 2026-09-08 by reader POC: self-contained generated code, serde_json-only dependency for the Value contract, zero-dep for diff --git a/tasks/architecture/oq-04-call-handlers-scope.md b/tasks/architecture/oq-04-call-handlers-scope.md new file mode 100644 index 0000000..85bdf13 --- /dev/null +++ b/tasks/architecture/oq-04-call-handlers-scope.md @@ -0,0 +1,42 @@ +--- +id: architecture/oq-04-call-handlers-scope +name: "External trigger: call-handlers consumer ask arrives" +status: pending +depends_on: [] +scope: single +risk: trivial +impact: isolated +level: research +--- + +## Description + +[external-trigger, deferred-oq] Tracker for OQ-04 (call-handlers +template set scope, `deferred(scope)` in +`docs/architecture/open-questions.md`). This task is not actionable +work; it exists so the task graph can represent the unblocking +condition. Mark `completed` when branch 1 converges and a concrete +consumer ask names the protocol surfaces to generate; then re-open +OQ-04 (or resolve it directly if no architecture decision remains). + +## Acceptance Criteria + +- [ ] Branch 1 (bast-rust-codecs) has converged (template set + in-crate, golden tests pinned) +- [ ] A consumer ask exists naming concrete protocol surfaces to + generate (the alkhttp adapter counterpart need) +- [ ] OQ-04 transitioned out of `deferred(scope)` + +## References + +- docs/architecture/open-questions.md (OQ-04) +- docs/architecture/targets.md (call-handlers) +- docs/sdd_process.md, "Deferred OQs and the blocker-task half" + +## Notes + +> Agent fills this during implementation. + +## Summary + +> Agent fills this on completion. diff --git a/tasks/architecture/oq-05-wgsl-requirements.md b/tasks/architecture/oq-05-wgsl-requirements.md new file mode 100644 index 0000000..a5e0883 --- /dev/null +++ b/tasks/architecture/oq-05-wgsl-requirements.md @@ -0,0 +1,41 @@ +--- +id: architecture/oq-05-wgsl-requirements +name: "External trigger: alkjs crate spec arrives" +status: pending +depends_on: [] +scope: single +risk: trivial +impact: isolated +level: research +--- + +## Description + +[external-trigger, deferred-oq] Tracker for OQ-05 (wgsl template set +requirements, `deferred(scope)` in `docs/architecture/open-questions.md`). +This task is not actionable work. Mark `completed` when the alkjs +crate spec exists and names the shaders to generate and its wgpu +pipeline shape; then re-open OQ-05 (or resolve directly). + +## Acceptance Criteria + +- [ ] alkjs crate spec exists +- [ ] Shader set and pipeline shape named (the codegen requirements + can be read from the spec) +- [ ] OQ-05 transitioned out of `deferred(scope)` + +## References + +- docs/architecture/open-questions.md (OQ-05) +- docs/architecture/targets.md (wgsl-jit) +- /workspace/@alkminer/reference/alkminer-v1/src/compute/kernel.rs + (ShaderBuilder precedent) +- docs/sdd_process.md, "Deferred OQs and the blocker-task half" + +## Notes + +> Agent fills this during implementation. + +## Summary + +> Agent fills this on completion. diff --git a/tasks/architecture/oq-06-jit-core-requirements.md b/tasks/architecture/oq-06-jit-core-requirements.md new file mode 100644 index 0000000..b768953 --- /dev/null +++ b/tasks/architecture/oq-06-jit-core-requirements.md @@ -0,0 +1,42 @@ +--- +id: architecture/oq-06-jit-core-requirements +name: "External trigger: template-JIT need materializes with measured motivation" +status: pending +depends_on: [] +scope: single +risk: trivial +impact: isolated +level: research +--- + +## Description + +[external-trigger, deferred-oq] Tracker for OQ-06 (template-JIT core +requirements, `deferred(scope)` in `docs/architecture/open-questions.md`). +This task is not actionable work. Mark `completed` when the alkjs +wgpu integration exists and a measured motivation shows the v1 render +entry point shape obstructs render-then-compile caching; then re-open +OQ-06 (or resolve directly). + +## Acceptance Criteria + +- [ ] alkjs wgpu runtime integration exists (OQ-05 context available) +- [ ] A measured motivation recorded (what the v1 shape obstructs) +- [ ] OQ-06 transitioned out of `deferred(scope)` + +## References + +- docs/architecture/open-questions.md (OQ-06) +- docs/architecture/emit.md (posture C) +- docs/architecture/decisions/002-handlebars-substrate-strict-mode.md, + decisions/003-core-posture-sync-wasm-deterministic.md (the v1 + properties that already satisfy posture C) +- docs/sdd_process.md, "Deferred OQs and the blocker-task half" + +## Notes + +> Agent fills this during implementation. + +## Summary + +> Agent fills this on completion. diff --git a/tasks/architecture/oq-07-html-primitives.md b/tasks/architecture/oq-07-html-primitives.md new file mode 100644 index 0000000..3e20094 --- /dev/null +++ b/tasks/architecture/oq-07-html-primitives.md @@ -0,0 +1,41 @@ +--- +id: architecture/oq-07-html-primitives +name: "External trigger: SSR shared need demonstrated" +status: pending +depends_on: [] +scope: single +risk: trivial +impact: isolated +level: research +--- + +## Description + +[external-trigger, deferred-oq] Tracker for OQ-07 (generic HTML layout +primitives in alkgen, `deferred(scope)` in +`docs/architecture/open-questions.md`). This task is not actionable +work. Mark `completed` when alkforge's SSR experience demonstrates a +concrete shared need for engine-owned layout primitives (base +layouts, form partials); then re-open OQ-07, which becomes an ADR plus +a new engine-owned feature. + +## Acceptance Criteria + +- [ ] alkforge SSR is running with its own page templates (ADR-008) +- [ ] A demonstrated shared need recorded (what two or more consumers + or pages would reuse from engine-owned primitives) +- [ ] OQ-07 re-opened or resolved + +## References + +- docs/architecture/open-questions.md (OQ-07) +- docs/architecture/decisions/008-template-set-ownership.md +- docs/sdd_process.md, "Deferred OQs and the blocker-task half" + +## Notes + +> Agent fills this during implementation. + +## Summary + +> Agent fills this on completion.