Python library: unified file storage across backends (Local, S3, SFTP, Azure). Spec-Driven Development (SDD).
- Ship complete: a change is finished when everything it touches is consistent: code, tests, docs, CHANGELOG, BACKLOG. Track gaps as
[~]. For releases, follow the full checklist inCONTRIBUTING.md§ Release. - Verify beyond the diff: search for what references the thing you changed. The ripple-check in
sdd/CLAUDE-REFERENCE.mdhas a Pre-work index (read before starting to anticipate ripples) and a Detailed checklist (read before committing to verify them). You MUST consult both presentations for changes that touch backends, errors, capabilities, versions, specs, or dependencies. - Repo describes reality at every commit: docs, backlog, and CHANGELOG reflect current state, not future intent. Same commit, or mark
[~]. - Single source of truth: Authoritative references live in one place: link to them, don't copy. Examples: ripple-check, CHANGELOG section order, backlog ID format. Copies become stale.
- Specs are source of truth: code vs spec conflict: code is wrong — unless no test ever asserted that clause for the backend in question, which
sdd/000-process.mdRule 7 qualifies (that makes the claim undecided, not the code right), along with prose vs Dafny vs conformance generally. Backlog vs history conflict: backlog is wrong. Fix the less authoritative side. - Run it, don't just type-check it: verify behavior, not signatures. Reproduce bugs before claiming fixes. Test what matters, not just what type-checks.
- Be critical, not agreeable: challenge assumptions, question completeness, flag what's missing. Especially in reviews: a rubber-stamp is worse than no review. Ask what's untested, what could break, what's absent from the checklist.
- Minimize mismatched detail, not detail: durable artifacts — code, docs, specs, tests — keep the detail whose change-rate and correctness-locus fit the artifact, and relocate detail that belongs to another layer to its authoritative home (per principle 4). Brevity is a byproduct of correct placement, never the target: never delete a load-bearing reason to hit a length budget.
- A figure names its derivation: a number, a count, or a claim about an action you took — in a spec, ADR, trace, backlog item, skill file, commit message, PR body, or review reply — states the command, query, or enumeration it came from, and that derivation is run before the sentence is written, not recalled. Re-reading the sentence does not catch the error; opening the source does. A reader can disagree with a derivation, and can only trust a total. Measured failure modes: ADR-0037.
See FEATURES.md for the authoritative list of backends,
extensions, capabilities, and install extras for the current version.
- Report only. Present findings with evidence. Do not fix anything.
- User decides next steps — what to fix, whether to create backlog items or an audit doc.
- An audit's authority is its diagnosis; its prescription is advisory. When implementing a follow-up, re-evaluate the proposed disposition against the diagnosed pain — diverge if a different path fits better.
See sdd/000-process.md § Rule 6 for the canonical pipeline (BACKLOG →
CHANGELOG → failing TEST → FIX → COMMIT together). Per principle 6, write
the failing test, run it, see it fail before implementing the fix.
- See
sdd/BACKLOG.mdfor workflow rules, ID prefixes, completing-work procedure, and active items.sdd/BACKLOG-DONE.mdholds everything that left it: completed work, IDs absorbed into a surviving item, and items decided against. Only the first is "done" — read the section an entry sits under. - Commit messages start with item ID when applicable (e.g.,
AF-008: Add credential masking).
When working on a backlog item, maintain sdd/traces/<id>-<slug>.yml as you work, not after merge. Schema: sdd/traces/_schema.yml. "Working on" means implementing the item, or closing it by implementing it. Two closes do not require a trace, because a trace records what reading and ripples the work met and there was no work: an item decided against and an item absorbed into another, both defined in sdd/BACKLOG.md § Completing work, which carry their decision in the BACKLOG-DONE.md register entry instead. A pure advisory annotation to a body (e.g. recording a verification-run result) that neither implements nor closes the item does not require one either; the trace is authored when implementation begins.
- Before starting: open the trace if it exists, otherwise create one from the schema example.
- As you read: record each gate and reference read as a step. Tag
outcome: unclear | misleadingon any read that did not deliver — these are aggregated byhatch run report-trace-outcomes, which ranks the documents that failed readers, so tag the artifact that actually misled and put your own misses in theextract. - As events occur: fill
discovery_followups(new backlog IDs born during the work),surprising_ripples(paths the ripple-check table did not anticipate),co_shipped_items(other items closed by the same PR). - Before submitting: tag
audiencepriority-sorted; the CHANGELOG-required rule derives from it. - Ship the trace in the same PR as the work. Not a separate later commit.
Scripts are defined in pyproject.toml under [tool.hatch.envs.default.scripts].
Run hatch run to list them. hatch run all is the pre-commit gate.
Claude-specific shell constraints:
- No
&&,||, or;. Split into separate Bash tool calls for auto-approval. - No shell redirects or pipes (
>,2>&1,| tee). Run commands plain and read output from the tool result for auto-approval. - No heredoc in git commits.
git commit -m "$(cat <<'EOF'...)"breaks theBash(git:*)auto-approve pattern. Use multiple-mflags instead. - No
/tmp/. Use./tmp/instead (gitignored)./tmp/is a system directory and triggers a separate permission prompt.
hatch run all uses a Stage-1, no-Docker test variant; the 95% strict gate lives in CI and the publish workflow. See pyproject.toml's test-cov* script comments for which variant to use when.
If test-cov-strict fails locally on coverage, do not loop on "master is passing it, let me re-run" — start Azurite or treat the strict gate as CI-only.
Local test* scripts run a resource-bounded parallel pass via scripts/run_tests.py (it caps xdist workers to leave the machine usable when several suites run at once; RS_TEST_WORKERS=auto or =<n> overrides — see the script docstring) plus a serial pass for sftp_docker conformance. See tests/backends/fixtures/registry.fixture_params for the carve-out. CI keeps pytest -n auto (its workflows call pytest inline, not these scripts). Don't reintroduce --dist loadgroup, MaxStartups tuning, or banner retries: the simpler carve-out is the entire stabilisation story.
hatch run test-isolation is the explicit "prove no hidden state" lane: bounded-parallel plus randomised order (pytest-randomly). Order is deterministic everywhere else — -p no:randomly lives in [tool.pytest.ini_options] addopts, so every pytest invocation (CI's inline pytest, the publish gate, ad-hoc runs) is reproducible by default; only the isolation lane re-enables shuffling with -p randomly (a command-line -p overrides the addopts opt-out). Each isolation run prints its seed (--randomly-seed=<n> reproduces).
The suite uses no GPU. GPU saturation during a test run comes from concurrent ollama/MCP sessions, not pytest — bound those, not the test workers.
- Never commit or push directly to master. Always create a feature branch.
- Branch naming:
id-021-store-child,fix-streaming-io,af-008-credential-masking, etc. - Push the feature branch; the user will create PRs or ask you to.
Decision questions go through the AskUserQuestion tool, never prose. A prose
question ends the turn and sits in scrollback; a tool question blocks until
answered.
Wiring, maintenance, and failure modes: sdd/CLAUDE-REFERENCE.md § Interview
mode.
Use em dashes (—) sparingly in prose responses. Default to periods, colons,
or commas. Never use -- as an em dash substitute anywhere in written output.
In tables, — (em dash U+2014) is the standard N/A / none value. Never
-- or No. The dash draws less attention than "No", keeping the eye on what is
supported rather than what is not. Applies to all capability, feature, and
comparison tables.
Preserve -- only in: shell end-of-options separators (git log -- path),
spec-ID ranges (BATCH-020 -- BATCH-025), Mermaid edge syntax
(A -- text --> B), --8<-- snippet includes, and code/SQL comments inside
fenced blocks. Table separator rows (| --- |) are structural Markdown.
Three authority docs govern documentation. Apply in order:
sdd/AUTHORING.md: placement (where files belong).sdd/DOCUMENTATION.md: structure (what shape they take).sdd/CONTENT-RULES.md: longevity (writing that stays accurate).
See sdd/DESIGN.md for code style rules. See sdd/000-process.md § Rules for
spec/test traceability obligations. Run hatch run lint before committing.
See sdd/TESTING.md for testing quality rules (assertion depth, mock discipline,
spec tracing). Applies to all new or changed tests.
See sdd/DRIFT-RULES.md for the rules governing mechanisms
that detect artifacts disagreeing with each other. Applies to any new or changed
cross-artifact check or drift report; that file states its own scope.
PR workflows are codified as skills: /pr, /rvw-pr, /fix-pr. Use those instead of ad-hoc gh commands.
Use /rvw-pr for PR reviews, not the built-in /review CLI command.
/ship delivers a whole task as one merge-ready PR, planning and building before
reviewing to convergence rather than to a round count. Its rationale is spread
across five records, each covering a different part of the loop:
ADR-0033 (convergence over a
round count),
ADR-0034 (panel rounds,
unprimed exit gate),
ADR-0035 (method over model),
ADR-0036 (subject and method
over domain persona; the fixer role) and
ADR-0037 (the whole-file
exit gate; figures that name their derivation). Read all five, and take each
record's amendment targets from its own Amends field — the parentheses above
are topics, not amendment claims, and the chain is not linear. Which record
changes which clause is the thing a reader most needs and the thing most easily
got wrong from a summary; a record here is as likely to withdraw a clause you
landed on earlier as to add one.
/orchestrate shares the review half of that model — reviewers picked by lens
and method — while keeping its own capped rounds and persona-based authoring
fan-out (ADR-0020,
ADR-0036).
For lookup tables, detailed procedures, and repo layout see sdd/CLAUDE-REFERENCE.md.
Repo-specific skills live in .claude/skills/. When the user mentions "skill"
(e.g. "use the orchestrate skill", "run the /pr skill"), read the matching
SKILL.md in .claude/skills/ for context, then serve the user's request.
Ignore AGENTS.md; this file defines Claude Code behavior for this repo.
For document structure rules see CONTRIBUTING.md § Authoritative Document Format.