From ee4053e3e28acecacb8f90fc5352eb70c0cdd322 Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Sat, 7 Nov 2026 06:07:28 +0000 Subject: [PATCH] docs(release): rewrite README on the shipped framing; CHANGELOG 0.1.0; package exclude fix - README: the engine contract (strict mode, set-per-registry, consumer-supplied text, typed errors, determinism, two sinks, per-set escaping), the three integration postures (emit.md A/B/C), an explicit what-0.1.0-does-not-ship section, no stale codegen/alktype references - CHANGELOG: Keep a Changelog 0.1.0 entry itemized against the actual surface, with the error-evolution policy recorded - Cargo.toml: exclude docs/plans/ and tasks/ from the package --- CHANGELOG.md | 70 ++++++++++++++++++++++++++++++ Cargo.toml | 2 +- README.md | 118 ++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 178 insertions(+), 12 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b07d0c9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,70 @@ +# Changelog + +All notable changes to this crate are documented here. The format is +based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and +this crate adheres to [Semantic Versioning](https://semver.org/). + +## [0.1.0] - 2026-11-07 + +Initial release: the text-generation core of the alk* stack +(ADR-001), proven fit for its first consumer, alkforge's server-side +HTML rendering. The engine is consumer-blind: no engine-owned +template sets ship in 0.1.0; consumer-supplied template and partial +text registers through the same machinery the engine would use +(ADR-008). + +### Added + +- `TemplateSet`: one registry per template set (ADR-002 decision 6), + constructed over an `EscapePolicy` with strict mode forced on + (never parameterizable). Typed fallible registration of templates + and partials (`register_template`, `register_partial`; malformed + text is rejected at registration with the substrate's parse error + reachable through the source chain), a helper registration seam for + set-owned custom helpers (purity is a documented convention, not + runtime enforcement), set-level bookkeeping distinguishing render + targets from partials (`is_template`; rendering a partial's name or + an unknown name fails typed before the substrate is consulted), and + the two render entry points: `render` (String sink) and + `render_to_write` (`io::Write` sink, streaming, not atomic by + contract). +- `EscapePolicy`: the escaper as an explicit per-set choice + (ADR-005). `Html` keeps the substrate's entity escaping on + (`{{& }}` as the per-expression opt-out); `NoOp` passes + interpolations through verbatim for code-shaped sets. +- `Error`: the crate's one public error enum, non-exhaustive by + policy (see below), with exactly two variants and no catch-all: + `Registration` (substrate `TemplateError` behind it) and `Render` + (substrate `RenderError` behind it). Every engine-owned failure + path maps to exactly one variant; no input the API accepts reaches + a panic path. +- The `bast-codegen` cargo feature, declared and gating nothing + (ADR-009): the manifest posture for the future engine-owned codec + set, verified not to pull alktype or any other dependency. + +### Guarantees + +- Rendering is synchronous, pure, and deterministic (ADR-003): + repeated renders, rebuilt registries, and both sinks produce + byte-identical output for the same registry contents and context, + pinned by tests across registration orders and fresh contexts. +- Strict mode (ADR-002): a missing context variable (top-level or + nested in `{{#each}}`/`{{#if}}` branches) is a typed `Error::Render` + in both sinks, never silently empty output. The write sink's + no-atomicity on failure is stated by tests (partially written + bytes stay; buffering is the consumer's tool for atomicity). +- Wasm-clean by construction: no filesystem, no threads, no time, no + environment access anywhere in the library; the + `wasm32-unknown-unknown` release build is a standing verification + gate. + +### Error evolution policy + +The `Error` enum is `#[non_exhaustive]`: its documented evolution is +additive (a context-build error arm lands with the first +engine-owned template set), and non-exhaustiveness keeps that +addition non-breaking for downstream match sites. `EscapePolicy` +stays exhaustive: ADR-005 fixes the policy set at two and the +expected consumer interaction is construction, not matching. + +[0.1.0]: https://git.alk.dev/alkdev/alkgen/tags/alkgen-v0.1.0 diff --git a/Cargo.toml b/Cargo.toml index ce17f2a..40b2d97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://git.alk.dev/alkdev/alkgen" readme = "README.md" keywords = ["handlebars", "templates", "text-generation", "templating", "html"] categories = ["development-tools", "template-engine"] -exclude = [".opencode/", "AGENTS.md", "docs/reviews/", "docs/research/", "docs/sdd_process.md"] +exclude = [".opencode/", "AGENTS.md", "docs/plans/", "docs/reviews/", "docs/research/", "docs/sdd_process.md", "tasks/"] [lib] name = "alkgen" diff --git a/README.md b/README.md index d4be0d0..2797778 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,112 @@ # alkgen -Template-based codegen engine for the alk* stack: [handlebars-rust] -templates rendered over [alktype] BAST (Binary Abstract Syntax Tree) -documents, producing code artifacts; binary readers, writers, and -validators for alkcall channels, and call-protocol handlers in the -shape of alkhttp's `from_openapi` / `from_jsonschema` adapters. Plus -plain HTML generation for [alkforge]'s server-side rendering, the -first real consumer (family ADR-002). +The text-generation engine of the [alk* stack][alkforge]: [handlebars-rust] +templates rendered over serde-serializable contexts with strict, +deterministic output. One crate, one purpose: register a template set, +render its templates, get the same bytes every time. -Status: Phase 0 (exploration), unpaused 2026-11-06 for the alkforge -SSR use case. See `docs/research/phase-0.md`. +Status: **0.1.0 publishes the core**, proven fit for its first consumer, +[alkforge]'s server-side HTML rendering. The engine is consumer-blind: +it owns no template sets and no assumptions about what the rendered +text is. HTML, Rust, and WGSL are all the same shape to it. -[handlebars-rust]: https://github.com/sunng87/handlebars-rust -[alktype]: https://crates.io/crates/alktype +[handlebars-rust]: https://docs.rs/handlebars [alkforge]: https://git.alk.dev/alkdev/alkforge + +## The engine's contract + +- **Strict mode is always on.** A missing context variable is a typed + error, never silently empty output. A silent gap in generated text + becomes a bug in the consumer's build, far from its cause; the engine + refuses to produce one. +- **One registry per template set.** A `TemplateSet` is the unit: + strict mode, the set's escaper, and its namespace for templates, + partials, and helpers all live there. A partial's name is never a + render target; the set tracks the difference and rejects a + partial-as-target as a typed error. +- **Consumer-supplied templates register through the same machinery + the engine would use.** Template text arrives as `&str` (inline + literals, `include_str!`, caller-built strings); there is no + filesystem path anywhere in the API, so the core is wasm-clean by + construction. +- **Typed errors, no panics.** Malformed template text fails at + registration; render failures (missing variable, unknown template, + unknown helper) fail at render. Every failure maps to exactly one + variant of the public `Error` enum (non-exhaustive; the substrate's + detail stays reachable through the error's source chain). Template + sets may add helpers, which must be pure: no time, no environment, + no randomness, no I/O. +- **Deterministic, byte-identical output.** Repeated renders and + rebuilt registries produce the same bytes, and there is no hash-map + iteration order that can leak into them. This is what makes + consumer-side caching and checked-in generated artifacts safe. +- **Two sinks.** `render` returns a `String`; `render_to_write` + streams to any `io::Write` target. Both produce byte-identical + output. The write sink is not atomic: a render that fails partway + may leave earlier bytes in the target, and consumers that need + atomicity buffer first. +- **Escaping is a per-set choice.** `EscapePolicy::Html` keeps the + substrate's HTML entity escaping on (with `{{& }}` as the deliberate + per-expression opt-out); `EscapePolicy::NoOp` passes interpolations + through verbatim for code generation, where output should be escaped + at the source, not mangled by entity escaping. + +## Usage + +```rust +use alkgen::{EscapePolicy, TemplateSet}; +use serde_json::json; + +let mut set = TemplateSet::new(EscapePolicy::Html); +set.register_template("page", "

