shrt docs
What an agent needs to compose an RPC chain and a contract. .claude/skills/shrt/SKILL.md routes
here; shrt init installs these four files into .shrt/docs/:
| file | job |
|---|---|
README.md |
the commands, exit codes, the loop, the rules |
GRAMMAR.md |
every key shrt accepts, generated from the code; a key not in it does not exist |
PLAYBOOK.md |
procedures: compose, fill, assert, probe, author a contract, verify, slice |
PITFALLS.md |
symptom → cause → fix |
A package path such as runner/runner.go is a file in the shrt module
(github.com/N4darae/shrt). Every command prints its flags and exit codes with -h.
Quickstart: a regression suite for a service
This is the path that finds regressions. Chains written by hand miss most of what the planner
probes (boundaries, the last item of a list, other roles, missing tokens, read-backs after every
write, repeats), so write contracts and let plan compose the chains.
export API_USER=... API_PASSWORD=... # the login's credentials, and <ROLE>_USER/_PASSWORD shrt init # observes the envelope from one real login shrt contract init -all # one overlay per domain under .shrt/contracts/ # fill each overlay from the service's code: required, failures, effects, fields.<f>.value shrt contract lint && shrt contract status -gaps shrt contract plan -all -write # one chain per rpc; fix every fill:/gap: line, re-plan -force shrt run <chain> # every chain; a red one on a correct backend is # a real defect: shrt chain pin <chain> keeps it red in a slice, the rest green shrt confirm -all -note "..." # show the user the table; approve only on their yes shrt gate # later, against every new release
A contract states behaviour; effects: states what a write does to numbers (GRAMMAR.md §3).
summary and note are prose for people.
Before the first command
cd "$(git rev-parse --show-toplevel)" shrt init -agents=false -build=false # only if .shrt/docs/ is missing, as on a fresh clone shrt catalog build # the descriptor; without it every catalog command fails shrt doctor # is this installation sound? shrt catalog ls -filter <word>
.shrt/docs/, the descriptor, .shrt/runs/, .shrt/tokens.json and
.shrt/safespots/pending/ are gitignored build output, so a fresh clone has none of them, and a
run id quoted in a document is no evidence it can check. Run shrt doctor before trusting a green. Rebuild the descriptor after any proto change and the binary
after any change to shrt itself (go build -o shrt ./cmd/shrt): both go stale quietly.
shrt init guesses the auth: block from the descriptor and says so; check the login it picked
(GRAMMAR.md §4). A second role on the same login gets a profile when <ROLE>_USER and
<ROLE>_PASSWORD are exported at init and the repo's README names the role; export them and
re-run shrt init to add one later. Export the env vars
auth.body reads before a run (read their names from .shrt/config.yaml); without them
shrt run refuses before sending anything.
Only run (not -dry-run), verify (not -run <id>), gate and chain slice -verify send
traffic.
Commands
Any command exits 2 for an unknown command, 1 for a bad flag or a setup it cannot load, 0 for -h
and otherwise as below; 3 is no verdict, neither red nor green: re-run.
| command | does | exits other than 0 |
|---|---|---|
shrt init |
write .shrt/, build the descriptor, install the skill, subagent and .shrt/ci-gate.sh |
2 descriptor not built; 3 credentials not exported |
shrt version |
version, commit, build time and the docs it carries | |
shrt doctor |
check this repo's .shrt/ installation: prints each WARN and FAIL, -v every check |
1 a FAIL, or a warning under -strict |
shrt catalog build |
rebuild the descriptor after a proto change | |
shrt catalog ls [-filter x] |
list the rpcs | |
shrt catalog describe <rpc> |
request and response schema with proto comments | |
shrt contract init <domain> |
scaffold a contract overlay (-all for every domain); keeps what you wrote |
|
shrt contract show <rpc>... |
schema, paste-ready step, exportable paths and the curated contract | |
shrt contract lint |
validate overlays against the descriptor | 1 an error, or no overlay |
shrt contract plan <rpc>[@alias]... |
compose one ordered chain from the contracts, with its probes (-write); -all plans one per rpc |
1 nothing planned |
shrt contract status [-gaps] |
coverage per domain; -gaps lists what no chain exercises |
|
shrt contract quality [-domain d] |
score contracts for what is missing; -gate -baseline <file> ratchets it |
1 off the baseline, or a contract error |
shrt chain new -name <c> <rpc>... |
scaffold a chain from the descriptor and contracts | |
shrt chain lint [<c>] |
static checks; -strict also fails unfailable-assertion, asserts-nothing, inert-allow-fail, export-overwritten, interpolated-arithmetic, envelope-only |
1 a lint error |
shrt chain ls |
one line per chain: * safe spot, ? pending proposal, R kept red |
|
shrt chain which [-rpc r] [-code n] |
which chains exercise an rpc or assert a code, with a slice command | 1 nothing matched |
shrt chain slice <c> -step <id> |
the minimal sub-chain reproducing one step; -verify -run <id> proves it |
1 refused, NOT REPRODUCED, intermittent, STILL FAILS without; 3 -run latest did not evaluate the step, DID NOT RUN, INCONCLUSIVE |
shrt chain pin <c> |
pin a red chain: each defect kept red in a verified slice of its own, the chain rewritten without it until it runs green | 1 refused, or a slice did not reproduce |
shrt chain hollow |
read steps that passed with an empty response, from run records | 1 hollow reads, or -gate off baseline; 2 no run records |
shrt run <c> |
execute in order and record (-dry-run, -keep-going, -var k=v, -quiet); 0 when kept red as pinned |
1 failed, or refused before sending; 3 |
shrt confirm <c> -note "..." |
propose a passing run as the safe spot: a short summary to show the user, the full report in .shrt/safespots/pending/; -all proposes every chain whose latest run passed and whose safe spot is missing or differs |
1 refused |
shrt confirm <c> -approve -by <email> |
write the safe spot after the user's yes (-all for each pending one); -reject, -pending; <new> -rename-from <old> carries one across a pure rename |
1 refused |
shrt verify <c> |
replay and diff against the safe spot; -run <id> re-diffs a record offline |
1 drift, replay failed, no safe spot, a FINDING; 3 |
shrt diff [<c>] <run-a> <run-b> |
compare two recorded runs; no safe spot needed, not a verdict | 1 they differ; 2 could not compare |
shrt gate |
the CI gate, below | 1 a failure, a FINDING or the ratchet; 3 |
CI gate
shrt gate sends every chain in .shrt/chains once (by verify when it has a safe spot, by run
otherwise, with a fresh -var tag as short as run's own), retries an exit 3 once, and holds shrt chain hollow to
.shrt/hollow-baseline. One line per chain:
| line | means |
|---|---|
PASS |
ran green, no drift from its safe spot |
KEPT RED |
failed exactly as its kept_red pins |
FAIL pins held, new change: |
every pin held; a change outside them is a regression, not a reason to re-pin |
FAIL regression: / order changed: / different input: / chain change: |
what verify calls the first new change |
FINDING intermittent: / repeated: |
its only failures are calls of an rpc this gate found failing on some calls, and the steps they explain; one FINDING: line at the end counts them over every chain and says once what that means |
FAIL over FINDING: ... failure at <rpc>, below |
such a call failed and something else changed too; the FAIL line names that change |
FAIL not as pinned: |
a kept-red chain that failed otherwise or passed; the moved pin and its suspect are named |
NO VERDICT |
exit 3: backend down, restarting or refusing auth |
Each FAIL line ends with its suspect and also <suspect> for the first other one, or same fault as <chain> when an earlier line named it and every other suspect of this chain; a slice failing at its parent's
first change, and not a kept-red slice failing not as pinned, has no line of its own, the
parent's says (+N slice(s) fail the same: ...). Then one line per suspect rpc and changed path. -v adds the suspect's request, every changed path and the
knock-on counts. How a suspect is chosen: PLAYBOOK.md §8.
A token refused early once makes the gate hold a
fresh one (at most 30s) and re-send a read: refused twice is a FINDING that sessions end early
(-no-session-check skips it). shrt gate <chain>... gates a subset, without the ratchet. With no
overlay, or an rpc whose contract no chain calls, it ends with one coverage: line.
shrt init writes this wrapper to .shrt/ci-gate.sh (commit it; init -force refreshes it); it
skips the contract checks while .shrt/contracts holds no overlay. Init also writes 0 into a
missing .shrt/quality-baseline, never over one; a gate failing on it names the current score, to
write in as a reviewed edit. The first gate outside CI writes .shrt/hollow-baseline with today's
count and says so; commit it. With CI set, a missing baseline fails the gate.
set -euo pipefail cd "$(git rev-parse --show-toplevel)" [ -f .shrt/docs/GRAMMAR.md ] || shrt init -agents=false -build=false shrt catalog build shrt doctor -strict if compgen -G '.shrt/contracts/*.y*ml' > /dev/null; then shrt contract lint shrt contract quality -gate -baseline .shrt/quality-baseline fi shrt chain lint -strict exec shrt gate
Build the tag into other text (name: item-${vars.tag}): a field that is ${vars.tag} alone is
input, so the fresh tag makes every verify of it drift. A slowdown fails the gate only with
latency: {fail: true}, which shrt init writes into every new config.
The loop
shrt catalog ls -filter <word> what rpcs exist
→ shrt contract show <rpc> what the curated layer knows
→ shrt contract plan <rpc> -write let the contract compose the chain
→ edit .shrt/chains/<name>.yaml fill ONLY the test data
→ shrt chain lint <name> shape, refs, required fields
→ shrt run <name> -dry-run resolve everything, send nothing
→ shrt run <name> the receipt
→ shrt chain hollow did any read pass and find nothing?
→ shrt confirm <name> -note "..." propose it; show the user the summary, ask
→ (user says yes) shrt confirm <name> -approve -by <their email>
→ shrt verify <name> replay and diff, later
Left of edit is derivation, and the tool does it. Right of it is evidence. Yours is the middle:
the test data, and the assertions that say what correct means. The gate's shrt chain lint -strict
fails the six assertion-quality warnings in the command table; other warnings exit 0.
Authoring the contract itself is a different loop, fed by shrt contract quality
(PLAYBOOK.md §7).
The other loop: you are about to change code
shrt run <name> a receipt on today's binary
→ shrt verify <name> -run <run-id> does it still match what a person approved?
→ (change the code)
→ shrt run <name> ; shrt verify ... the same two lines
run asks whether your assertions hold; verify asks whether every response field still matches
the approved run, including what nobody asserted. With no safe spot yet, shrt diff <name>
compares the last two runs; it is a comparison, not a verdict (PLAYBOOK.md §9). shrt chain ls
shows which chains have a safe spot; expect few at first, since only a person creates one.
Four rules that are never negotiable
- Approve only on the user's yes. Propose with
shrt confirm <c> -note "...", the note saying what you inspected in the responses and why it is right. Present the proposal in the conversation (PLAYBOOK.md§8) and ask. Run-approve -by <user email>only after the user answers yes to that proposal; silence or your own judgement is not a yes. - Never reorder, skip or parallelise steps. Order is the contract.
- Never hand-write a body from memory. Field names and enum values come from the descriptor
(
shrt catalog describe). - Never leave a step asserting only that it did not crash.
error.code == OKis the floor, not the assertion (PLAYBOOK.md§4).chain lint -strictfails such a step when its contract declares response facts.