TL;DR
Reef is the part of agentmako that remembers things. Indexed facts (file → symbols), diagnostic findings (this file has an eslint error), audit findings (this route is missing a tenant scope), and human acknowledgements (we reviewed this and it's fine) all live in the same local store. Reef tools let the agent ask "what do you already know about this file?" before doing the work over.
Why durable findings matter
Without a persistence layer, every agent session starts from zero. The same lint check runs again. The same auth audit runs again. The same finding is reported, debated, and dismissed — again. The agent has no concept that someone has already looked at this file last week and decided "yes, the missing tenant scope is intentional, the table is read-only by design."
Reef solves this with three primitives that survive across sessions:
- Facts — derived knowledge ("this function is exported", "this route is GET /api/x", "this table has RLS enabled").
- Findings — observations that may need attention ("this file has an unused export", "this route is missing an auth guard").
- Acks — explicit "we reviewed this, it's fine, here's why" markers attached to findings.
Facts vs. findings
The distinction matters because they decay differently.
Facts are descriptive. "This file declares
symbol verifySession." A fact is true until the
file changes. When the index refreshes, contradicted facts get
marked.
Findings are evaluative. "This route handler is missing a tenant scope check." A finding can be: open (still true), resolved (the code was fixed), suppressed (intentionally ignored, with a reason), or stale (the code changed, finding needs re-running).
Tools like file_findings and
project_findings read the finding store.
project_facts and file_facts read the
fact store. reef_inspect shows you both for a given
file plus the diagnostic-run history.
Anatomy of a Reef row
Both facts and findings carry enough metadata to answer: who claimed this, when, against what code, and how do I dedup it?
A fact
{
"factId": "fact_01H8K2…",
"subjectKind": "file",
"subjectId": "lib/auth/dal.ts",
"kind": "exports",
"value": { "name": "verifySession", "kind": "function" },
"sourceTool": "project_index_refresh",
"indexedAt": "2026-04-26T15:14:02Z",
"fileHash": "a1c2…",
"freshness": "fresh_indexed"
} A finding
{
"findingId": "find_01H8K2…",
"code": "auth/missing-tenant-scope",
"severity": "warn",
"subjectKind": "file",
"subjectId": "lib/auth/dal.ts",
"line": 142,
"column": 9,
"snippet": "return await db.from('manager_district')…",
"identity": {
"matchBasedId": "auth/missing-tenant-scope:lib/auth/dal.ts:142:resolveManagerScope",
"ruleId": "auth/missing-tenant-scope"
},
"sourceTool": "lint_files",
"createdAt": "2026-04-26T15:14:02Z",
"lastSeenAt": "2026-04-26T15:14:02Z",
"status": "open",
"ack": null
} The two fields agents lean on most:
identity.matchBasedId— a stable fingerprint built from the rule + file + a structural anchor (function name, AST node) rather than line numbers. Surviving across edits is the whole point: when you reformat a file, the finding re-attaches to the same logical site instead of disappearing and re-appearing as a "new" finding.status— one ofopen,resolved,suppressed,stale. Drives whether the finding shows up inproject_findingsorproject_open_loops.
Freshness, briefly
Every Reef row carries enough metadata to be labeled as live, fresh-indexed, stale, historical, contradicted, or unknown. The agent shouldn't return a stale answer as a confident one — it should know.
Full freshness model: How agentmako tracks freshness.
The ack ledger
Some findings are intentional. A "this file uses
any in three places" warning may be the right call
for an integration boundary. Acking the finding records:
fingerprint of the match, who acked it, why, and when.
Two ack flows exist:
finding_ack— single ack, used for one reviewed finding. Requires the fingerprint and a reason.finding_ack_batch— for sweeping reviews. Same payload shape, applied across many findings.
Ack data is never sent anywhere. It's a local ledger. Use
finding_acks_report before assuming a clean result
means no one suppressed anything — sometimes "all green" is
really "all green or already acked."
How agents use Reef tools
The most useful tools, in approximate order of how often an agent should reach for them:
reef_scout— turn a fuzzy request into ranked facts/findings/rules/diagnostic candidates. First call for many vague tasks.reef_inspect— explain one file or subject: facts, findings, recent diagnostics. First call before editing a sensitive file.file_findings— list active findings for a single file. Quick read before editing.project_findings— active findings across the project. Useful for "what's on fire?" queries.project_open_loops— unresolved findings, stale facts, and failed diagnostic runs in one view.verification_state— whether cached diagnostics still cover current working-tree facts. Tells you if "everything's fine" is current or stale.project_conventions— discovered auth guards, runtime boundaries, route patterns, schema usage conventions — without re-reading the codebase.evidence_confidence— label a piece of evidence as fresh, stale, contradicted, or unknown.
Read-only Reef tools batch well. Use tool_batch
when you want freshness, conventions, and open loops in one
round-trip.
Lifecycle: how a finding moves
A finding's status is not just a flag — it's the
result of a small state machine driven by re-runs of the source
tool, file edits, and human review.
┌──────────┐
create → │ open │ ─── ack with reason ─→ ┌────────────┐
└────┬─────┘ │ suppressed │
│ └────────────┘
(re-run, still matches) │
│ │
▼ (file changes,
┌──────────┐ ── re-run, no match ─→ ack auto-revoked)
│ stale │ │
└────┬─────┘ ▼
│ ┌──────────┐
(re-run, no match) │ open │
│ └──────────┘
▼
┌──────────┐
│ resolved │
└──────────┘ Transitions:
- open → suppressed: a human (or agent under
instructions) calls
finding_ackwith a reason. The ack is bound to the finding'sidentity.matchBasedId— not its line number — so it survives reformatting. - open → stale: the file changed in a way
that may have invalidated the finding, but no diagnostic has
re-run yet. The finding still appears in
project_open_loopsas "needs re-check," not as "still failing." - stale → resolved: a re-run produces no matching finding. Reef closes it out automatically.
- stale → open: a re-run produces the same
finding again. The original
findingIdis reused becausematchBasedIdmatches. - suppressed → open: the file changed
structurally enough that the structural anchor in
matchBasedIdno longer matches the ack target. The ack is auto-revoked. This prevents stale acks from hiding real bugs that snuck in during a refactor.
Where Reef lives on disk
Reef is a local SQLite database. There is no hosted Reef service. Two on-disk locations matter:
~/.mako-ai/— global Mako state (project registry, model-provider keychain references). Not project data.<project-root>/.mako-ai/— per-project Reef store. Indexed facts, findings, ack ledger, diagnostic run history, scoped instructions all live here.
You can git add .mako-ai/ if you want findings
and acks to travel with the repo for the team — or
.gitignore it if Reef is one-developer state. Both
are valid; the design assumes you can choose.
Reef contents are never sent anywhere. agentmako has no telemetry. The only outbound traffic is the optional model provider you configure for the harness, or the Postgres / Supabase connection you opt in to. See PRIVACY.md.
Reef in practice
Three real workflows that come up often:
Investigating a noisy finding
A diagnostic run produces 30 findings of the same code on the same file after a refactor. Are these new bugs or one existing pattern that the indexer rediscovered?
- Call
file_findingson the file. Look atcreatedAtvslastSeenAt— if all 30 were created in the same run, this is one pattern repeating, not 30 separate bugs. - Open one finding and read its
identity.matchBasedId. That tells you whether they're structurally the same site or distinct. - If the pattern is intentional (e.g., a generated SDK file
that legitimately uses
any), usefinding_ack_batchwith a clear reason. Future re-runs auto-suppress the same set.
Carrying findings into a new agent session
You ended a long Claude Code session yesterday with several unresolved findings. Today you start fresh.
- Have the agent call
project_open_loops. It returns yesterday's unresolved findings, stale facts, and any failed diagnostic runs. - For each finding worth investigating, call
reef_inspectto get the full evidence trail. - The agent now has the same picture you ended on, without you having to summarize it in a prompt.
Verifying that "all green" is current
Diagnostics passed. But did they pass today?
verification_statereports whether cached diagnostic runs still cover the current working-tree facts.- If it returns
stale, the green is from before recent edits — not a current guarantee. - Run
diagnostic_refreshto bring it current before relying on the result.
What Reef is not
- Not a vector store. Reef stores typed rows with stable identities — not embeddings of code chunks. It does deterministic lookups, not similarity search.
- Not a hosted service. The whole store
sits in a local SQLite file under
.mako-ai/. No sync, no upload, no SaaS account. - Not a replacement for tests. Reef tracks what diagnostics have said and what humans have acked. Your test suite is still authoritative for "does the code work?".
- Not a static analyzer. Reef is the persistence layer underneath the analyzers (TS, ESLint, Biome, Mako rules). Findings come from those tools; Reef keeps them durable.
Common pitfalls
- Acking to silence noise. Acks should
mean "reviewed and intentional." If you ack to make the
dashboard quieter, you're trading durable signal for empty
calm. Use
finding_acks_reportperiodically to audit what's been suppressed and why. - Trusting stable answers as fresh.
cross_searchhits Reef, which can be stale. After edits, preferlive_text_searchfor exact current text, or refresh withproject_index_refresh { mode: "if_stale" }. - Treating Reef as a TODO list. Findings describe what diagnostics observed, not work you've planned. Use your issue tracker for tasks; use Reef for evidence.
- Forcing a refresh on every turn.
mode: "force"re-parses the whole repo and is slow on large codebases. Reach for it only when indexed answers look wrong, not just old.