Files
bcbdc273a5 Restructure CLAUDE.md into modular rules + add pr-shepherd command (#750)
* refactor: restructure CLAUDE.md into modular rules and add pr-shepherd command

Trim CLAUDE.md from 710 lines to 92 by moving detailed guidance into
path-scoped `.claude/rules/` files that load on demand. Add a new
`/pr-shepherd` command that consolidates the full PR lifecycle
(review, fix, quality gate, CI fix loop, merge) into one workflow.

Changes:
- CLAUDE.md: keep only essentials (build commands, code style, architecture,
  module specs, config reference, debugging)
- .claude/rules/review-discipline.md: 15+ review rules, scoped to src/**/*.rs
- .claude/rules/database.md: dual-backend rules with SQL dialect translation
  table, scoped to src/db/** and migrations/**
- .claude/rules/safety-and-sandbox.md: safety layer and sandbox rules, scoped
  to src/safety/**, src/sandbox/**, src/secrets/**
- .claude/rules/testing.md: test tiers and patterns, scoped to src/** and tests/**
- .claude/rules/tools.md: tool architecture and implementation pattern, scoped
  to src/tools/** and tools-src/**
- .claude/commands/pr-shepherd.md: 7-phase PR lifecycle command that subsumes
  review-pr, respond-pr, ship, and manual CI fix loops

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address review feedback on CLAUDE.md restructure

- Restore project structure tree in CLAUDE.md (zmanian blocking)
- Create .claude/rules/skills.md with trust model, SKILL.md format,
  selection pipeline, and skill tools (zmanian blocking)
- Restore configuration section with key env vars (zmanian medium)
- Restore "Adding a New Channel" guide (zmanian medium)
- Add heartbeat mention to Workspace & Memory section (zmanian low)
- Fix pr-shepherd: replace `git add -A` with specific file staging (zmanian)
- Fix pr-shepherd: ask user for merge strategy instead of hardcoding --squash (zmanian)
- Fix pr-shepherd: replace `--watch` with polling + 10min timeout (zmanian)
- Fix testing.md: "skipped if DB is unreachable" not "expected to fail" (Copilot)

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address review comments on PR #750

- Narrow `crate::` import rule: `super::` is fine in tests and intra-module refs
- Fix capabilities file naming: `<name>.capabilities.json` sidecar, not bare `capabilities.json`
- Update mechanical verification checklist to match narrowed import rule

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* refactor: move Bedrock docs from CLAUDE.md to src/llm/CLAUDE.md

Bedrock provider details (auth, config, feature flag) belong in the
LLM module spec, not the top-level guide. Added file map entry,
provider table row, and dedicated section in src/llm/CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* refactor: move env var config block out of CLAUDE.md

Replace 20-line config block with one-liner pointing to .env.example
and src/llm/CLAUDE.md. Config details are only needed during deployment,
not everyday coding.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: use gh pr checkout for fork-safe PR checkout in pr-shepherd

Replaces git fetch/checkout with gh pr checkout {number} which
handles both same-repo and fork-based PRs automatically.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address Copilot review round 5 on PR #750

- Add gh pr list and gh pr checkout to pr-shepherd allowed-tools
- Align crate:: import rule in pr-shepherd with updated CLAUDE.md guidance
- Fix vector type in database.md: BLOB (flexible dims), not F32_BLOB(1536)
- Update MCP limitation: stdio/HTTP/Unix transports exist, no streaming

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-09 23:19:25 +00:00

3.1 KiB

paths
paths
src/db/**
src/history/**
migrations/**

Database Rules

Dual-backend persistence: PostgreSQL + libSQL/Turso. All new persistence features must support both backends.

See src/db/CLAUDE.md for full schema, dialect differences, and libSQL limitations.

Adding a New Operation

  1. Decide which sub-trait it belongs to (ConversationStore, JobStore, SandboxStore, RoutineStore, ToolFailureStore, SettingsStore, WorkspaceStore) or create a new one
  2. Add the async method signature to that sub-trait in src/db/mod.rs
  3. Implement in src/db/postgres.rs (delegate to Store/Repository)
  4. Implement in src/db/libsql/<module>.rs (use self.connect().await? per operation)
  5. Add migration if needed:
    • PostgreSQL: new migrations/VN__description.sql
    • libSQL: add CREATE TABLE IF NOT EXISTS to libsql_migrations.rs
  6. Test feature isolation:
    cargo check                                          # postgres (default)
    cargo check --no-default-features --features libsql  # libsql only
    cargo check --all-features                           # both
    

SQL Dialect Translation Checklist

When writing SQL for both backends, translate these types:

PostgreSQL libSQL
UUID TEXT
TIMESTAMPTZ TEXT (ISO-8601, write with fmt_ts(), read with get_ts())
JSONB TEXT (JSON string)
BOOLEAN INTEGER (0/1 -- use get_i64(row, idx) != 0 to read)
NUMERIC TEXT (preserves rust_decimal precision)
TEXT[] TEXT (JSON-encoded array)
VECTOR BLOB (flexible dimensions; vector index dropped, brute-force search fallback)
jsonb_set(col, '{key}', val) json_patch(col, '{"key": val}') -- replaces top-level keys entirely, cannot do partial nested updates
DEFAULT NOW() DEFAULT (datetime('now'))
tsvector + ts_rank_cd FTS5 virtual table + sync triggers

Schema Translation Beyond DDL

Don't just translate CREATE TABLE. Also check:

  • Indexes -- diff CREATE INDEX statements between backends
  • Seed data -- check for INSERT INTO in migrations (e.g., leak_detection_patterns)
  • Triggers -- PostgreSQL functions vs SQLite triggers (no stored procs in SQLite)

Transaction Safety

Multi-step operations (INSERT+INSERT, UPDATE+DELETE, read-modify-write) MUST be wrapped in a transaction. Ask: "If this crashes between step N and N+1, is the database consistent?" If not, wrap in a transaction. Applies to both backends.

libSQL Connection Model

LibSqlBackend::connect() creates a fresh connection per operation with PRAGMA busy_timeout = 5000. This is intentional -- no pool exists. Never hold connections open across await points. Satellite stores (LibSqlSecretsStore, LibSqlWasmToolStore) receive Arc<LibSqlDatabase> via shared_db() and call .connect() themselves -- never pass a live Connection.

Fix the Pattern, Not the Instance

When fixing a bug in one backend's SQL, always grep for the same pattern in the other. A fix to postgres.rs that doesn't also fix libsql/jobs.rs is half a fix. Same applies to satellite stores.