How it works
A short tour of what gsttt does between the command you type and the answer you read.
One binary, no model
gsttt is one binary. It has no daemon and no model: every command reads the files of your repository, brings an index up to date and answers from it.
The index is a graph. Files are its nodes, and its edges are the imports between files and the contracts that tie services together. An answer is a walk over that graph.
The index: files and contracts
Files and the imports between them. The TypeScript, Python and Rust connectors parse source files and resolve each import to a file in the tree.
Contracts. The interfaces through which code reaches code it does not import. The OpenAPI, GraphQL, protobuf, Prisma, SQL and env connectors read declarations, and code that serves or calls a contract is linked to it.
A contract has a kind and a key, written kind key, for example http GET /bookings/{}. The kinds are:
http- an HTTP route or call
graphql- a GraphQL operation
grpc- a gRPC method
trpc- a tRPC procedure
table- a database table
queue- a message queue
env- an environment variable
A file plays one of three roles for a contract: it declares it, serves it or calls it.
HTTP contracts match when the methods are equal or the method is *, and the paths have the same number of segments once the configured http_prefixes are removed, with {} standing for any one segment. With http_prefixes = { "/api" }, a call to GET /api/bookings/me meets the route GET /bookings/{}.
Refreshed before every answer
Before every answer gsttt brings the index up to date. The steps are:
- Find the state directory:
gsttt/inside the git directory, or a directory named by a hash under$XDG_CACHE_HOME/gsttt/when the tree is not a git repository. - List the files: tracked files that are present, plus untracked files that are not ignored. Submodules, symlinks, skip-worktree files and anything the config excludes are left out.
- Give every file a content id, its git blob id. A clean tracked file is never read, because git already knows its id.
- Run the connectors in a fixed order (ts, python, rust, openapi, graphql, proto, prisma, sql, env) and parse only the files whose content id changed. What a parse found is keyed by connector, options, path and content id, and the snapshots of sibling worktrees are reused.
- Resolve the imports and contracts, link them into the graph and store the result.
The snapshot
The graph is stored as snapshot.gsttt in the state directory. The file starts with a magic number and a version, then a JSON header that can be read on its own, then the graph.
It is written to a temporary file and renamed into place, so a reader never sees a half-written snapshot.
Determinism
Nothing in the pipeline asks a model. The configuration is Lua, run in a sandbox without io, os, package or debug, so it cannot make the graph depend on anything but the tree. The same tree with the same config always gives the same graph.
What coverage means
Every answer ends with a coverage line. It says how much of the code gsttt could read, so you know how far to trust the answer. This is an example:
python: 212 of 214 files read, 99% of imports resolvedUnder it, a freshness line says whether the index matches the tree: fresh at a1b2c3d, 3 files dirty, or stale: 2 files changed since the index.
Coverage is the limit of an answer. Code that gsttt could not read is code where an edge may be missing, and the line tells you so instead of hiding it.
How impact is computed
Impact is a fixpoint: gsttt applies these rules until no file is added.
- A file that imports an affected file is affected.
- A file that declares a contract affects the files that serve and call it.
- A file that serves a contract affects the files that call it.
- A file that only calls a contract affects nobody through it.
Each file in the answer carries a distance, the number of steps from the file you named, and the first reason that reached it, such as imports src/a.ts or calls http GET /bookings/{}. Test files are listed in a group of their own.