Guidance for any coding agent (Codex, Claude Code, etc.) working in this repo. Claude Code also
reads CLAUDE.md; the two are kept in sync. Full rationale: docs/AGENTIC-ENGINEERING.md.
Deckard is a native, self-custodial Ethereum wallet (GPUI + Rust, macOS + Linux). It is security-sensitive: it holds private keys, BIP-39 seed phrases, and an encrypted keystore. Treat key material with care.
crates/deckard-app— thedeckardGPUI desktop app (the view layer / shell; binarydeckard).crates/deckard-core— the headless engine (Ethereum provider + verified reads, balances, HD keys, encrypted keystore, key-less shield builder). No GPUI dependency; fully unit-testable. Most logic belongs here, not in the app.crates/deckard-contract— the frozen wire contract (Intent / Decision / Policy / RPC / ReadStatus).crates/deckard-signerd— the process-isolated signer daemon (owns the key; UDS server).crates/deckard-wallet-client— shared key-less signer client/account/chain/error primitives for local interfaces.crates/deckard-mcp— MCP/agent interface over shared wallet capabilities.crates/deckard-browser-bridge— EIP-1193 dapp/browser interface over shared wallet capabilities.
GitHub issues are the backlog and product/task tracker. Execution plans are repo-local working artifacts for non-trivial implementation, debugging, security, CI, or UI work.
- Before non-trivial work, read
PLANS.mdand create or update a plan inexecplans/. - Keep plan status notes current as reality changes; do not let plans become stale parallel docs.
- Move durable decisions into
docs/ordocs/adr/; leave the plan as the task log and checklist.
- Iterate fast:
just core— clippy + test the GPUI-free engine (deckard-core) without building the gpui app (the heavy verified-reads/shield deps compile once, then it's fast). Use while working on the engine; the full DoD still applies before done. UI work needsjust check. - Lint:
just check— clippy-D warningson the whole workspace + the app's--features trayconfig. - Format:
just fmt(cargo fmt); CI gatescargo fmt --all --check. - Test:
cargo test --workspace.- macOS note: if signer-daemon integration tests fail before the daemon binds with
path must be shorter than SUN_LEN, rerun with a shorter temp root:TMPDIR=/tmp cargo test --workspace. The default/var/folders/...path can make Unix socket paths too long.
- macOS note: if signer-daemon integration tests fail before the daemon binds with
- Bump the git GPUI stack:
just bump-gpui(the ONLY way to change those pins).
Before changing files, always establish the current branch and its source-of-truth status:
- Run
git status --short --branch. - If on
main, rungit fetch origin --prune, fast-forward fromorigin/main, then create a new feature branch before editing. - If not on
main, rungit fetch origin --pruneand check whether the branch has already been merged into currentorigin/main.- If it has been merged, switch back to
main, fast-forward fromorigin/main, and create a new feature branch before editing. - If it has not been merged, inspect the branch's upstream/ahead/behind state and update local state before editing. Do not stack unrelated work on a stale or merged branch.
- If it has been merged, switch back to
- If there are uncommitted changes, identify whether they are user changes before switching, rebasing, stashing, or applying patches.
After implementing and verifying a task, do not leave the branch as uncommitted local work:
- Commit the changes in logical chunks. Keep unrelated workflow/docs updates separate from feature code when the change is large enough to warrant it.
- Push the feature branch and open a pull request.
- Check CI status for the PR.
- If CI is green and the PR does not require human review/approval, merge it to
mainusing the repo's normal merge strategy. - If CI is red or blocked, report the failing check and fix or ask for guidance before merging.
cargo fmt --all --checkcleanjust checkgreen (both feature configs)cargo test --workspacegreen- No new/changed dependencies in
Cargo.tomlorCargo.lockunless explicitly approved
Never report a task complete while any of these is red.
Enforced workspace-wide (CI fails the build): todo! / dbg! denied; unused_must_use denied;
deckard-core is #![forbid(unsafe_code)] and the app crate is unsafe_code = "deny" (a new unsafe
block needs a reviewed // SAFETY: comment + explicit #[allow]); std::mem::forget /
core::mem::forget / rand::thread_rng denied (use drop() / OsRng).
Enforced in deckard-core via crate-level #![deny(...)]: no .unwrap() / .expect() / panic! /
raw slice indexing in non-test code — propagate with Result / ? and parse untrusted bytes through the
bounded Reader in keystore.rs. The app crate may unwrap infallible GPUI handles; the engine must
not. Genuinely-unrecoverable boundaries use a scoped #[allow] + // reason (see eth.rs), never a
bare unwrap.
Always: never log or Debug-print a seed, key, or passphrase. Secrets live in Zeroizing.
In docs, UI copy, and refusal/error strings: prefer plain words over crypto jargon, and explain a term once where it first appears (e.g. "shield — move funds to a private balance").
Before any visual/UI change, read DESIGN.md (and the constraints in CLAUDE.md). Ground design in
the real reference screenshots, not remembered descriptions.