# 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." The workflow: 1. Make the change 2. Verify: `cargo test --release`, `cargo clippy --all-targets -- -D warnings`, `cargo doc --no-deps` if docs changed, `cargo build --target wasm32-unknown-unknown --release` if layout/wasm-relevant code changed 3. Inspect `git status` and `git diff` before staging — stage only the intended files, never secrets 4. Write a concise commit message matching the repo style (see `git log --oneline -10`). For multi-point changes, use a summary line plus a body with bullet points and a verification block. 5. `git push origin main` 6. 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 repo is on crates.io; the public surface is the 0.1.0 contract — `AlkTypeEngine`, `Schema`/`Definitions`/`Discriminator` builder types, `FieldValue`, `AlkTypeKind`, `Endian`, `VariableEncoding`, `DiscriminatorKind`, `LayoutMode`, `OffsetMap`/`ByteRange`, `LayoutBuilder`/`PackedLayout`/ `FieldPosition`, `SequentialReader`, `UnionDispatch`, `AlkTypeError` variants, and the `data_access`/`schema`/`tunion`/`materialize`/ `validation` public function signatures). Additive, non-breaking changes (new methods, new error variants, new builder setters) are fine to commit; renames, removals, signature changes, or behavioral shifts on existing public items are not. - 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.2 `). Do not change `git config`, skip hooks, or use `git commit -i`. ## Project Conventions (Rust / binary struct engine) This is a binary struct engine crate. The conventions below 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. 1. **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 (e.g., "the `u32` at offset 1 is unaligned — correct for protocol wire formats, which pack fields tightly"). 2. **Error handling** — `AlkTypeError` (the hand-rolled enum in `src/error.rs`) is the library error type. No `anyhow` or `thiserror` — this is a library crate with a single error enum covering the three engine phases (schema, offset, access) plus validation. Never panic in library code. No `unwrap()` or `expect()` outside tests — if you reach for `unwrap`, the error path wasn't specified, stop and decide what should actually happen. 3. **Schemas are untrusted input** — every engine path that walks a schema must return `Err` on a malformed schema, never `panic!`/ `unreachable!`. The downstream `alkcall` consumer accepts schemas from arbitrary internet peers in its hub/spoke topology, so a malicious or unsupported schema definition must produce a handleable error, not a crash. If you add a new `AlkTypeKind` variant, the `k if k.is_fixed_size()` guard pattern in `offset_map` and `layout_builder` will catch it at runtime via the `_ => Err(...)` arm; listing all fixed-size kinds explicitly to restore compile-time exhaustiveness is a separate cleanup, not a blocker (review #002, L2). 4. **Overflow safety** — use `checked_add`/`try_from` for any offset arithmetic or cast that can overflow on adversarial input. The `data_access::write_bytes` u32 truncation guard (review #002, M2) is the canonical pattern: validate the cast, return `AlkTypeError::Access` on failure. Do not regress to bare `as u32` or `+` in production paths. 5. **No `async`** — the engine is fully synchronous. No `tokio`, no `async`/`.await`, no async-sync primitives. Schema compilation, layout walks, read/write, and validation are all blocking, CPU-bound operations. 6. **No feature flags** — the crate has no feature flags and no optional dependencies. `default = []` in `Cargo.toml`. If a future need surfaces (e.g. `no_std` per OQ-002, or an optional strict RFC 3339 validator), raise it as an OQ/ADR before adding one. 7. **WASM-clean** — the only dependencies are `jsonschema` (with `default-features = false`) and `serde_json` (with `preserve_order`). No platform deps, no `std::time`, no filesystem, no threads. Must compile to `wasm32-unknown-unknown`. If you reach for a new dependency, first verify it's wasm-compatible and confirm it's worth the dependency cost. 8. **`serde_json`'s `preserve_order` is load-bearing** — field order in the schema JSON determines byte order in packed mode, and the `OffsetMap`/`PackedLayout` iteration order in both modes. Do not disable the `preserve_order` feature, and do not sort schema object keys anywhere in the engine. 9. **Naming** — Rust standard: `snake_case` for functions/variables/ modules, `PascalCase` for types/traits, `SCREAMING_SNAKE_CASE` for constants. 10. **Module structure** — one module per file under `src/`, re-exported from `src/lib.rs`. Public API surface is the `lib.rs` re-exports; if a new public type or function needs to be visible to consumers, add it to the `pub use` block in `lib.rs`. 11. **No `unsafe`** — the crate has zero `unsafe` blocks and zero `unsafe extern` declarations. Bounds-checked slice access via `data_access::check_bounds` and `get(..)` with `ok_or_else` is the pattern. Do not introduce `unsafe` for performance; the bounds-check-eliding optimization belongs in the `jsonschema`/serde layer, not here. ## Verification Commands Run these before committing. All must pass. ```bash cargo test --release # full suite (~396 tests: 310 crate + 86 integration) cargo clippy --all-targets -- -D warnings cargo doc --no-deps # if docs changed cargo build --target wasm32-unknown-unknown --release # if layout/wasm-relevant code changed cargo publish --dry-run --allow-dirty # before a release ``` ## Architecture Context - `docs/architecture/` — the authoritative spec. Read it before non-trivial changes. ADRs are numbered; OQs (open questions) track resolved/deferred decisions. - ADR-001 — purpose, scope, "schema is the format" principle, jsonschema as the validation engine - ADR-002 — two layout modes (packed sequential vs aligned static); the most important architectural decision - ADR-003 — schema annotations (endianness, alignment, encoding, TUnion discriminators) - ADR-004 — error handling and validation strategy; the `AlkTypeError` enum, load-time build, access-time check - ADR-005 — Int64/Uint64 as first-class kinds; JSON precision caveat - ADR-006 — reject non-final inline length-prefixed variable fields in aligned mode (prevents silent data corruption) - ADR-007 — packed-mode read factory; `engine.sequential_reader()` returns an owned fresh reader (the reader has mutable cursor state) - ADR-008 — reject TUnion in aligned mode for v1 (broken semantics) - ADR-009 — builder API producing `serde_json::Value`; resolves OQ-003 - ADR-010 — `validate_bytes` on `AlkTypeEngine`; materialize `Value` from bytes, then validate - If a TODO references a design direction that an ADR has since decided against, the TODO is stale — remove it and align with the ADR. Do not implement the rejected design. - OQ-001 (deferred): arrays of variable-length-element structs — blocked on a concrete consumer that needs interleaved variable-stride arrays. - OQ-002 (deferred): `no_std` + `alloc` support — blocked on an embedded use case; the core engine is already allocation-free, `jsonschema` is the only `alloc` consumer.