Agents and memory
Avert ships two agents. Both call the same tools as the MCP server, through a plain tool-use loop on Groq’s OpenAI-compatible
API (GROQ_API_KEY, model from AVERT_MODEL). They never talk to each other: the only handoff is the graph.
Watcher: avert watch <repo>
Section titled “Watcher: avert watch <repo>”- Runs
sync_changes, which turns provider changes into findings. - Triages with
rank_findings,migration_batches,recall,explain_pathandblast_radius. - Records a
notedecision on the most urgent finding and logs aSession.
Without a key it prints a deterministic ranking instead.
Migrator: avert migrate <repo> --agent <name>
Section titled “Migrator: avert migrate <repo> --agent <name>”For each open finding, in ranked order:
- Claim it atomically. The claim is a single write,
WHERE f.status='open' SET f.status='claimed'. FalkorDB serialises writes per graph, so two migrators cannot both win; the loser is told who holds it.scripts/concurrency.tsruns several processes against one repo and asserts there are no double claims. - Recall. An earlier
wontfix,deferoraccept_riskdecision means the finding is skipped and markeddeclined. - Fix. If the change names a replacement, or a recipe covers it, it opens a pull request. Otherwise the model calls
sdk_paramsto read the new SDK’s parameters, chooses the replacement from the docs, saves it withsave_recipe, and opens the pull request. The migrator publishes the recipe toavert:sharedonly after that pull request opens. If no pull request is possible it opens an issue for the batch. The model’s tools are pinned to the claimed finding, its change and its batch, so text in a tool result cannot point it at another finding. - Record a
fixdecision, once the pull request or issue has opened. The migrator records it, not the model, so memory never says a finding was fixed when nothing was opened. If the run fails for any reason, the claim is released (claimed → open) for another agent.
Finding status moves open → claimed → done | declined. Pass --dry-run to claim and release without acting.
Pull requests are stable
Section titled “Pull requests are stable”Branches are named from the change event, never the clock: avert/<provider>/<verb>-<subject>, for example
avert/stripe/remove-payment-intents-create-payment-method. Names use a-z, 0-9 and -, are capped at 60 characters, and events
that slugify identically get a stable 6-character hash suffix. One change is one branch and one pull request covering every call
site. Re-running rebuilds Avert’s single commit on the base and updates the existing pull request instead of opening another.
If someone pushed a commit to the branch, Avert leaves the branch alone, and it never reopens a pull request that was closed without
merging.
Nothing is ever merged automatically.
What is remembered
Section titled “What is remembered”| Memory | Stored as | Used for |
|---|---|---|
| Episodic | Decision (fix, defer, wontfix, accept_risk, note) with agent, client and time, linked ON a finding |
recall; the migrator skips what was already ruled out |
| Conversations | Session per client connection, linked DISCUSSED to the findings it touched |
A new session sees what earlier ones, from any client, did |
| Procedural | Recipe (from → to, steps), saved per repo and optionally published to avert:shared |
A repository hit by the same change is offered the fix |
recall with no arguments returns recent decisions and sessions, which is how a second agent client picks up where the first
left off without being told.