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
This commit is contained in:
glm-5.3-flash committed 2026-11-07 06:07:28 +00:00
1 parent a7ebd28303
commit ee4053e3e2
3 files changed
+178 -12

No files matched your search

+70
View File
@@ -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
+1 -1
View File
@@ -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"
+107 -11
View File
@@ -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", "<h1>{{title}}</h1>{{> footer}}")?;
set.register_partial("footer", "<p>{{year}}</p>")?;
let ctx = json!({ "title": "Hello", "year": "2026" });
assert_eq!(set.render("page", &ctx)?, "<h1>Hello</h1><p>2026</p>");
# 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.