← Back
N4darae

N4darae/shrt

Snapshot the valid call chains of an API so an agent can look one up instead of tracing for it. gRPC today, OpenAPI next

View on GitHub ↗
Stars
279
Forks
21
Watchers
279
Open issues
0
Contributors
2
Language
Go
License
—
Default branch
main
Created Sep 3, 2026Updated Sep 28, 2026

Star growth

Today—
This week—
This month—

Star history will appear here once this repo has been tracked for a couple of days.

README

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. shrt doctor compares the installed docs and the .claude/ kit with the binary, the descriptor with a rebuild, .gitignore, the token cache, the auth: profiles and their env vars, the envelope conventions, the overlays and every safe spot; run it 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

command does
shrt init write .shrt/, build the descriptor, install the skill, subagent and .shrt/ci-gate.sh
shrt version version, commit, build time and the docs it carries
shrt doctor check this repo's .shrt/ installation; -strict fails on warnings
shrt gate verify every chain with a safe spot, run the rest, each with a fresh tag; one line per chain, failures grouped by suspect rpc
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
shrt contract plan <rpc>[@alias]... compose one ordered chain from the contracts, with its probes (-write); -all plans one per rpc
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
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
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
shrt chain slice <c> -step <id> the minimal sub-chain reproducing one step; -verify -run <id> proves it
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
shrt chain hollow read steps that passed with an empty response, from run records
shrt run <c> execute in order and record (-dry-run, -keep-going, -var k=v, -quiet)
shrt confirm <c> -note "..." propose a passing run as the safe spot; prints a short summary to show the user, the full report in .shrt/safespots/pending/
shrt confirm <c> -approve -by <email> write the safe spot after the user's yes; -reject, -pending
shrt confirm -all -note "..." propose every chain whose latest run passed and has no or a changed safe spot; -all -approve -by <email> after the user's yes to each
shrt confirm <new> -rename-from <old> -by <email> carry a safe spot across a pure rename
shrt verify <c> replay and diff against the safe spot; -run <id> re-diffs a record offline
shrt diff [<c>] <run-a> <run-b> compare two recorded runs; no safe spot needed, not a verdict

Exit codes

Any command exits 2 for an unknown command, 1 for a bad flag or a setup it cannot load, 0 for -h.

command 0 1 2 3
run passed; dry run valid; kept red as pinned failed, or refused before sending — error: no verdict, re-run
verify no drift, replay passed drift, replay failed, no safe spot, a FINDING — could not verify, re-run
confirm proposed, approved, rejected, listed, renamed refused — —
chain slice printed or written; -verify: reproduced refused; NOT REPRODUCED, intermittent — -run latest did not evaluate the step; DID NOT RUN, INCONCLUSIVE
diff runs do not differ they differ could not compare —
chain hollow nothing unexplained; -gate at baseline hollow reads; -gate off baseline no run records —
chain which matched nothing matched — —
doctor no FAIL a FAIL, or a warning under -strict — —
contract lint no error an error, or no overlay — —
contract plan printed or written nothing planned — —
contract quality -gate at the baseline off the baseline, or a contract error — —

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
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, or same fault as <chain> when an earlier line named it; a slice failing at its parent's first change 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.

Exit 0 is green; 1 is a failure, a FINDING or the ratchet; 3 is no verdict (re-run once the backend is up, count it neither red nor green). 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. Write 0 into .shrt/quality-baseline first; 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. A gate runs shrt chain lint -strict, which fails the six assertion-quality warnings in the command table; other warnings, such as unasserted-timestamp (one line per lint; -v lists each step), stay warnings and 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

  1. 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.
  2. Never reorder, skip or parallelise steps. Order is the contract.
  3. Never hand-write a body from memory. Field names and enum values come from the descriptor (shrt catalog describe).
  4. Never leave a step asserting only that it did not crash. error.code == OK is the floor, not the assertion (PLAYBOOK.md §4). chain lint -strict fails such a step when its contract declares response facts.

Where the authority is

question authority not
what fields does this rpc take shrt catalog describe <rpc> the contract, which may be stale
what does this rpc require the contract's required, read from backend source the proto
what keys may I write GRAMMAR.md another tool's keys
does this code really fire a run record under .shrt/runs/ the contract's when:
did that read find anything shrt chain hollow a green step
which roles may call this rpc the backend's authorisation rules an unchecked requires_role:
is this run correct the user's yes, via shrt confirm -approve a green run, or an agent
are these docs the rules enforced shrt doctor the fact that they are installed

Run records are gitignored and machine-local: a run id quoted in a document is not evidence a fresh clone can check.