{{title}}

{{> footer}}")?; +set.register_partial("footer", "

{{year}}

")?; + +let ctx = json!({ "title": "Hello", "year": "2026" }); +assert_eq!(set.render("page", &ctx)?, "

Hello

2026

"); +# Ok::<(), alkgen::Error>(()) +``` + +## Integration patterns + +How rendered text reaches its consumer; the render machinery is +identical in every posture (see `docs/architecture/emit.md`, which +ships in the package): + +- **In-process SSR** (the first real consumer: [alkforge]): link the + crate, register your page templates and fragment partials, render on + demand or behind your own cache. Determinism is what makes caching + safe to add later. Render failure is a typed error your error-page + mapping owns. +- **Build-time code generation**: call the API from a `build.rs` + script (there is no CLI in v1, by design), render each document, and + write the files yourself: the engine returns text and owns nothing + about file placement. For artifacts that get checked in, the + family's hygiene convention applies at the emit layer: generated + files start with an "Auto-generated by alkgen. DO NOT EDIT." banner + and regeneration is diff-quiet because rendering is deterministic. +- **Render-to-compile** (future): render shader or other target text + and feed it to a downstream compiler, the alkminer-v1 + `ShaderBuilder` shape. No new surface is required from the engine + for this. + +## What 0.1.0 does not ship + +Engine-owned template sets: the BAST codec set (binary +readers/writers/validators for alkcall channels), protocol handler +sets, and the WGSL set are future activations, each behind its own +review; the `bast-codegen` cargo feature is declared but gates +nothing. The ssr-html target is proven in-crate through +consumer-shaped fixture sets that register through the same public +API a real consumer uses. Your template sets are yours: register them +from your crate through this API. + +## Design record + +`docs/architecture/` ships in the package: the component spec +(`core.md`, `emit.md`), the activation queue (`targets.md`), and the +decision records (ADR-001 through 010) this README summarizes. + +## License + +Dual-licensed under [MIT](LICENSE-MIT) or +[Apache-2.0](LICENSE-APACHE), at your option.