Adds six sanity-check layers to the release workflow: - preflight: orphaned worktrees, baseline green, plan-state grep, branch collision - cleanup: removes merged worktrees + branches (git branch -d, never -D) - debug artifact scan: dbg!/ console.log / TODO / unwrap() in diff (advisory) - checkbox hygiene: unticked plan tasks before verify (advisory) - pre-release version consistency across Cargo.toml workspace - pre-release tag collision check CLAUDE.md: discipline rules 5 (preflight before develop) and 6 (cleanup after lift). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
13 KiB
CLAUDE.md — Relicario
Working with the user
- Default to "yes" / the recommended option. When asking the user a multiple-choice or yes/no decision, pick the recommended answer and proceed without prompting. Optional follow-ups in checklists: do them. Subagent dispatch / running tests / writing code: proceed without checking.
- Always pause and ask before:
rm,rm -rf,git push --force,git reset --hard,git branch -D, deleting files via Bash, dropping tables, force-pushing to main. The system-prompt's "executing actions with care" guidance still applies — this preference does not override that. - This rule does not override genuine intent-discovery: brainstorming-skill clarifying questions about what to build still need user input, because picking a default would mean designing the wrong product.
- Sprinkle Mexican Spanish into replies. Drop 1–2 Spanish words, slang, exclamations, or idioms per reply (replies only — never in code, file contents, commit messages, or other project artifacts), each followed by
[translation]in square brackets. Mexican flavor is preferred: ¡órale! [alright!], ¡híjole! [yikes!], ¿qué onda? [what's up?], chido [cool], ahorita [right now / in a bit], no manches [no way], ni modo [oh well], no hay bronca [no problem], ¡ya estuvo! [it's done], etc. Skip in one-word acknowledgements where the flourish would feel awkward.
What is this
Relicario is a git-backed, self-hostable password manager with a Rust core. Two-factor vault decryption: passphrase + a reference JPEG carrying a 256-bit secret embedded via DCT steganography. The server only ever sees opaque ciphertext.
Build and test
cargo build # build everything
cargo test # run all tests (unit + integration)
cargo test -p relicario-core # core library tests only
cargo test -p relicario-cli --test basic_flows # CLI integration tests
cargo build -p relicario-wasm --target wasm32-unknown-unknown # WASM target
cargo run -p relicario-cli -- --help # CLI help
cargo run -p relicario-cli -- generate --length 32 # quick smoke test
Project structure
crates/
├── relicario-core/ # Platform-agnostic library (no filesystem, no git, no network)
│ ├── src/
│ │ ├── lib.rs # Re-exports public API
│ │ ├── error.rs # RelicarioError enum (thiserror)
│ │ ├── crypto.rs # Argon2id KDF (length-prefixed, Zeroizing) + XChaCha20-Poly1305
│ │ ├── ids.rs # ItemId, FieldId, content-addressed AttachmentId
│ │ ├── time.rs # now_unix, MonthYear
│ │ ├── item_types/ # per-type cores + ItemType/ItemCore enums
│ │ ├── item.rs # Item envelope, Field, FieldKind, FieldValue, Section
│ │ ├── attachment.rs # AttachmentRef, EncryptedAttachment, encrypt/decrypt helpers
│ │ ├── manifest.rs # Browse-without-decrypt index (schema_version 2)
│ │ ├── settings.rs # VaultSettings: retention, generator defaults, caps
│ │ ├── generators.rs # CSPRNG password + BIP39 + zxcvbn gate
│ │ ├── vault.rs # JSON ↔ AEAD wrappers for Item/Manifest/VaultSettings
│ │ └── imgsecret.rs # DCT steganography (MAX_DIMENSION cap)
│ └── tests/ # integration.rs, attachments.rs, generators.rs, format_v2.rs, field_history.rs
├── relicario-cli/ # `relicario` binary
│ ├── src/main.rs # clap surface + command handlers
│ ├── src/helpers.rs # vault_dir, git_command, iso8601
│ ├── src/session.rs # UnlockedVault (master key in Zeroizing)
│ └── tests/ # basic_flows, edit_and_history, attachments, settings, vault_detection
├── relicario-wasm/ # WASM bindings for the extension
│ ├── src/lib.rs # #[wasm_bindgen] surface
│ └── src/session.rs # opaque SessionHandle → Zeroizing<[u8;32]>
└── relicario-server/ # `relicario-server` binary (pre-receive Git hook)
└── src/main.rs # verify-commit + generate-hook subcommands
Key design decisions
- relicario-core is bytes-in/bytes-out. No filesystem, no network, no git operations. Makes it portable to WASM, Android, iOS.
- XChaCha20-Poly1305 over AES-GCM — 192-bit nonce eliminates collision risk, fast in WASM/ARM without AES-NI.
- Single master_key (no per-entry subkeys) — simpler, sufficient for family vault sizes.
- imgsecret uses central-embed DCT — embeds only in the middle 70% of the image (15% crumple zone for crop tolerance), with majority voting across 5-50 redundant copies.
- QUANT_STEP = 50.0 — higher than typical (25) to survive JPEG recompression down to Q85.
- Device ed25519 keys are separate from the KDF. Revoking a device doesn't require rotating the passphrase or reference image.
Crypto pipeline
passphrase (UTF-8 bytes) || image_secret (32 bytes from reference JPEG)
→ Argon2id(salt=vault_salt, m=64MiB, t=3, p=4)
→ master_key (32 bytes)
→ XChaCha20-Poly1305(nonce=random 24 bytes)
→ encrypted Item/Manifest/VaultSettings
Conventions
- Tests use fast Argon2id params (m=256, t=1, p=1) so they don't take forever.
- Test JPEGs are generated synthetically via
make_test_jpeg()— no binary test fixtures. - Item IDs and Field IDs are random 16-char hex strings (64 bits of OsRng entropy). AttachmentIds are content-addressed: first 32 hex chars of SHA-256 over the plaintext (128 bits).
- Git history is preserved as an audit log — no squashing.
- The CLI shells out to
gitfor sync — no libgit2/gitoxide dependency.
Remote
Source code: ssh://git@git.adlee.work:2222/alee/relicario.git
Planning & design specs
Before starting any planning or implementation task, search docs/superpowers/specs/ for a spec covering the feature area, and docs/superpowers/plans/ for any existing implementation plan. The specs are the authoritative design record; plans track per-milestone implementation details. Once a plan exists, execute it via the release workflow (see Release lifecycle below) — not directly via subagent-driven-development or executing-plans unless the workflow is unavailable.
Core references (read before touching crypto, data model, or architecture):
docs/superpowers/specs/2026-04-11-relicario-design.md— threat model, entropy analysis, crypto pipeline, crate layoutdocs/superpowers/specs/2026-04-18-relicario-typed-items-design.md— typed-item data model and envelopedocs/superpowers/specs/2026-04-30-relicario-fullscreen-ux-redesign-design.md— fullscreen UX phase plan
After completing any dev iteration, update STATUS.md to reflect what shipped and what's now in flight. Update the component doc for any area you changed (see table below).
Release lifecycle
The release workflow (.claude/workflows/release.js) is the default execution layer for all dev work. Invoke it via the Workflow tool or the /release skill. Full reference: docs/superpowers/RELEASE-WORKFLOW.md.
Standard actions
| Action | When | How |
|---|---|---|
develop + mode:"single" |
Implement a plan; phone/remote; fire-and-forget | Workflow({name:"release", args:{action:"develop", mode:"single", release:"<label>"}}) |
develop + mode:"multi" |
Parallel streams; at PC; PM supervises devs | Workflow({name:"release", args:{action:"develop", mode:"multi", release:"<label>"}}) |
debug |
Fix a failing test or broken feature after manual testing | Workflow({name:"release", args:{action:"debug", context:"<paste failure>"}}) |
verify |
Confirm tests pass before releasing | Workflow({name:"release", args:{action:"verify"}}) |
release |
Cut and tag a version | Workflow({name:"release", args:{action:"release", release:"<label>"}}) |
Execution defaults
- Single-plan work →
mode:"single". One agent works through tasks sequentially; updatesSTATUS.mdautomatically on completion. - Multi-plan or multi-phase work →
mode:"multi". PM agent reads plans, assigns dev streams (up to 6), generates prompt files + a<release>-launch.shindocs/superpowers/coordination/. Run the launch script — it starts the relay and opens a tmux session. - Debugging → always
action:"debug". Never hand-fix without at least trying the debug loop first. - Releasing → always
action:"release". It verifies first, writes CHANGELOG, tags, and stops before push.
Multi-agent relay
The relay server (tools/relay/) supports roles pm, dev-a through dev-f. The launch script starts it automatically. If you need to start it manually: cd tools/relay && ./start.sh. Protocol reference: docs/superpowers/coordination/RELAY.md.
Roadmap & status
Current in-flight work: STATUS.md. Full roadmap with release targets: ROADMAP.md. Wire format reference: docs/FORMATS.md.
Living docs — update discipline
| File | What it documents | Update when... |
|---|---|---|
DESIGN.md |
Cross-codebase structure: four codebases, contracts, secrets map, build matrix, test strategy | Adding a codebase, changing inter-codebase contracts, new build targets |
docs/CRYPTO.md |
Crypto pipeline diagrams, vault creation/unlock flows, DCT embedding, encrypted file format | Changing crypto primitives, format version byte, or file format |
crates/relicario-core/ARCHITECTURE.md |
Module map, invariants, key flows, test architecture for relicario-core |
Adding/changing modules, item types, or crypto invariants in core |
crates/relicario-cli/ARCHITECTURE.md |
Module map, invariants, key flows (init, unlock, all commands) for relicario-cli |
Adding/changing CLI commands, helpers, or session behavior |
extension/ARCHITECTURE.md |
Bundle structure, SW↔popup contract, component architecture | Adding bundles, changing the SW message protocol, or major UI flows |
docs/SECURITY.md |
Threat model, device auth, env-var trust surface | Adding env vars, changing auth model, new security-relevant config |
docs/FORMATS.md |
Wire formats: .enc blobs, params.json, devices.json, manifest schema |
Changing any serialized format, version number, or on-disk layout |
STATUS.md |
In-flight work, recent landings, what's next | End of every dev iteration |
ROADMAP.md |
Full roadmap with release targets | When milestones shift or new work is scoped |
CHANGELOG.md |
User-facing release history | When tagging a release |
Discipline rules
Four rules to prevent the kind of drift the 2026-05-30 audits found:
-
Scope-boundary check. When editing a tour doc, verify the change fits the doc's scope header. If it doesn't, the change belongs in a different doc — move it instead of stretching the scope. Concretely: a sentence about crypto added to
DESIGN.mdbelongs indocs/CRYPTO.md; a wire-format table added todocs/CRYPTO.mdbelongs indocs/FORMATS.md. -
Code-constant pinning. When a tour doc cites a code constant (
VERSION_BYTE = 0x02,QUANT_STEP = 50.0,MIN_COPIES = 5,MANIFEST_SCHEMA_VERSION = 2, etc.), the doc must cite the source file + line. When the underlying constant changes, grep for the citation pattern and update the docs together with the code change in the same commit. Most drift the audit found was code-constant drift — this rule attacks it at the source. -
New-doc rule. When adding a tour doc, also update (a)
DESIGN.md's code-map, (b) the reading-order sequence (the "Next:" footer chain), and (c) the living-docs table above. A new doc that doesn't appear in all three is not done. -
Plan-state hygiene. Plan checkboxes and
STATUS.md/ROADMAP.mdmust reflect what's actually shipped. Two halves:- Ship side: when a commit lands work that maps to a plan task, tick that plan's checkboxes in the same commit (or the immediately-following docs commit). Same for
STATUS.md— the "Up next" list does not get to lag the actual state ofmainby weeks. - Execute side: before starting execution of a plan whose checkboxes are all unchecked, spot-check git log (
git log --oneline --all --grep <distinctive-name>) or grep for a distinctive symbol/file the plan would create. A plan whose work already merged is the worst kind of plan to re-execute. The 2026-05-30 status-audit found Phase 2B, v0.5.1 Streams A/B/C, and 1C-γ all stealth-shipped two-to-three weeks earlier because nobody ran this check.
- Ship side: when a commit lands work that maps to a plan task, tick that plan's checkboxes in the same commit (or the immediately-following docs commit). Same for
-
Pre-flight before develop. Before running
action:"develop"on any release, runaction:"preflight"first. If preflight reports FAIL (baseline not green or version mismatch), fix the failure before proceeding. WARN results (orphaned worktrees, partially-done plan) require a judgement call — acknowledge them explicitly before proceeding. -
Cleanup after every lift. Once all PRs for a release are merged into main, run
Workflow({name:"release", args:{action:"cleanup"}})to remove the lift's worktrees and feature branches. Stale worktrees accumulate silently and create confusion for the next lift's branch-collision check.