- 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
12 KiB
AGENTS.md
Operating instructions for opencode agents working in this repo. opencode
auto-loads this file as instructions, overriding the built-in defaults for
this project. Custom agents in .opencode/agents/ inherit these rules
unless their own prompts say otherwise.
Git Workflow
Commit and push when reasonable. When a change is complete and
verified (build + lint + tests pass), commit and push to origin/main
without asking. This overrides the built-in default of "only commit when
explicitly asked."
Commit in small, focused units: one commit per unit of work (a fix, a feature, a doc change), not one large commit covering many topics. Push regularly so work is never stranded locally.
The workflow:
- Make the change
- Verify:
cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --check,cargo doc --no-depsif docs changed,cargo build --target wasm32-unknown-unknown --releaseif dependency/wasm-relevant code changed - Inspect
git statusandgit diffbefore staging; stage only the intended files, never secrets - Write a concise commit message in conventional commits style:
<type>(<scope>): <summary>; types arefeat,fix,docs,chore,refactor,test,perf,build,ci; the scope is optional. For multi-point changes, use a summary line plus a body with bullet points and a verification block. git push origin main- Report the commit hash and the verification summary
Exceptions; do not commit or push without asking:
- The change is exploratory / speculative (you're not sure the user wants it kept)
- The user is actively reviewing the diff and may ask for changes
- The change touches semver-relevant public API (this crate is headed to crates.io; once published, renames/removals/signature changes on public items are breaking, while additive changes are fine)
- You'd be force-pushing, amending a published commit, creating an empty commit, or skipping hooks
Never commit secrets, keys, or credentials. If a commit fails or hooks reject it, fix the issue and create a new commit. Do not amend the failed one.
Git identity is preconfigured (glm-5.3-flash <glm-5.3-flash@alk.dev>).
Do not change git config, skip hooks, or use git commit -i.
Project Conventions (Rust / template codegen crate)
This is alkgen, the codegen engine of the alk* stack: handlebars-rust
templates rendered over alktype BAST documents to produce code artifacts
(binary readers/writers/validators, protocol handlers), plus plain HTML
generation for alkforge's server-side rendering. Conventions apply to all
work in src/ and tests/. They mirror
.opencode/agents/implementation-specialist.md §Project Conventions and
are repeated here so they apply to every session, not just spawned
implementation agents.
-
No comments in code unless the user explicitly asks. This is a project-wide convention. Doc comments (
///,//!) are fine and expected on public API. Inline//comments only when the user asks or when a non-obvious safety/correctness constraint would otherwise be missed. -
Error handling:
thiserrorfor library error types. No panics in library code. Nounwrap()orexpect()outside tests. If you reach forunwrap, the error path wasn't specified; stop and decide what should actually happen. Template registration/render failures are data, not crashes: a malformed template or BAST input must produce a typed error, never a panic (generated code feeds alkcall, which accepts BAST from arbitrary peers). -
No
async: template rendering and codegen are synchronous, CPU-bound operations. No tokio, no async/await in the core. (If a future file-watch/rebuild service is wanted, that is an assembly- layer concern, not a lib concern.) -
WASM-clean: the crate must compile to
wasm32-unknown-unknown. No filesystem access in library code (handlebarsdir_sourcefeature is off-limits in lib code; templates come from strings orinclude_str!). No threads, nostd::time, no platform deps. If you reach for a new dependency, first verify it is wasm-compatible and worth the cost. -
serde_jsonpreserve_orderis load-bearing: alktype relies on it (field order in schema JSON determines packed byte order). Do not disable it and do not sort schema keys in contexts built for templates unless the template explicitly requires a sort. -
Strict template mode:
Handlebars::set_strict_mode(true)for any registry the lib renders with. A missing variable in a codegen template must be a render error, not silently-empty output (silent gaps become compile bugs in the consumer's build, far from the cause). alkminer-v1'sShaderBuilderis the precedent. -
Templates are code: templates live as
const &str(inline) orinclude_str!files, versioned in-repo, with render tests pinning their output. Changing a template changes generated code; treat template edits with the same care as source edits (tests must pin golden output before merging). -
Generated-code hygiene: generated artifacts start with a "Auto-generated by alkgen. DO NOT EDIT." banner (typebox-rs precedent). Rendering must be deterministic: same BAST + same templates + same options → byte-identical output (no HashMap iteration order leaking into generated code).
-
Naming: Rust standard:
snake_casefor functions/variables/ modules,PascalCasefor types/traits,SCREAMING_SNAKE_CASEfor constants. Template registry names: lowercase kebab or snake, one name per template kind. -
Module structure: one module per file under
src/, re-exported fromsrc/lib.rs. Public API surface is thelib.rsre-exports. -
No
unsafe: zero unsafe blocks. If a perf argument appears, the answer lives in the template/data-model layer, not in unsafe. -
No em dashes in prose. Do not use the em dash (U+2014) in source doc comments, markdown docs (
docs/,README.md,AGENTS.md), agent prompts under.opencode/agents/, or commit messages. Restructure instead: split the sentence, use parentheses, commas, a colon, or a semicolon, whichever reads naturally, and vary the choice rather than converting every instance the same way. A spaced hyphen (-) is not an acceptable substitute; it reads as a different but equally recognizable writing tell. The same rule covers en dashes used as clause separators, though en dashes inside numeric ranges keep their normal meaning. Hyphens in compound words (fail-closed,client-side) are untouched. In test fixtures and scripts the rule applies to prose-y content only: comment lines are rewritten too, while fixture data, wire examples, and tool transcripts survive verbatim. Vendored third- party files under.opencode/node_modules/are out of scope, as are rendered template outputs meant for consumer code.
Verification Commands
Run these before committing. All must pass.
cargo test # full suite
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo doc --no-deps # if docs changed
cargo build --target wasm32-unknown-unknown --release # if deps/wasm-relevant code changed
cargo publish --dry-run --allow-dirty # before a release
grep -rn '—' src/ docs/ README.md AGENTS.md .opencode/agents/ tests/ \
Cargo.toml # must be zero hits; see convention 12
Task files (taskgraph)
Any file under tasks/ is a taskgraph task. Frontmatter is required
(id, name) and its enum fields are closed sets (taskgraph
0.1.3): status: pending|in-progress|completed|failed|blocked (never
done or finished), scope: single|narrow|moderate|broad|system,
risk: trivial|low|medium|high|critical, impact: isolated|component|phase|project, level: planning|decomposition|implementation|review|research. A file without
frontmatter is silently dropped from the graph (validate does not flag
it); prose-only trackers belong under docs/. Run taskgraph validate after writing or editing anything under tasks/; full field
semantics live in docs/sdd_process.md §"Task File Format" /
§"Categorical Estimates".
Lifecycle Status
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)
needs server-side HTML generation for its web view, and alkgen provides
it. The alkforge SSR use case is simpler than the code-generation
branches and is needed before them, so it jumps the queue as the first
real consumer. The generality constraint it brings: the core is a
text-generation engine (contexts + templates + partials → text) and
must not bake reader/writer assumptions into shared core types; HTML,
Rust, and WGSL are all the same shape, proven by handlebars' native
case, the reader POC, and alkminer respectively.
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/ (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/<poc-name>). POCs have relaxed constraints
(comments/unwrap acceptable) since they are exploration tools, not
production code.
Architecture Context
docs/sdd_process.md: the SDD process (phases, roles, task format). Phase 0 (research/POC) runs before architecture; small changes may skip the ceremony per the doc's "reasonable exceptions" clause.docs/research/phase-0.md: the current phase-0 findings + OQ list. Read it before proposing design changes; extend it when research 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/: the authoritative architecture spec (started 2026-11-06, docs indraftstatus).README.mdthere is the index (doc table, ADR table, lifecycle); ADRs are numbered underdecisions/; OQs are tracked inopen-questions.mdwith 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)
/workspace/@alkdev/alktype: the BAST engine (crates.io 0.4.0;docs/architecture/there is the schema/layout/validation spec)./workspace/@alkimiadev/typebox-rs/src/codegen: older handlebars codegen precedent (simple context structs + inline templates)./workspace/@alkminer/reference/alkminer-v1/src/compute/kernel.rs: handlebars-as-JIT precedent (strict registry, partials, render-then- compile with cache)./workspace/@alkdev/alkhttp/src/adapters:from_openapi/from_jsonschemahandler shapes the call-protocol codegen mirrors./workspace/@alkdev/alkcall: channels consumer; generated readers/writers/validators serve its channels./workspace/@alkdev/alkforge+ family ADR-002 (/workspace/@alkdev/alkfamily/docs/architecture/decisions/002-alkforge-gitea-replacement.md): the SSR consumer; the no-deformation rule binds its asks to design input here, not forks of the core./workspace/handlebars-rust: vendored handlebars checkout (head; crates.io 6.4.4 is the pinned stable)./workspace/alkgen-reader-poc: the reader POC crate (standalone mode, 2026-09-08); its Cargo.toml tracks the dep stack branch 1 exercises. Findings live indocs/research/reader-poc-summary.md.