Guidelines for AI coding agents working in this Rust codebase.
If the user tells you to do something, even if it goes against what follows below, YOU MUST LISTEN. THE USER IS IN CHARGE, NOT YOU.
YOU ARE NEVER ALLOWED TO DELETE A FILE WITHOUT EXPRESS PERMISSION. Even a new file that you yourself created, such as a test code file.
YOU MUST ALWAYS ASK AND RECEIVE CLEAR, WRITTEN PERMISSION BEFORE EVER DELETING A FILE OR FOLDER OF ANY KIND.
- Absolutely forbidden commands:
git reset --hard,git clean -fd,rm -rf, or any command that can delete or overwrite code/data must never be run unless the user explicitly provides the exact command and states, in the same message, that they understand and want the irreversible consequences. - No guessing: If there is any uncertainty about what a command might delete or overwrite, stop immediately and ask the user for specific approval. "I think it's safe" is never acceptable.
- Safer alternatives first: When cleanup or rollbacks are needed, request permission to use non-destructive options (
git status,git diff,git stash, copying to backups) before ever considering a destructive command. - Mandatory explicit plan: Even after explicit user authorization, restate the command verbatim, list exactly what will be affected, and wait for a confirmation that your understanding is correct. Only then may you execute it — if anything remains ambiguous, refuse and escalate.
- Document the confirmation: When running any approved destructive command, record (in the session notes / final response) the exact user text that authorized it, the command actually run, and the execution time. If that record is absent, the operation did not happen.
The default branch is main.
- All work happens on
main— commits, PRs, feature branches all merge tomain - Treat lingering
masterreferences as stale unless the user explicitly asks about legacy history
decoding is a deterministic convergence engine for legacy-system archaeology. It consumes derived claim.v0 events from crucible scan and produces canonical entries where claims converge, escalations where they conflict, and convergence reports summarizing the state of resolution.
decoding sits downstream of crucible and upstream of human review:
legacy estate
-> crucible scan
-> metadata catalog (direct observations — bypass decoding)
-> derived claim.v0 (ambiguous/inferential — goes through decoding)
-> decoding archaeology
-> canon_entry.v0 + escalation.v0 + convergence.v0
decoding only owns derived claims — inferred values, liveness assessments, semantic labels, weak dependency edges. Directly observed metadata (table existence, file inventory, mechanically extractable lineage) lands in the catalog and bypasses decode entirely.
- Observed metadata — facts directly recoverable from scans, normalized into the metadata catalog
- Derived claims — propositions that are ambiguous, inferential, or contradicted across sources
If an implementation finds itself parsing catalog records directly, the boundary has drifted.
- Package manager: Cargo only, never anything else
- Edition: Rust 2024 (follow
rust-toolchain.toml) - Unsafe code: Forbidden (
#![forbid(unsafe_code)]) - Dependencies: Explicit versions, small and pinned
[profile.release]
opt-level = "z" # Optimize for size (lean binary for distribution)
lto = true # Link-time optimization
codegen-units = 1 # Single codegen unit for better optimization
panic = "abort" # Smaller binary, no unwinding overhead
strip = true # Remove debug symbolsRun after any substantive code changes:
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo testNEVER run a script that processes/changes code files. Make code changes manually.
Revise existing code files in place. NEVER create variations like main_v2.rs.
We do not care about backwards compatibility — we're in early development. Do things the RIGHT way with NO TECH DEBT.
# Read the spec first
sed -n '1,100p' docs/PLAN_DECODING.md
# See the execution graph
br ready
br blocked
# AI-agent prioritization
bv --robot-next
bv --robot-triage --robot-max-results 5
# Quality gate
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test
ubs .- Spec:
docs/PLAN_DECODING.md— all behavior must follow this document - Execution graph: .beads/issues.jsonl
- Do not invent behavior not present in the plan
- If code, README, and plan disagree, the plan wins
| Path | Responsibility |
|---|---|
src/main.rs |
Thin binary entrypoint only |
src/lib.rs |
Module root and shared library surface |
src/cli.rs |
Clap surface, exit-code mapping, file loading orchestration |
src/contracts/{mod,claim,canon_entry,escalation,convergence,policy,vocabulary}.rs |
Wire contracts, serde schemas, contract validation, frozen enums |
src/normalize.rs |
Canonical JSON, string normalization, sorted-set helpers, hash helpers |
src/bucket.rs |
Logical bucket keys, edge/base bucket construction, bucket grouping |
src/compare.rs |
Property-aware comparator registry |
src/resolve.rs |
State machine and resolution decisions |
src/render.rs |
canon_entry and escalation JSONL output rendering |
src/report.rs |
Convergence summary generation |
tests/contracts/*.rs |
Parse/refusal and schema tests |
tests/fixtures/*.rs |
Mixed-source archaeology fixtures |
tests/snapshots/*.rs |
Explanation and output snapshots |
Critical structural rule:
src/main.rsstays thin- module declarations and shared APIs belong in
src/lib.rs
Target domain outcomes:
| Exit | Outcome | Meaning |
|---|---|---|
0 |
Clean | All claims converged, no escalations |
1 |
Escalations | One or more buckets escalated for human review |
2 |
Refusal | Invalid claim set, invalid policy, or contract violation |
Target output routing:
--output <FILE>: canon_entry.v0 JSONL (default: stdout)--escalations <FILE>: escalation.v0 JSONL--convergence <FILE>: convergence.v0 JSON summary--json: JSON status messages on stderr- stderr without
--json: human-readable status only
Refusal output goes to stderr. Refusals are contract violations, not domain outcomes.
Same input claim set + same policy file = byte-for-byte identical canon_entry.v0, escalation.v0, and convergence.v0 outputs. No randomness, no timestamp-dependent behavior.
Malformed or unknown claims must fail fast at the refusal boundary (exit 2) and never leak into escalation handling. Refusal conditions: malformed JSONL, missing required fields, malformed claim_id, unknown source.kind, unknown subject.kind, unknown property_type, value shape mismatches, unknown policy keys.
Edge properties (reads, writes, depends_on, used_by, authoritative_for) use an extended bucket key that includes (value.kind, value.id). One subject can have many independent targets without collapsing into a single bucket.
Repeated identical claim_ids collapse to one logical claim before bucketing. Source-artifact distinct counting is computed from surviving distinct claims. Explanation payloads never repeat the same claim_id.
liveness uses special fold rules. Structural evidence alone is weak. Absence of evidence is not death. Prefer alive, stale, or unknown over overclaiming dead.
decoding only owns derived claims. If the implementation starts parsing table/file/resource/link catalog records directly, the boundary has drifted and should be corrected. Direct observations belong in the metadata catalog.
Unknown source.kind, subject.kind, or property_type values are refusal conditions, not escalation conditions. Do not add vocabulary entries without freezing them in the plan first.
Every canonical entry carries a structured explain block with winner_claim_ids, compatible_claim_ids, and resolution_kind. Free-text commentary is not part of Phase 1.
bucket_id must be computed from canonical JSON of the logical bucket key with deterministic key ordering. If bucket identity is unstable across identical reruns, stop and fix normalization before widening the fixture corpus.
Note: br is non-invasive — it NEVER executes git commands. After br sync --flush-only, you must manually run git add .beads/ and git commit.
Beads is the execution source of truth in this repo.
- Beads = task graph, state, priorities, dependencies
- Agent Mail = coordination, reservations, audit trail
br ready # Show unblocked ready work
br list --status=open # All open issues
br show <id> # Full issue details with dependencies
br create --title="..." --type=task --priority=2
br update <id> --status=in_progress
br close <id> --reason "Completed"
br sync --flush-only # Export to JSONL (NO git operations)- Include bead IDs in coordination subjects, e.g.
[dc-1cp] Start: bucket store - Use the bead ID in reservation reasons for traceability
- Prefer concrete ready beads over the epic tracker
- Start with
br readyand pick one unblocked bead. - Mark it
in_progressbefore coding. - Reserve exact files and send start message.
- Implement + validate.
- Close bead, send completion summary, release reservations.
If you are blocked or idle:
- Run
br ready - Pick an unblocked bead and continue
- If none are ready, report blockers and state the next fallback task
Use bv robot mode when dependency-aware prioritization is unclear:
bv --robot-triage # Full triage view with recommendations
bv --robot-next # Single top recommendationImportant: use only --robot-* commands in automation. Bare bv opens an interactive TUI.
Golden Rule: ubs <changed-files> before every commit. Exit 0 = safe.
Useful patterns:
ubs $(git diff --name-only --cached) # staged files
ubs --only=rust,toml src/ # language-filtered scan
ubs --ci --fail-on-warning . # CI-style strict runUse ast-grep when structure matters:
- codemods/refactors
- syntax-aware policy checks
- safe pattern rewrites
Use rg when text search is enough:
- finding literals/config keys/TODOs
- fast repository reconnaissance
Rule of thumb:
- structural match or rewrite ->
ast-grep - textual search ->
rg
Commit and push after completing each bead. Do not accumulate work across multiple beads before committing. The workflow for every bead is:
- Claim the bead (
br show <id>, add a comment that you're starting) - Implement the work
- Run quality gates:
cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test - Commit and push immediately:
br sync --flush-only git add .beads/ <changed files> git commit -m "<bead-id>: <short description of what was done>" git push
- Close the bead:
br close <id> --reason "Completed" - Move to the next bead
Do NOT:
- Work on multiple beads before committing
- Accumulate a large uncommitted diff
- Wait until the end of the session to commit
- Skip the push step
Do:
- Keep commits small and focused (one bead = one commit)
- Push after every commit so other agents can pull your changes
- Run
git pull --rebasebefore starting each new bead to pick up others' work
Agent Mail is the coordination layer for multi-agent sessions in this repo: identities, inbox/outbox, thread history, and advisory file reservations.
- If direct MCP Agent Mail tools are available, ensure project and reuse your identity:
ensure_project(project_key="/Users/zac/Source/cmdrvl/decoding")whois(project_key, agent_name)orregister_agent(...)only if identity does not exist
- Reserve only exact files you will edit:
- Allowed:
src/bucket.rs,src/compare.rs - Not allowed:
src/**,src/contracts/**, whole directories
- Allowed:
- Send a short start message and finish message for each bead, reusing the bead ID as thread.
- Check inbox at moderate cadence (roughly every 2-5 minutes), not continuously.
When this repo is worked via ntm, the session may be connected to Agent Mail even if the spawned harness does not expose direct mcp__mcp-agent-mail__... tools.
If direct MCP Agent Mail tools are unavailable:
- do not stop working just because mail tools are absent
- continue with
br, exact file reservations via the available coordination surface, and overseer instructions - treat Beads + narrow file ownership as the minimum coordination contract
- Do not run retry loops for
register_agent,create_agent_identity, ormacro_start_session. - If a call fails with a transient DB/SQLite lock error, back off for 90 seconds before retrying.
- Continue bead work while waiting for retry windows; do not block all progress on mail retries.
- If a message has
ack_required=true, acknowledge it promptly. - Keep bead updates short and explicit: start message, finish message, blocker message.
- Reuse a stable bead thread when possible for searchable history.
- Reserve only specific files you are actively editing.
- Never reserve entire directories or broad patterns.
- If a reservation conflict appears, pick another unblocked bead or a non-overlapping file.
This repo is designed for parallel agent work. Reserve exact files only.
Per-lane target surfaces:
| Lane | Expected files |
|---|---|
| bootstrap | Cargo.toml, src/lib.rs, src/main.rs |
| cli | src/cli.rs |
| contracts — claim | src/contracts/claim.rs, src/contracts/vocabulary.rs |
| contracts — canon_entry | src/contracts/canon_entry.rs |
| contracts — escalation | src/contracts/escalation.rs |
| contracts — convergence | src/contracts/convergence.rs |
| contracts — policy | src/contracts/policy.rs |
| normalize | src/normalize.rs |
| bucket | src/bucket.rs |
| compare | src/compare.rs |
| resolve | src/resolve.rs |
| render | src/render.rs |
| report | src/report.rs |
| fixtures | tests/fixtures/**, test harness files |
| snapshots | tests/snapshots/** |
Do not reserve broad globs like src/** or src/contracts/**.
src/contracts/ owns wire shapes, serde schemas, and validation. Do not put resolution logic or state-machine behavior in contract modules.
src/compare.rs owns property-aware compatibility rules. src/resolve.rs owns state transitions and decision-making. Do not leak resolution policy into the comparator.
src/report.rs owns convergence summary generation and math. src/render.rs only formats existing data into JSONL lines. Do not re-derive counts in the renderer.
src/bucket.rs owns logical bucket keys and bucket_id hashing. src/normalize.rs provides the canonical JSON and hashing primitives. Do not compute bucket IDs anywhere else.
Use the plan vocabulary directly:
claim.v0,canon_entry.v0,escalation.v0,convergence.v0single_source,corroborated,priority_break,liveness_foldconflicted,missing_corroboration,no_resolution_pathreview,scan_more,fix_scanner,fix_policybucket_id,claim_id
Avoid renaming these into "friendlier" local synonyms.
Current repo reality:
- Phase 1 fully implemented — 4,200+ lines of Rust, 75+ tests
- CI workflow at
.github/workflows/ci.yml(fmt + clippy + test) - Release workflow at
.github/workflows/release.yml(cross-platform binaries on tag push) - Smoke workflow at
.github/workflows/smoke.yml(CLI execution tests) - v0.1.0 is the first tagged release
CI/release discipline:
fmt/clippy/test/ubsbefore publish- deterministic artifacts
mainas primary branch
When working alongside other agents:
- Never stash, revert, or overwrite other agents' work
- Treat unexpected changes in the working tree as if you made them
- If you see changes you didn't make in
git status, those are from other agents working concurrently — commit them together with your changes - This is normal and happens frequently in multi-agent environments
Do NOT:
- Stop and ask "I see unexpected changes, what should I do?"
- Offer options like "triage these changes" or "run a full suite"
- Express concern about uncommitted work you don't recognize
Do:
- Continue working as normal
- Include those changes when you commit (they belong to the shared effort)
- Trust that other agents know what they're doing
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
- File issues for remaining work — Create beads for anything that needs follow-up
- Run quality gates (if code changed) — fmt, clippy, test
- Update issue status — Close finished work, update in-progress items
- PUSH TO REMOTE — This is MANDATORY:
git pull --rebase br sync --flush-only git add .beads/ <other files> git commit -m "..." git push git status # MUST show "up to date with origin"
- Verify — All changes committed AND pushed
- Summarize — what changed, what was validated, remaining risks
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing — that leaves work stranded locally
- NEVER say "ready to push when you are" — YOU must push
- If push fails, resolve and retry until it succeeds