# shrt: contract-driven gRPC regression testing for coding agents

> shrt is a Go harness that treats an API's valid call chains as contracts you write and review, lets a planner compose regression chains from those contracts, and gates new releases against human-approved baselines. The design detail that matters most is its exit code 3, which means no verdict at all, and the rule that a green result is worthless unless shrt doctor says the installation is sound.

**N4darae/shrt** — A contract-driven gRPC regression harness for AI coding agents, with human-approved baselines. Snapshot the valid call chains of an API so an agent can look one up instead of tracing for it. gRPC today, OpenAPI next

- Repository: https://github.com/N4darae/shrt
- Stars: 2,460 · Forks: 169
- Language: Go
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/n4darae-shrt

## The idea is that chains are written, not traced

shrt is a contract-driven gRPC regression harness for AI coding agents, with human-approved baselines. The project description puts the motivation plainly: snapshot the valid call chains of an API so an agent can look one up instead of tracing for it. gRPC today, OpenAPI next.

That is a different starting point from most regression tooling. The usual approach is to write test cases by hand, and a hand written case covers the paths its author thought of. shrt's premise is that the coverage gap is systematic, and the README states it as such: chains written by hand miss most of what the planner probes, including boundaries, the last item of a list, other roles, missing tokens, read-backs after every write and repeats.

So the workflow inverts the usual one. You describe the behaviour of each operation as a contract, and a planner composes the chains from those contracts rather than a person writing call sequences. The contract states behaviour, an effects entry states what a write does to numbers, and summary and note are prose for people.

The human stays in the loop at the point where it matters. Confirming a run proposes it as the safe spot, shows the user a short summary, writes the full report into a pending directory, and only writes the baseline after an explicit approval with a -note and an approver identity. A later gate compares every new release against those approved baselines.

The project is written in Go, the module is github.com/N4darae/shrt, and it depends on the protobuf runtime and a YAML library and little else. The licence is not stated in the repository metadata, and the last push was on 2026-10-05 with no GitHub releases.

## Four documents land in your repository

The repository you are reading is the documentation the tool installs into your own project, and that is a deliberate design choice rather than a publishing quirk.

shrt init writes .shrt/docs/ with four files, and a skill file at .claude/skills/shrt/SKILL.md routes the agent to them. The four files each have one job. README.md holds the commands, the exit codes, the loop and the rules. GRAMMAR.md holds every key shrt accepts, generated from the code, with the rule stated bluntly: a key not in it does not exist. PLAYBOOK.md holds the procedures, compose, fill, assert, probe, author a contract, verify and slice. PITFALLS.md is a symptom to cause to fix table.

Generating the grammar from the code is the part that makes the documentation trustworthy. In a tool with a large command surface, a hand written list of flags is wrong within a release, and an agent that trusts it will produce a command that does not exist. Here the grammar is the code's own behaviour, so the failure mode is removed rather than documented.

The playbook and the pitfalls file are aimed at the agent as much as at the person. The procedures tell it how to compose and verify a chain, and the pitfalls file tells it what a symptom means, which is the knowledge a human expert would otherwise have to supply in every prompt.

## The loop, and the commands that make it up

The quickstart in the README is the whole workflow, and it is worth reading as a sequence of intents rather than as a command list.

```bash
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/
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 confirm -all -note "..."                  # show the user the table; approve only on their yes
shrt gate                                      # later, against every new release
```

You export the credentials, API_USER and API_PASSWORD, and the per role variants. You run shrt init, which observes the envelope from one real login. You run shrt contract init -all, which creates one overlay per domain under.shrt/contracts/, and you fill each overlay from the service's code, with the required fields, the failures, the effects and the field values. You lint the overlays and check the coverage with contract status -gaps. You plan the chains with contract plan -all -write, one chain per rpc, fixing every fill or gap line and re-planning with -force. You run the chains, and here the README draws a distinction that carries the whole design: a red chain on a correct backend is a real defect, and shrt chain pin keeps that defect red in a slice while the rest of the chain stays green. You confirm the passing runs with a note and a human approval, and later you run shrt gate against every new release.

The command table is long and each row carries its own failure exit code, which is unusual and useful. Beyond the lifecycle commands, there is shrt catalog describe for the request and response schema with the proto comments, shrt contract quality to score contracts for what is missing with a -gate and -baseline mode that ratchets the score, shrt chain which to find which chains exercise an rpc or assert a code, shrt chain slice to extract the minimal sub-chain reproducing one step, and shrt chain hollow to find steps that passed with an empty response. There is also shrt diff for comparing two recorded runs, which is explicitly not a verdict and needs no safe spot.

## Exit code 3 means the tool has no opinion

The exit code convention is the most reusable idea in the project.

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 listed. And then there is 3, which is no verdict, neither red nor green, and the instruction is to re-run.

The places where 3 appears are the places where the answer genuinely is not known. A chain slice can exit 3 when a run with -run latest did not evaluate the step, with the reasons spelled out as DID NOT RUN and INCONCLUSIVE. A run can exit 3. A verify can exit 3. A contract plan that planned nothing exits 1, but a verify that could not reach a conclusion exits 3.

