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:
1 parent
a7ebd28303
commit
ee4053e3e2
3 files changed
+178
-12
No files matched your search
@@ -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
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
Reference in new issue
Block a user