{
  "markdown": "<p align=\"center\">\n  <img src=\"assets/sinter_logo.png\" alt=\"sinter logo\" width=\"360\">\n</p>\n\n# sinter\n\nA local code knowledge graph for coding agents, shipped as one static binary.\nsinter builds a typed, directed graph of a repository's symbols with\ntree-sitter and keeps it fresh incrementally. Start in an unfamiliar repository\nwith `sinter map`: it returns a bounded structural inventory with module\nnode/file counts, dependency hubs measured by graph in-degree, documentation\nentry points, and graph-health limitations. It does not infer runtime entry\npoints or domain ownership. Then use the graph for focused work:\n\n- **Reverse blast radius** — what transitively depends on a symbol,\n  cross-file and cross-language (`sinter affected`). Traversal stops at\n  hubs and names them; test dependents are counted, not listed, unless\n  asked for.\n- **Paths** — how one symbol reaches another (`sinter path`).\n- **Diff impact** — which symbols and tests a changeset can affect\n  (`sinter impact`), plus `--expect <symbol>` for the unfinished-refactor\n  check: direct dependents of a symbol the diff did not touch.\n- **Bounded text search** — a regex search whose corpus is a graph traversal\n  (`sinter grep '<regex>' --within 'affected(<sym>)'`), replacing the\n  run-affected-then-grep-the-files pipeline.\n- **Polyglot, zero setup** — syntax-only graphs work without an LSP. When the\n  syntax provides the necessary type information, sinter resolves self\n  receivers, locally typed receivers, typed fields, and explicit static/class\n  calls. Complete receiver-call binding often requires a fresh SCIP index;\n  zero setup is not compiler-complete resolution.\n- **Calibrated lexical navigation** — `sinter ask` ranks content-bearing\n  starting points from names, docs, paths, signatures, string literals, and\n  limited call evidence. Its confidence line reflects the evidence class of\n  the top hit; `--explain` adds the ranking-margin bucket and that bucket's\n  holdout count with a 95% interval (the holdout is 46 cases, so the\n  interval is wide). The bucket is not a per-result probability; `ask` is a\n  navigator, not a semantic answer engine.\n- **Symbol orientation** — `sinter show` turns a selected symbol into a compact\n  card with its definition (attributes included) and a one-line used-by\n  tally; `--body` adds the source (whole when it fits in 60 lines, else up\n  to the byte budget), `--impls` the type's impl blocks, and\n  `show @file:line` names the enclosing symbol. A span over 8 KB or 200\n  lines is a black hole no body dump can show: the card outlines it\n  instead, listing the nested definitions, literal branches and command\n  literals inside it with their line numbers (`--outline` forces it).\n- **Task evidence packet** — `sinter context \"<task>\"` resolves identifiers in\n  the task against real node names (fuzzy when needed, shown as\n  `term ~> symbol`) and seeds from them, returning edit candidates, string\n  literals and hand-maintained mirrors that mention the task's terms, and\n  affected tests as runnable commands with the symbol that reached each.\n  It never abstains while it has hits. Add `--workspace <manifest>` to rank\n  candidates across declared member repositories in one packet.\n- **Snapshot-scoped assertions** — `sinter assert no-callers <symbol>` checks\n  depth-one call edges in an explicit corpus scope. Its decision is one of\n  `violated`, `holds_for_indexed_snapshot`, or `not_proven`; it never claims\n  runtime exhaustiveness. `sinter assert deletable <symbol>` tallies every\n  depth-one dependent across all scopes, grouped by scope.\n- **Citation maintenance** — `sinter cite <symbol>` emits a Markdown location\n  with a stable symbol key. `sinter verify-doc <file.md>` re-resolves managed\n  citations and fails on moved, missing, invalid, or identity-free references.\n- **[Cross-repo workspaces](docs/workspaces.md)** — federated graphs over many repos for\n  distributed systems: blast radius, paths, and PR impact across service\n  boundaries (`--workspace`).\n\nThe design rule underneath everything: **evidence or nothing.** An edge\nexists only when structural, scope, import, or compiler (SCIP) evidence\nbinds a reference to a definition (workspace manifests may add\noperator-declared cross-repo links, and Rust trait impls add labeled\n`dynamic` dispatch fan-out so blast radius survives `dyn Trait`).\nAmbiguity resolves to nothing — \"unresolved\" is a first-class, counted\noutcome, never a guess. Every edge carries its evidence kind, and every\nquery can filter on it. Be clear about what that buys without a compiler:\nsyntax-only evidence binds names it can prove by scope and imports and\nleaves the rest (receiver calls, re-exports, macros) unresolved, so the\nzero-setup graph is a precise subset, not the full graph. `sinter scip`\ncloses that gap where a compiler index is available.\n\n## Install\n\nPackage managers:\n\n```\ncargo install sinter-io        # or: cargo binstall sinter-io (prebuilt)\nuv tool install sinter-io      # or: pipx install sinter-io\nbrew install shellfu/tap/sinter\n```\n\nAll of them put a binary named `sinter` on PATH. Or the one-liner, no\ndependencies (Linux and macOS; verifies the release checksum, installs\nto `~/.local/bin`):\n\n```\ncurl -fsSL https://raw.githubusercontent.com/shellfu/sinter/main/scripts/install.sh | sh\n```\n\nWindows, in PowerShell (new and less battle-tested; verifies the release\nchecksum, installs to `%LOCALAPPDATA%\\sinter\\bin`):\n\n```\nirm https://raw.githubusercontent.com/shellfu/sinter/main/scripts/install.ps1 | iex\n```\n\nTo verify a manually downloaded release asset against its GitHub build\nprovenance attestation (requires the `gh` CLI):\n\n```\ngh attestation verify sinter-<target>.tar.gz --owner shellfu\n```\n\nPyPI wheels carry sigstore provenance via Trusted Publishing. Once\ninstalled, `sinter update` self-updates from GitHub releases.\n\nOr build from source (requires a Rust toolchain):\n\n```\ncargo build --release\n```\n\n## Quickstart\n\nAgents that only need a usable local graph should create derived state without\ninstalling hooks or editing client configuration:\n\n```\nsinter ensure /path/to/repo\n```\n\nThis command only builds or refreshes `.sinter/`. It is safe to run within a\nread-oriented coding flow.\n\nOnboard a repository — builds the graph, installs git hooks, registers\nagent integration (AGENTS.md block, MCP), and finishes with a doctor\nreport:\n\n```\n./target/release/sinter init /path/to/repo\n```\n\nInit prints everything it is about to write, grouped by scope, and asks\nonce before writing any of it (`-y` skips the prompt; a non-interactive\nrun prints the plan and proceeds). Every write lands inside the repo —\nnothing under `~/.claude` is touched unless `--global` is passed, which\nadds the machine-wide skill card and enforcement hooks. Both `init` and\n`ensure` append `.sinter/` to the root `.gitignore` when the repository is\na git worktree and no existing line covers it.\n\nRepo-local Claude hooks use bounded strict enforcement: the first broad\nrecursive search in a session is redirected to Sinter, while a retry is\nallowed with a fallback-search reminder. Machine-wide hooks installed by\n`--global` remain advisory.\n\nOn a terminal, init asks before running compiler indexers (`sinter scip`)\nbecause those toolchains execute repository build scripts; pass `--scip`\nor `--no-scip` to answer up front (non-interactive init skips them).\n\nAfter that, every query self-syncs at the query boundary — when nothing\nchanged, the sync is a stat-only walk (no file reads, no write\ntransactions) — and the git hooks refresh eagerly on commit;\n`sinter build` stays available for CI and scripting. Commands also work\nfrom any subdirectory, discovering the graph root the way git does. The\nbuild report distinguishes the heuristic's anchored miss rate from\ncompiler-relative accuracy. The anchored rate is useful without SCIP but\nis not recall: the heuristic can classify a compiler-resolvable reference\nas external.\n\n```\nresolution (this pass): ... resolved (scip 0, import 118, scope 189), ... unresolved (105 internal, 3193 external)\nanchored miss rate (this pass): 25.5% (heuristic classification, not compiler-relative recall)\n```\n\nWhen a SCIP index is present, the report also prints the compiler\ncross-check (what share of internally-bound refs agree with the\ncompiler) and internal recall vs the compiler (how many compiler-bound\nrefs sinter found without SCIP). Files edited since the index was built\nget no SCIP evidence at all — their references fall back to import and\nscope resolution rather than being rebound by byte position — so a stale\nindex never attributes a call to a name that no longer sits on that line.\nFiles the grammar could only parse partially are counted in one build\nline (`N parsed partially (M symbols in them; …)`); `sinter doctor\n--verbose` lists them.\n\nOrient before searching so the next query uses the repository's own module and\nsymbol vocabulary:\n\n```\nsinter map /path/to/repo\n```\n\nThe map is the first-pass inventory. Use `ask` next when the target is still a\nconcept rather than a known symbol.\n\nAsk a question against the graph (output shown for this repository):\n\n```\n$ sinter ask \"where is the trigram search\"\n\n1. function Store::search    [doc+name+path+sig 2/2 terms]\n   crates/sinter-store/src/search.rs:148\n   /// Fuzzy candidates: nodes sharing the most trigrams with the query,\n   pub fn search(&self, query: &str, limit: usize) -> Result<Vec<Node>, StoreError>\n```\n\n`ask` is a calibrated lexical navigator, so treat its hits as places to inspect\nrather than generated answers. Every hit shows its match provenance and the\ntext card carries one confidence line (`confidence: high — verify top hit`)\nrated from the top hit's evidence; a weak top hit is `unrated`. String\nliterals that match the question are listed after the symbol hits, and\nfixture, example, and test-local definitions rank below production ones\nwith the same evidence.\nAgent JSON groups results by topic under one strict result budget and keeps\neach hit lean: rank, name, kind, file, line, signature, doc, matched terms,\nchannels, and a one-word confidence. `--explain` (text or JSON) adds the\nranking margin, query-term coverage, the named holdout calibration, and\nper-hit scores. Query terms expand through a small synonym table (cap, limit,\nbudget; size, bytes; caller, dependent) at reduced weight, so a literal match\nalways outranks a synonym. Weak singletons, low term coverage, and undersampled\nranking-margin buckets abstain. CLI JSON and MCP `structuredContent.data` use\nthe same payload.\n\nEvery `affected`, `deps`, and `path` response is deliberately bounded.\n`deps` defaults to depth 1 (`--max-depth` widens). `affected` stops at hubs\n(fan-in over 100, or a seed with more than 50 direct callers) and says so\n(`stopped at hub Store (fan-in 114); --through-hubs to continue`). Both\ncount test-scope rows instead of listing them (`--include-tests`), and\n`--relations calls,uses` drops file-level import rows. `path -k N` returns\nup to N node-disjoint routes; a miss prints the closest frontier, the edges\nthe filter excluded, `reason: filter_excluded` when a filter caused it, and\na retry to run. In `--json`, positive and negative answers carry a\n`coverage` summary (completeness, conclusive, snapshot, compiler-index\nstate); `--coverage` restores the repository-wide block with evidence\nsources, certain and possible result counts, unresolved-reference counts,\nand explicit gaps. The `universe` field names the repository root or every\ndeclared workspace member searched. `completeness` describes only the\nindexed snapshot and `conclusive` remains false, so agents do not turn a\nnon-empty syntax-only result into an exhaustive claim.\n\nAgent-facing node `id` values are stable symbol keys and survive unrelated\noffset shifts. `snapshot_id` retains the byte-exact locator for the reported\nsnapshot. Handle-consuming operations accept `if_snapshot` and return typed\nstale-snapshot, relocated-handle, or ambiguous-candidate outcomes instead of\nsilently rebinding. A symbol whose bare name is ambiguous can be addressed as\n`Name@file` or `Name@file:line` (`run@doctor.rs:175`).\n\n## Test Sinter on a real repository\n\nSinter is recruiting ten design partners who use coding agents on repositories\nwhere dependency or blast-radius investigations are expensive. Bring one real\ntask. The evaluation will compare the investigation with and without Sinter and\nrecord where the graph helped or failed.\n\n[Open a design-partner issue](https://github.com/shellfu/sinter/issues/new?template=design-partner.yml)\nto propose a public repository or describe a private repository without naming\nit. Do not include private source code, credentials, or customer information in\nthe issue.\n\n## Commands\n\n| Command | Purpose |\n|---|---|\n| `sinter ensure [repo]` | Build or refresh only the derived graph; does not install hooks or edit agent/client configuration |\n| `sinter map [repo]` | First action in an unfamiliar repo: structural module inventory, explicitly measured dependency hubs, doc entry points, and one graph-health line (partial-syntax files, user gaps, compiler-index state) (`--json`) |\n| `sinter init [repo]` | Onboard a repo: build + hooks + agent integration + doctor. Shows its plan and confirms first (`-y` skips). Repo-scoped by default; `-g` also installs the skill card and enforcement hooks machine-wide (`--scip`/`--no-scip` answer the indexer consent up front) |\n| `sinter uninit [repo]` | Offboard completely: remove the graph and every sinter-managed artifact (`-g` also removes global skill + hooks) |\n| `sinter build [repo]` | Build or incrementally refresh the graph |\n| `sinter watch [repo]` | Keep the graph fresh from filesystem events |\n| `sinter hooks install` | Git hooks that refresh after commit/checkout/merge |\n| `sinter ask \"<question>\"` | Calibrated lexical starting points for a vague question, with verify/abstain guidance |\n| `sinter show <symbol>` | One-screen orientation card for a symbol or file: signature with attributes, a one-line `used by: N files, M edges` tally (`--callers` lists the files), `impls (N)` for types (`--impls` prints their bodies). `--body` prints the whole source when it is 60 lines or fewer, else as much as fits `--budget-bytes`; `--context-lines 0` forces the whole span. A span over 8 KB or 200 lines prints `outline (N)` instead — its nested definitions, literal-discriminating branches and command/flag literals, by line (`--outline` forces the outline on any symbol; `--body` the source). `show @file:line` names the symbol enclosing that line, and `Name@file:line --body` excerpts around the line with a `>` marker. A tie-broken name leads with `resolved: Name@file` and lists `also_see` same-stem symbols |\n| `sinter query <symbol>` | Exact + fuzzy symbol search, production copies first; exits 1 when only fuzzy neighbors match; Markdown section bodies are capped at 200 characters |\n| `sinter affected <symbol>...` | Reverse blast radius, evidence-filterable; multiple seeds are unioned and deduplicated, each row naming the seeds that reached it. Stops at hubs (`--through-hubs` continues) and counts test rows (`--include-tests` lists them) |\n| `sinter deps <symbol>` | Forward blast radius: what a symbol depends on, direct only by default (`--max-depth N` widens) |\n| `sinter unresolved` | List unresolved references — the graph's honest gaps. Rows are user gaps (a name the corpus should define, a dangling `crate::x::gone` path); external, resolver-gap, and unsupported-syntax refs are counted and hidden (`--all` lists them). 50 rows per page, `--cursor N` for the next; `--file`, `--name` filter |\n| `sinter path <from> <to>` | Shortest dependency path with per-step evidence; `-k N` returns up to N node-disjoint routes. An unproven answer reports the closest frontier, excluded edges, a `reason` (`filter_excluded`), and suggested retries |\n| `sinter grep <regex> [--within <traversal>]` | Regex over the indexed corpus. Unbounded by default (every file in `--scope`; `--no-tests` drops test files); `--within 'affected(SYM)'`, `deps(SYM)`, `file(PATH)`, or `file(DIR)` (every indexed file under it) bounds it, repeatable and unioned, the seed's own file always in the bound. A bound that matches nothing is a warning, not a silent empty search |\n| `sinter context \"<task>\"` | Evidence packet for a coding task: edit candidates, deps/dependents, matching literals and mirrors, relevant tests as runnable commands, gaps, next sinter commands (`--workspace <manifest>` federates member packets) |\n| `sinter assert no-callers <symbol>` | Check for production callers by default; exits 0 only for `holds_for_indexed_snapshot`, with `--scope`, `--workspace`, `--certain`, and `--json` controls. Refuses to pick silently among same-stem symbols in other files (`also_see`); an unknown name exits 2. JSON is compact (`ignored_out_of_scope` inline); `--verbose` keeps the repository-wide `coverage.graph` block |\n| `sinter assert no-dependents <symbol>` | Same contract over every non-containment relation (uses, reads, writes, implements, …) for constants, types, and traits; `no-callers` counts `calls` edges only |\n| `sinter assert deletable <symbol>` | Every depth-one dependent across all scopes, grouped by scope; `has_dependents` exits 1, `none_observed` exits 0 |\n| `sinter cite <symbol>` | Emit a repository-root-relative Markdown `file#Lline` citation carrying a stable symbol key |\n| `sinter verify-doc <file.md>` | Re-resolve managed citations; bare `path:line` references return `not_proven` even when the location exists |\n| `sinter impact <rev-range>` | Changed symbols → blast radius → affected tests. Validation commands come first; tests are ordered by distance from the changed symbols and printed as runnable commands per language (`cargo test`, `go test -run`, `pytest`, `npx vitest run`); production files precede test harness files in the radius. `--expect <symbol>` reports direct dependents the diff did not touch; `--full` restores the whole radius beside it |\n| `sinter serve` | MCP server over stdio (`--repo` for one repo, `--workspace <manifest>` for a cross-repo scope) |\n| `sinter overlap <range>...` | Map open PRs onto the graph; rank pairwise merge risk (direct/radius/file; `--relations` picks what the radius tier follows). Ranges where one contains the other's endpoint are reported as `sequential`, not scored |\n| [`sinter workspace <manifest>`](docs/workspaces.md) | Build all members of a cross-repo workspace + refresh boundary links |\n| [`sinter init --workspace`](docs/workspaces.md) | Write a starter workspace manifest (never overwrites) |\n| `sinter install [targets]` | Write agent cards (claude, cursor, agents/AGENTS.md, enforce (`--strict` available), all); `--mcp` registers the server for Claude Code, Cursor, and Codex |\n| `sinter scip [repo]` | Run every matching compiler indexer, merge into `.sinter/index.scip`, rebuild; no-op when fresh (`--force` reindexes); `scip check` is the CI freshness guard. Indexer output lands in `.sinter/scip-<lang>-<n>.log`, one summary line per indexer on the terminal |\n| `sinter doctor [repo]` | Diagnose installation + graph: one `integration: all N checks ok` rollup (or the failing checks), `sinter serve` processes for this repo running a different version (Linux), partial-syntax file count (`--verbose` lists them), the SQL grammar gap, schema lints; every finding names its fix; `--fix` applies the safe ones and shows rebuild progress |\n| `sinter update` | Self-update to the latest release, checksum-verified (`--dry-run` reports only) |\n| `sinter completion <shell>` | Shell completions |\n| `sinter version` | Version, graph schema, language packs |\n\nMCP registrations use the portable `sinter` command and start non-required so\na missing binary cannot prevent the client from starting. `sinter doctor`\nchecks that the command resolves to an executable on `PATH` and performs an MCP\nhandshake.\n\nReads open the graph with a shared lock; a rebuild holds it exclusively. A\nquery that arrives during a rebuild queues for up to two minutes and prints\none `waiting for another sinter process` notice after a second, so parallel\nagents see a delay rather than a `Database already open` error. Leaked MCP\nservers from finished sessions keep their original binary; when versions\ndiffer each one rewrites the graph in its own format, so `sinter doctor` lists\nthe ones serving this repository with their pids.\n\n`affected`, `deps`, and `path` accept `--evidence scip,import,scope,dynamic`\nand `--certain` to restrict traversal to stronger evidence tiers, and\n`--relations calls,uses,imports,implements,extends,reads,writes,creates,alters,drops`\nto restrict which edge\nrelations are followed (e.g. drop file-level import edges from a blast\nradius); `sinter grep` accepts the same traversal filters for its `--within`\nbound; their MCP counterparts take the same filters as `evidence` (array),\n`min_confidence: \"certain\"`, and `relations` (array) parameters.\n\nMCP tools share the CLI defaults: `scope` is the CLI corpus\n(`production,test,docs`; `ask` uses `production,docs`), `deps` is depth 1,\n`affected` stops at hubs. Arguments are validated against the advertised\nschema (`tools/list` carries enums and one-line descriptions), so\n`max_depth: \"two\"` or `relations: \"calls\"` is an `invalid_arguments` result,\nnot a silently ignored filter. Every user-fixable failure — unknown symbol,\nambiguous name, bad argument — is an `isError` tool result whose\n`structuredContent.error` carries `code`, `message`, and `Name@file`\ncandidates; `outcome.status` (`complete`, `partial`, `not_proven`,\n`not_found`, `error`) and `outcome.reason` (`limit_reached`,\n`filter_excluded`, …) are the one place to branch. Paging covers the whole\nresult: `limit: 0` is unlimited, `next_cursor` is set whenever rows remain,\n`cursor` resumes. `symbol` echoes are trimmed to key, name, file, line; the\n`coverage` block is omitted unless `include_coverage: true`. `show`, `deps`,\nand `affected` take `symbols: [...]` and `path` takes `pairs: [[from, to]]`\nfor one result per entry. `budget_bytes` below the smallest answer returns\nthat answer flagged, not an error; `next_actions` are tool calls.\n\n`ask` defaults to the `production,docs` corpus; every other verb to\n`production,test,docs`. Use `--scope` or the MCP `scope` array to include\nfixtures, examples, generated files, or vendor code. Exact `show` remains\nunfiltered. Repositories can exclude paths in `.sinterignore`; classify\nfixture corpora and apply ordered overrides in `.sinter.toml` (committed) or\n`.sinter/config.toml` (local, wins) with gitignore-syntax patterns:\n\n```toml\n[scope]\nfixture = [\"worked/**\", \"tools/*/expected/**\"]\n\n[[scope.override]]\npattern = \"tools/golden-production/**\"\nscope = \"production\"\n```\n\nWithout configuration, any path segment named `fixture(s)`, `golden`,\n`testdata`, `expected`, `worked`, or `snapshot(s)` classifies as a fixture\nand `example(s)`, `sample(s)`, or `demo(s)` as an example.\n\n## Languages\n\nRust, Go, Python, TypeScript, JavaScript (ESM, CJS, JSX), Java, C#, C,\nC++ (including Unreal Engine macro conventions), SQL (DDL/DML), Bash,\nProtobuf, and Markdown (headings become nested section nodes with the\nopening paragraph as doc, so `sinter ask` finds prose docs with file:line\nprovenance; `[text](target)` links become `uses` edges from the linking\nsection to the target file — or, with `#fragment`, to the section whose\nheading slugifies to it — when and only when the target resolves to a\nfile in the corpus: dead links stay unresolved, external URLs produce\nnothing). A language is pure data — a tree-sitter grammar, one `.scm`\ncapture query (plus an optional secondary inline grammar, spec-declared),\nand a spec row — consumed by a single engine that never\nbranches on language. Adding a language requires no engine code; if it\never does, the capture contract is wrong, not the language.\n\nSyntax-only binding covers, per language: Python `import x as y` aliases,\nfunction-local `from m import f` calls, and calls inside nested `def`s;\nTypeScript `new X()` constructor calls, default exports, and `export *`\nbarrel re-exports; Go interface implementations matched by method set\nacross packages (an `affected` on an interface method that could not\ntraverse implementations says `gap: implementations not traversed`).\nExplicit implementations — Rust `impl Trait for T`, Java, C# and\nTypeScript `implements`, class inheritance — carry a method-level\n`implements` edge too, so `sinter affected Trait::method` reaches the\nimplementing methods and not only the trait method's callers.\n\nAn edge keeps every call site it saw, up to eight, plus the true total: a\ncaller that calls a symbol twice renders as `path/to/file.rs:12, :48`, and\n`--json` adds `sites` and `sites_total` when there is more than one. Graph\nschema v16 carries these facts; an existing graph rebuilds once on the\nfirst query after upgrading.\n\n### SQL graph\n\nFor `.sql` files, Sinter emits `table`, `view`, `column`, and `index` nodes.\nIt also records the direction and purpose of object references:\n\n- `reads`: a `SELECT`, `FROM`, or `JOIN` source\n- `writes`: an `INSERT`, `UPDATE`, or `DELETE` target\n- `creates`, `alters`, `drops`: schema changes owned by the SQL file\n- `uses`: foreign-key and index dependencies\n\nA view owns the reads in its defining query. Top-level statements belong to\ntheir file, including migration files.\n\nSQL embedded in Rust is extracted too, at known query sinks and for literal\nstrings only: the `sqlx::query!`/`query_as!`/`query_scalar!` macros, the\n`sqlx::query*` and diesel `sql_query` functions, and the `query*`, `execute*`,\n`batch_execute`, and `prepare*` methods of tokio-postgres, rusqlite, and\nconnection pools. The enclosing Rust function gains the `reads`/`writes`\nedges, so a table's blast radius crosses the language boundary. Table names\nresolve within a database root first (a directory holding migrations and\nqueries), then fall back to the unique corpus-wide definition.\n\nTo find every query, migration, and Rust function that touches a table:\n\n```sh\nsinter affected users --relations reads,writes,creates,alters,drops\n```\n\nTo prove nothing in the indexed snapshot writes, alters, or drops a table:\n\n```sh\nsinter assert no-writers users --json\n```\n\nTo inspect the data and schema dependencies of one migration:\n\n```sh\nsinter deps migrations/20260901_users.sql \\\n  --relations reads,writes,uses,creates,alters,drops\n```\n\n`sinter doctor` folds migrations in filename order and warns when a table is\ndropped at head but still referenced, or referenced but never created.\n\nLimits. The SQL grammar (tree-sitter-sequel) does not parse several\nPostgreSQL constructs — `CREATE FUNCTION`/`PROCEDURE`/`TRIGGER`/`POLICY`,\n`DO` blocks, row-level security. Files containing them are indexed from\npartial trees: the statements the grammar dropped are absent from the graph,\nand `doctor` reports the gap as one row naming the likely constructs. SQL\nstrings built at runtime, and SQL embedded in Go, Python, or TypeScript, are\nnot extracted. Sinter does not infer column-level lineage, transaction scope,\nindexes required by a query, or planner behavior; use `EXPLAIN` against the\ntarget database for planner evidence.\n\nTop-level `graphify-out/`, `memory/`, and `.memory/` are excluded from the\nsemantic corpus and SCIP freshness inventory. These are derived analysis\nproducts; indexing them would feed generated summaries back into `ask` and\ndependency answers.\n\nIf a compiler-produced SCIP index (`index.scip`) is present at the repo\nroot or at `.sinter/index.scip`, sinter ingests it as the highest\nevidence tier. `sinter scip` discovers configured project roots from build\nmarkers and runs only matching indexers that are available on `PATH`\n(rust-analyzer, scip-go, scip-typescript for TS and JS,\nscip-python, scip-clang for C/C++, scip-java, scip-dotnet), merges the\nresults into `.sinter/index.scip`, and rebuilds. Isolated source files and\nfixtures without a project marker do not trigger repository-level indexer\nrecommendations. Bash, proto, SQL, and Markdown have no SCIP indexers.\n\n## Teams\n\nGraphs are per-machine and rebuild in seconds; the SCIP index is the\nexpensive shared artifact. Build it once in CI (`sinter scip`),\ndistribute the file, and each teammate's next build ingests it\nautomatically. Recipe, cache-key guidance, and a copy-paste workflow:\n[`docs/team.md`](docs/team.md).\n\n## Accuracy and performance are measured, not asserted\n\n- **Golden corpus**: hand-verified fixtures (82 at time of writing) across all thirteen languages,\n  mined from real-world extraction idioms. Extraction and resolution both\n  gate CI at precision/recall 1.0 for the enumerated contract facts; any change that moves the metric fails\n  with the exact missing/extra tuples printed. Expectations derive from\n  language semantics, never from engine output (`harness/golden/`).\n- **Real-repository evaluation**: 249 hand-labeled, syntax-only navigation\n  tasks run against pinned releases of ripgrep (Rust), Cobra (Go), Flask\n  (Python), Hono (TypeScript), and Gson (Java): 3 exact lookups, 166\n  natural-language `ask` questions labeled by intent with a tuning/holdout\n  split, 36 direct-caller checks, and 44 path checks. The current scorecard\n  reports `ask` top-1 accuracy 0.705 (holdout 0.696), MRR 0.810, recall@5\n  0.945, recall@10 0.986, and p95 latency 55 ms. Callers score precision\n  1.000 and recall 0.523, paths 0.727: syntax-only graphs miss receiver-typed\n  and Java static-class calls that a compiler index binds, and the harness\n  labels those sites anyway. The weekly and manually dispatched workflow\n  uploads the full scorecard and syntax-only build timings (`harness/eval/`).\n- **Agent-flow evaluation**: eighteen deterministic, network-free coding flows\n  cover multi-step graph use, bounded agent responses, task context, and a\n  labeled design-similarity question that must surface both existing mapping\n  tables before inspection.\n  The current scorecard passes 9/9 flows and 21/21 steps with one correct\n  abstention and zero unsafe-confidence failures. These are observational\n  scenarios, not a claim of general end-to-end coding accuracy.\n- **Budgets** (measured on a ~2M-LOC Go repository, 271k nodes, before\n  the stat-gated scan landed): full build 18s, one-file edit under 1s\n  typical, cold point query under 100ms, `ask` 66ms end-to-end. A clean\n  sync is now a stat-only walk — no file reads, no write transactions —\n  measured 46→16ms on this repository and 55→10ms on an 80MB/400-file\n  synthetic corpus; the old 73ms 2M-LOC no-op figure predates the stat\n  gate. Cold open and incremental-edit budgets are CI-enforced\n  (`crates/sinter-store/tests/cold_start.rs`,\n  `crates/sinter-cli/tests/incremental_build.rs`); the full-build, warm-query,\n  and memory budgets are measured manually per `harness/perf/README.md`.\n\n## Workspace layout\n\n| Crate | Responsibility |\n|---|---|\n| `sinter-core` | Typed graph model; invariants enforced at construction |\n| `sinter-store` | Embedded redb store: adjacency, search indexes, incremental derivation |\n| `sinter-extract` | Language-agnostic tree-sitter extraction; languages as data |\n| `sinter-resolve` | Evidence-based reference resolution + SCIP ingest |\n| `sinter-cli` | The `sinter` binary: pipeline, verbs, MCP server |\n",
  "bytes": 31271,
  "sha": "85fe94be7cebfde2fc621ef141cb214a0d60f8ee7476f11b45f43b49f85c8f6d",
  "repo_slug": "shellfu/sinter",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_shellfu_sinter_06bc2b57/readme"
}