Files
alktype/AGENTS.md
glm-5.2 fb3a27f974 Pre-publish docs sweep: README, AGENTS.md, licenses, inline doc fixes
- Add README.md reflecting the v0.1.0 state: 19 AlkType kinds, two
  layout modes, builder + AlkTypeEngine usage example (verified to
  compile and run), validation entry points, crate independence,
  untrusted-schemas guarantee, docs pointers. Mirrors the alkvault
  README structure.
- Add AGENTS.md with alktype-specific git workflow, project
  conventions (no comments, AlkTypeError, untrusted schemas, overflow
  safety, no async, no feature flags, wasm-clean, preserve_order
  load-bearing, no unsafe), verification commands, and ADR/OQ index.
  Blocks auto-commit on semver-relevant public API changes per the
  crates.io 0.1.0 contract.
- Add LICENSE-MIT and LICENSE-APACHE (dual MIT/Apache-2.0, matching
  alkvault and the Cargo.toml license field).
- Cargo.toml: add readme, keywords, categories, rust-version = "1.85".
- Fix broken intra-doc link in builder.rs: DiscriminatorKind ->
  crate::schema::DiscriminatorKind (cargo doc now warning-free).
- N1 (review #002): document is_rfc3339_timestamp as non-strict in the
  function doc comment. Lists the specific gaps (day-of-month per
  month, seconds range, leap seconds) and points consumers needing
  strict validation to chrono/time.
- N2 (review #002): document the FieldValue::Bytes-for-Record API
  asymmetry in the FieldValue enum doc and on read_record_value.
- .opencode/agents/implementation-specialist.md: point to AGENTS.md
  for full convention details (matches the alkvault pattern).
- review #002: mark N1/N2 resolved; all 7 findings now closed.

Verification:
- cargo test --release: 396 tests pass (310 crate + 86 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo doc --no-deps: clean (no broken intra-doc link warnings)
- cargo build --target wasm32-unknown-unknown --release: clean
- cargo publish --dry-run --allow-dirty: clean
2026-08-11 09:33:33 +00:00

8.7 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."

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 <glm-5.2@alk.dev>). 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 handlingAlkTypeError (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.

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.