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
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:
- Make the change
- Verify:
cargo test --all-features,cargo clippy --all-features --all-targets,cargo doc --no-deps --all-featuresif docs changed - Inspect
git statusandgit diffbefore staging: stage only the intended files, never secrets - 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. 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 published wire formats or semver-relevant public API
(this repo is on crates.io;
EncryptedDatais 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.
-
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"). -
Error handling:
thiserrorfor library error types,anyhowfor application code (this crate is a library; usethiserror). 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. For poisonedRwLock/Mutex, useunwrap_or_else(|e| e.into_inner())so a panic in one operation does not cascade to other operations. -
Cryptographic nonces use
OsRng. AES-GCM IVs and any other cryptographic nonces must useSysRng/OsRng(or equivalent CSPRNG), neverrand::random(). IV reuse under the same key is catastrophic for GCM (authenticity breaks, two-time-pad on plaintext). Seedocs/architecture/encryption.md§Security Constraints. -
Secret material is zeroized on drop: any type holding derived keys, decrypted credentials, or other secret material must derive
ZeroizeandZeroizeOnDrop. Secrets must not linger in freed heap memory. SeeEncryptionKeyandSeedfor the pattern. -
Feature flags:
secp256k1is a feature gate for the Ethereum BIP-0032 derivation path. The base crate compiles lean (no networking, notokio, nosecp256k1unless the feature is on). Verify bothcargo test(default) andcargo test --all-featurespass. -
No
async. This crate is synchronous (std::sync::RwLock, direct method calls). Notokiodependency (ADR-025). Do not introduceasync/.awaitor async-sync primitives. -
Naming (Rust standard):
snake_casefor functions/variables/ modules,PascalCasefor types/traits,SCREAMING_SNAKE_CASEfor constants. -
Module structure. Keep one module per file under
src/, re-exported fromsrc/lib.rs; the public API surface is thelib.rsre-exports. -
Wire format is frozen.
EncryptedDatais a stable wire format shared withalknet-storageand the TypeScript@alkdev/storageconsumer 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. Thesaltfield is unused in v2 key derivation (ADR-020) but retained for wire-format compatibility; do not remove it. -
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 isdocs/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,
EncryptedDatawire 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.