Files
alkgen/tasks/implementation/w1-error-model.md
T
glm-5.3-flash 02037c4f10 feat(error): the public error enum per core.md error model
- src/error.rs: pub enum Error via thiserror with Registration
  (#[from] TemplateError) and Render (#[from] RenderError) variants;
  substrate errors stay reachable through the source chain
- src/lib.rs re-exports alkgen::Error per convention 10
- six unit tests pin variant mapping, source-chain reachability, and
  operation-naming Display strings
- no catch-all; no context-build arm in this scope (recorded
  interpretation, flagged for the wave-1 gate)

Verification: cargo test, clippy -D warnings, fmt --check, cargo doc,
wasm32-unknown-unknown release build, taskgraph validate (14 tasks)
all green
2026-11-06 19:54:47 +00:00

5.3 KiB

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
w1-error-model Wave 1: the public error model completed
w1-manifest
single low project implementation

Description

Land the crate's one public error enum (core.md §Error model; this is the contract every later task and every consumer maps against):

  • pub enum Error in src/error.rs, via thiserror (AGENTS.md convention 2), covering the engine-owned operations core.md names: registration, template parsing, and rendering. A malformed template (registration-time parse failure) and a failed render are data: typed variants wrapping the substrate's handlebars::TemplateError and handlebars::RenderError. No panics, no unwrap/expect outside tests.
  • No catch-alls: every variant names the operation that failed, and every engine-owned failure path maps to exactly one variant. No lossy flattening: the underlying substrate error stays reachable (source chain via thiserror #[from]/#[source]), for consumers that need detail (alkforge maps render failure to an error page).
  • No context-build arms in this scope: no engine-owned template set ships in v0.1.0, and consumer context builders own their errors (ADR-008). The arm lands additively with the first engine-owned set. Recorded interpretation, flagged for the wave-1 gate.
  • src/lib.rs re-exports the enum per convention 10; doc comments on the public surface (no inline // comments per convention 1).

Acceptance Criteria

  • pub enum Error with registration/parse and render variants wrapping TemplateError/RenderError; the substrate error stays reachable through the source chain
  • no catch-all variant; registration, parse, and render failures each map to exactly one variant
  • no unwrap/expect in src/; no panics
  • re-exported from src/lib.rs; doc comments on public items
  • cargo test, cargo clippy --all-targets -- -D warnings, cargo fmt --check pass

References

  • docs/architecture/core.md §Error model
  • docs/architecture/decisions/002-handlebars-substrate-strict-mode.md (decision 3: typed fallible registration)
  • docs/architecture/decisions/008-template-set-ownership.md (consumer builders own their errors)
  • docs/plans/implementation.md (the context-build-arms interpretation)

Notes

Design: two variants total. Registration and template parsing are one variant because the substrate's register_template_string parses at registration time and returns Result<(), TemplateError>; there is no substrate operation that can fail as "registration but not parse". Registration names the operation and its doc comment states that parse failures surface under it, so the core.md three-operation list (registration, parsing, rendering) is fully covered. No #[non_exhaustive]: the plan records the enum's evolution policy (the context-build arm lands additively pre-1.0, and the freeze inventory carries the policy post-publish), and #[non_exhaustive] would tax every consumer match arm for a guarantee the plan does not call for. Revisit at publish review if the freeze inventory wants it.

Summary

Agent fills this on completion.

Landed 2026-11-06. src/error.rs holds the one public enum (core.md §Error model), src/lib.rs re-exports alkgen::Error (convention 10):

  • Error::Registration(#[from] handlebars::TemplateError): template and partial registration failure, including the registration-time parse failure. Doc comment points to the substrate error carrying reason, template name, and position.
  • Error::Render(#[from] handlebars::RenderError): render failure, e.g. a missing context variable under strict mode. Doc comment points to the substrate error carrying the originating template.

The no-catch-all and one-variant-per-failure criteria hold by construction (only the two substrate error types exist on the two engine-owned paths). No context-build arm in this scope, per the recorded interpretation, flagged for the wave-1 gate.

Findings for later tasks:

  • The substrate's TemplateError derives std::error::Error with its reason as a plain field, so TemplateError::source() is None; the reason is reachable through the reason() accessor instead. The crate's source chain still satisfies the no-lossy-flattening rule: Error::source() returns the substrate error itself (tests pin this via downcast), whose Display embeds the reason text.
  • RenderError::source() returns the RenderErrorReason; the substrate's strict-mode missing-variable reason is MissingVariable, with its detail in the reason's Display.
  • RenderError::new is deprecated since substrate 5.0; tests use RenderError::strict_error for constructing substrate errors.

Tests (unit, in src/error.rs): malformed template maps to Registration with the substrate error, name ("bad"), and reason reachable through the source chain; strict-mode missing variable maps to Render with the reason reachable; substrate detail (the unclosed beta) survives on the render error; Display for both variants starts with the failing operation's name.

Verification: cargo test (6 passed), cargo clippy --all-targets -- -D warnings, cargo fmt --check, cargo doc --no-deps, and cargo build --target wasm32-unknown-unknown --release all green. No unwrap/expect outside tests, no panics in lib code, no em dashes.