Files
alkvault/AGENTS.md
T
glm-5.3-flash 8e01fce5de docs: remove em dashes from prose across the repo
Adds the alk-family "no em dashes in prose" convention (as AGENTS.md
convention 10, mirroring alktty/alktunnels/alksocks) and rewrites the
existing prose to comply: markdown docs under docs/ (ADRs, spec docs,
OQs, sdd_process), README.md, CHANGELOG.md, Rust doc and inline
comments under src/, AGENTS.md, and the agent prompts under
.opencode/agents/. Punctuation substitutes are varied (colons,
parentheses, commas, semicolons, sentence splits); wire data, JSON
examples, and code identifiers are untouched. Prose-only change; no
code, API, or wire-format changes.

Also:
- ADR-021: add a historical note that the embedded code snippets
  predate review 001 (decrypt now returns Zeroizing<String>); the
  snippets are intentionally preserved as the decision record
- protocol.md: align the DerivedKey deserialization error snippet with
  the current error string in src/protocol.rs
- docs/reviews/ is exempt per convention 10 (archived review records)

Verification:
- grep for em/en clause dashes over docs/, src/, tests/, README.md,
  CHANGELOG.md, AGENTS.md, .opencode/agents/: clean (docs/reviews/
  exempt)
- cargo test --all-features: 108 passed, 0 failed
- cargo test: 101 passed, 0 failed
- cargo clippy --all-features --all-targets: clean
- cargo doc --no-deps --all-features: clean
2026-11-04 06:43:36 +00:00

6.9 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 --all-features, cargo clippy --all-features --all-targets, cargo doc --no-deps --all-features if docs 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 published wire formats or semver-relevant public API (this repo is on crates.io; EncryptedData is frozen per ADR-018)
  • 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 / cryptographic library)

This is a cryptographic vault 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., "fresh per call; IV reuse under the same key is catastrophic").

  2. Error handling: thiserror for library error types, anyhow for application code (this crate is a library; use thiserror). No panics 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. For poisoned RwLock/Mutex, use unwrap_or_else(|e| e.into_inner()) so a panic in one operation does not cascade to other operations.

  3. Cryptographic nonces use OsRng. AES-GCM IVs and any other cryptographic nonces must use SysRng/OsRng (or equivalent CSPRNG), never rand::random(). IV reuse under the same key is catastrophic for GCM (authenticity breaks, two-time-pad on plaintext). See docs/architecture/encryption.md §Security Constraints.

  4. Secret material is zeroized on drop: any type holding derived keys, decrypted credentials, or other secret material must derive Zeroize and ZeroizeOnDrop. Secrets must not linger in freed heap memory. See EncryptionKey and Seed for the pattern.

  5. Feature flags: secp256k1 is a feature gate for the Ethereum BIP-0032 derivation path. The base crate compiles lean (no networking, no tokio, no secp256k1 unless the feature is on). Verify both cargo test (default) and cargo test --all-features pass.

  6. No async. This crate is synchronous (std::sync::RwLock, direct method calls). No tokio dependency (ADR-025). Do not introduce async/.await or async-sync primitives.

  7. Naming (Rust standard): snake_case for functions/variables/ modules, PascalCase for types/traits, SCREAMING_SNAKE_CASE for constants.

  8. Module structure. Keep one module per file under src/, re-exported from src/lib.rs; the public API surface is the lib.rs re-exports.

  9. Wire format is frozen. EncryptedData is a stable wire format shared with alknet-storage and the TypeScript @alkdev/storage consumer by type-level agreement (ADR-018). Fields, encoding, and semantics are locked. No field may be removed or renamed; new fields must be optional (default on deserialization) and must not change the meaning of existing fields. The salt field is unused in v2 key derivation (ADR-020) but retained for wire-format compatibility; do not remove it.

  10. No em dashes in prose. Do not use the em dash (U+2014) in source doc comments, markdown docs (docs/, README.md, CHANGELOG.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. Inside fenced code blocks the rule applies to prose-y content only: line comments and ASCII-diagram label separators are rewritten too, while wire data, JSON examples, and tool transcripts survive verbatim. The same rule covers en dashes used as clause separators, though en dashes inside numeric ranges keep their normal meaning. Hyphens in compound words (key_version, version-indexed) are untouched. The one exemption is docs/reviews/: reviews are archived historical records, so their prose is preserved verbatim and is excluded from the em-dash greps.

Verification Commands

Run these before committing. All must pass.

cargo test --all-features          # full suite (~108 tests)
cargo test                        # default features (~101 tests)
cargo clippy --all-features --all-targets
cargo doc --no-deps --all-features   # if docs changed
cargo publish --dry-run --all-features --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-018: vault standalone, EncryptedData wire format lock
  • ADR-020: HD derivation for encryption keys, salt unused in v2
  • ADR-021: key rotation via version-indexed derivation paths
  • ADR-025: local-only dispatch, no async/tokio/irpc
  • If a TODO references a "Phase B" or a design direction that an ADR has since decided against, the TODO is stale; remove it and align the comments with the ADR. Do not implement the rejected design.