A config tier deleted rather than repaired: v0.7.0

The long-term support release. Three things define it: the ledger stops guessing whether a fold is worth making and measures it, the surfaces stop reporting numbers the project had already retired, and a config tier that could let a repository edit what a visitor’s agent sees is deleted rather than repaired.

The trust gate hashed a different file from the one it gated

guard::trust::is_trusted hashed <project>/omni_config.json, and the answer decided whether to load <project>/.omni/signals/*.toml. The hash covered a different file from the one it gated, so a signal added or edited after a project was trusted loaded with the trust record untouched.

A TOML filter carries strip_lines_matching, which is enough to hide a failing test or an error line from an agent. So a repository could quietly edit what its visitors were shown.

The obvious repair is to hash the directory it actually gates. The deletion is better and is what shipped. The tier is worth nothing measurable: disabling every embedded signal moves the filter column by 804 bytes over 6,656 commands, and a project tier is a subset of that. What it costs is a supply chain the tool does not need. 354 lines deleted against 43 added.

The integration test is inverted rather than removed: a signal placed inside a checkout, with omni_config.json marking it trusted, must not load.

OMNI reads no filter file from disk

This is the breaking change in the release. With the project tier gone, ~/.omni/signals/ goes too. Every signal is compiled into the binary, so the set that runs is the set the inline tests cover and no file on any machine changes what an agent is shown.

What the external tiers cost is not a rounding error: a second dispatch path, a cache fingerprint over files that may not exist, a load-time difference between a developer’s machine and CI, and a class of bug where a distiller is well tested and unreachable.

Two writers had to become honest with it. omni learn --apply and the omni_learn MCP tool both wrote a file and reported success. With nothing reading that file, reporting success would be the fabrication class this project exists to stop shipping, so both now say there is nowhere to apply to.

The ledger folds when it pays for its marker

MIN_LEDGER_RUN_LINES and MIN_LEDGER_RUN_BYTES asked whether a run was big and never whether it was worth replacing. A 3-line 150 byte run was refused, while nothing anywhere compared a run against the size of the marker that would replace it.

Both are gone, replaced by MIN_LEDGER_RUN_GAIN: fold only when the run saves 150 bytes after paying for its own marker, weighed against the marker rendered rather than estimated, which is possible because a handle is always 16 characters. The marker is shorter too, 87 bytes to 65, and the two compound.

The split that decided this is worth recording. Of the repetition the ledger declined, the 4-line bound held 266,005 bytes over 11,406 runs, which is 23 bytes per run against an 87 byte marker. That bound was protecting the output rather than costing anything.

Measured over 6,656 Claude Code traces from 2026-08-04 to 08-11: aggregate 13.9% to 15.4%, the ledger claiming 817,155 bytes over 849 folds against 720,518 over 678.

The 100 byte bar was left on the table deliberately. It is worth another 0.6 points and costs 240 more substitutions, which is the same choppiness trade the sweep behind the old constants declined.

omni retrieve, because a marker promised a tool half the hosts cannot call

Reported from a real session. A 50 line span of prose came back as [OMNI: 50 lines already shown, omni_retrieve("cd900c16a4a94eb2")], and omni_retrieve is an MCP tool. On a host where it is not surfaced, the agent was left with a hole in the file it had just read and no way to fill it.

Every marker names the CLI now, which exists everywhere. It accepts the handle bare, quoted, or still wrapped in the old omni_retrieve(...) call, because a model handing the marker back verbatim is the likeliest way it is called. An unknown handle says why by naming the 30 day working tier rather than reading as a broken archive.

omni dashboard, on loopback, over the numbers omni stats prints

The terminal report is a snapshot and the meters are trends, which is the one shape a table in a terminal cannot show. std::net::TcpListener, one thread, one page, no dependency added and no JavaScript. It binds 127.0.0.1 and answers GET only, because the database holds command output and none of it should leave the machine.

Every panel reads a Store method the CLI already reads, so the two surfaces cannot disagree. That constraint is the point rather than a shortcut: a dashboard computing its own numbers would be a second source of truth, and this release exists partly to fix one of those.

Six adapters become a table

889 lines across roo_code, opencode, copilot, antigravity, zed and vscode that differed in three fields: where the config lives, which JSON key holds the server map, and what to call the host. install, uninstall and doctor_check were the same code six times, so a seventh host meant a seventh copy and the cheap path was the wrong one.

802 lines deleted, 346 added. Adding an MCP host is a row in mcp_host::HOSTS. Zed keeps context_servers and VS Code keeps servers, asserted by a test along with the property that no two hosts resolve to the same file.

Five smaller things that were reporting a state they did not have

omni patterns reported 20 of 20 entries RESOLVED, five of which had fired 27 times. resolve_pattern marked every unresolved pattern of a tool family at once, so a single green cargo test declared five distinct failures fixed, and nothing took the label back when they recurred. Resolved means “has not happened since” now, which is the only reading an agent can act on.

Every documented flag on omni reset was rejected before the command ran. print_help advertised thirteen and main.rs declared Reset as a bare clap variant, so omni reset --all failed with “unexpected argument”.

project_hash had four implementations and one disagreed. One trimmed the trailing slash and three did not, so a project reached as /foo/bar/ on one path and /foo/bar on another had two addresses, and its knowledge, engrams and patterns split across both while omni_recall found half.

loop_memory.ttl_days was declared and never enforced. The column has said 30 days since it was added while no SELECT filtered on it and no DELETE used it, so goal memory was a permanent table wearing a retention label.

The database wipe left omni.db-wal and omni.db-shm behind. SQLite runs in WAL mode, so the database is three files. 4.2 MB of -wal survived a wipe that reported success, and a stale -wal beside a fresh database is the one way this can corrupt rather than merely mislead.

brew install fajarhide/tap/omni && omni init
omni dashboard