That distinction is the difference between a test harness you can put in a pipeline and one you cannot. A harness that returns failure for everything it cannot decide trains people to ignore red, and it trains the agents reading the output the same way. A separate code for no verdict says that the honest answer is sometimes neither, and forces the caller to handle that case rather than to guess.

The related flag convention follows the same logic. Only run, and not -dry-run, only verify, and not -run on an id, only gate and chain slice -verify send traffic. Everything else is inspection.

## Everything important is gitignored, and that is the point

There is a paragraph in the README that deserves to be read twice, because it describes the most likely way to be fooled by this tool.

The .shrt/docs/ directory, 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. The instruction is to run shrt doctor before trusting a green.

Then the second half, which is the sentence to remember: rebuild the descriptor after any proto change and the binary after any change to shrt itself, with the command go build -o shrt ./cmd/shrt, because both go stale quietly.

Staleness here is not a slow failure, it is a wrong success. A descriptor built before a proto change will validate the old schema, a contract will pass against behaviour that no longer exists, and the gate will report green against a baseline nobody re-verified. shrt doctor is the check for that, and the -strict mode turns warnings into failures.

The repository also carries a test named librarymode_test.go and another named unknownkey_test.go, and both names map directly onto the two claims above: the library mode and the rule that a key not in the generated grammar does not exist.

## Credentials are a precondition, and traffic is opt-in

Two more behaviours protect the operator.

The first is about authentication. shrt init guesses the auth block from the descriptor and says so, and the README tells you to check the login it picked. A second role on the same login gets a profile when the role's user and password variables are exported at init and the repository's README names the role, so a second role can be added later by exporting them and re-running init. The environment variables that auth.body reads have to be exported before a run, and their names are read from .shrt/config.yaml, and without them shrt run refuses before sending anything.

That refusal is the important part. A tool that is going to drive a production service should fail closed when it does not know how to authenticate, and this one does.

The second behaviour is the traffic rule, and it is stated as a list: only run, and not -dry-run, only verify, and not -run on an id, only gate and only chain slice -verify send traffic. Every other command, including all the contract and catalog work, is local.

That gives a clean way to use the tool safely. Authoring contracts, linting them, planning chains and inspecting the catalogue are all offline, so the expensive and risky part, the traffic, happens in exactly four commands that you have to name deliberately.

## Conclusion

shrt fits a team that keeps breaking a gRPC service in ways its own tests do not catch, that wants an agent to help author the contracts rather than to have the agent guess at call chains, and that is willing to review the chains before they become the baseline. It does not fit a team with no gRPC service, since that is the only transport today, with OpenAPI listed as next, and it does not fit a project that wants a quick smoke test, because the contract authoring step is deliberately manual. Verify three things before you trust a green run: that shrt doctor passes, since the docs and the descriptor are gitignored build output and go stale quietly, that the descriptor was rebuilt after the last proto change, and that the safe spot you approved is the one being replayed, because shrt verify diffs against a baseline a human agreed to and that baseline is the whole point of the tool.

## FAQ

### What does shrt do differently from a hand written gRPC regression suite?

The chain planning is the difference. The README says chains written by hand miss most of what the planner probes, including boundaries, the last item of a list, other roles, missing tokens, read-backs after every write and repeats, so you write contracts describing behaviour and let plan compose one chain per rpc from them.

### What do the four files that shrt init installs do?

They go into .shrt/docs/. README.md holds the commands, exit codes, loop and rules. GRAMMAR.md holds every key shrt accepts, generated from the code, and a key not in it does not exist. PLAYBOOK.md holds the procedures, and PITFALLS.md maps a symptom to a cause to a fix. A skill file at .claude/skills/shrt/SKILL.md routes the agent to them.

### What does shrt exit code 3 mean?

It means no verdict, neither red nor green, and the instruction is to re-run. The commands that can return it, including run, verify and chain slice, use it for cases such as a run that did not evaluate the step, DID NOT RUN or INCONCLUSIVE. Exit 2 is an unknown command, 1 is a bad flag or a setup it cannot load, and 0 is for -h and for the listed cases.

### How does a run become the baseline that later releases are gated against?

shrt confirm proposes a passing run as the safe spot with a short summary for the user and the full report in .shrt/safespots/pending/. The baseline is only written after an explicit approval, with an approver identity, and -all proposes every chain whose latest run passed and whose safe spot is missing or differs. Later, shrt gate compares against those approved baselines.

### Which shrt commands actually send traffic to my service?

Only run, and not -dry-run, only verify, and not -run on an id, only gate, and only chain slice -verify. Everything else, including the contract, catalog, chain and doctor work, is local, and without the credentials that auth.body reads, shrt run refuses before sending anything.

## Sources

- [Issues](https://github.com/N4darae/shrt/issues)
- [N4darae/shrt on GitHub](https://github.com/N4darae/shrt)
- [README](https://github.com/N4darae/shrt/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/n4darae-shrt
