A gate keyed on the command nobody runs: v0.6.14

Gate 6 redacts secrets. It lived inside the env distiller, so it only ran when the registry decided a command was an env command. A bare env passes through verbatim and printenv is not recognised at all, so both delivered their values exactly as printed.

Measured over 5,733 recorded traces, and the measurement is the whole argument. 25 carry an env-shaped payload. 17 of those hold a credential that reached the model unredacted: DB_POSTGRESDB_PASSWORD eight times, plus an API key, an SSH_PRIVATE_KEY and an access token. Not one of the 25 came from a command named env. They arrive from cd … && …, kubectl exec, sed, printf and export.

A gate keyed on the command could only ever have covered the case nobody runs. The redaction sits on the payload now, at the same dispatch choke point as the JSON guard, and returns the input minus its secrets rather than continuing to a summariser that might drop the line instead.

Known and accepted cost: SENSITIVE_PATTERNS matches on substrings, so a public key and a public URL variable are redacted too, 2 of the 17 in the corpus. Over-redaction leaves the key name visible, the line in place and the value in the rewind archive. Under-redaction puts a password in the transcript. The asymmetry decides it.

The env distiller kept only the secret

It emitted per-prefix counts and no values, so a nine-variable kubectl exec … env came back as env: 9 vars | REDACTED: 1 sensitive over DB(8) APP(1) plus the password line. The DB host, port, database, user, schema, SSL flag and driver were gone, and the one value surfaced by name was the only one that must never be printed.

DB(8) answers “which DB vars are set”. The question anyone runs the command for is “set to what”. env output is an enumeration, the shape passthrough already protects for ls and ps, and the only reason to touch it is the secret in it. So it redacts rather than summarises.

Two guards in the hook had to go with it, and without them this would have been a security regression rather than a fix. Redacting makes the output longer than the input, because [REDACTED] is wider than the secret it replaces, so the guardrail restore handed the raw bytes back under “nothing worth a deletion” and put the password on screen. And a distillation that saves nothing is dropped entirely before it reaches the agent, which left the host’s own plaintext in place.

A command naming a parseable format was still being cut

az acr repository show-tags … --orderby time_desc --top 25 -o tsv delivered 10 of 25 rows. format::sniff protects the shapes it can recognise, and a single-column TSV has no delimiter to recognise, so nothing stopped the cloud distiller head-sampling it.

Head-plus-marker is the worst possible cut for a time-ordered listing: the head is what the caller already knew and the tail is the history they ran the command for. The reported case turned on rows 18 to 20.

An explicit -o or --output naming tsv, json, yaml, name, jsonpath, go-template or custom-columns now passes the payload through, because naming one of those is a statement that something downstream parses it. The value is the tell and not the flag, so -o wide and -o table render for a human and stay compressible. Over the corpus: 48 traces carry the flag, 3 were being visibly shortened, and the 1.4 KB that buys back was loss rather than saving.

Four more shapes that lost the answer

A vitest suite that never loaded was reported as four test failures. When a transform error stops the suite loading, vitest runs zero tests and says so. failed_tests parsed as 0 and the count fell through to the length of the list, which in that payload is the transform’s stack frames, so the delivered line read vitest: ✓ 0/0 | ✗ 4 for a run where nothing executed. The [TSCONFIG_ERROR] line, the only actionable one, was deleted. A failure counter is the number a reader trusts without checking.

A JSON line was not handed back whole. Two JSON API responses printed beside a single pod "vmq" deleted notice are not NDJSON, so sniff returned None, format-safety switched off for the whole payload, and 17 bytes of a 460 byte answer arrived. The guard sits ahead of distiller dispatch now. Both figures from the sizing are in the source comment, because the first one would have justified the wrong build.

An already-aggregated histogram was routed to the pod-table distiller. kubectl … logs … | awk … | sort | uniq -c is a histogram by the time OMNI sees it, but uniq was not a reshaping tail. Of 40 rows, 10 were delivered, and both traffic spikes the query existed to find were in the 30 that went.

A lossless regroup was reported as data loss. The grep distiller folds a repeated path: prefix into a header, so 11 matches come back as 15 lines holding all 11. The byte ratio lands in Soft, so the output carried [Partial signal] and 274 bytes omitted, both telling the reader a complete answer was incomplete. A distiller that emits more lines than it consumed restructured rather than cut.

Cursor’s post-hook was wired to a file-edit event

install_omni_hooks wired --post-hook to afterFileEdit. A file write carries no stdout, so the half of the integration that produces the savings was fed nothing for its entire life, while the pre-hook was correct, which is what made the wiring look plausible.

omni doctor reported it as installed: it grepped the whole file for --post-hook and printed two fixed labels, so the check passed whatever event the command sat under. It asserts the event per hook now and names the missing one.

The README stopped listing five names under one verb

It claimed OMNI “works with Claude Code, Cursor, Windsurf, Codex and Roo out of the box”, and for the product’s core function that is not true on Cursor. Cursor’s only output-rewriting field is documented “For MCP tools only”, and afterShellExecution defines no output fields at all.

Driven through the 0.6.13 release binary with both documented Cursor payload shapes: zero bytes returned, zero rows recorded. omni init --cursor still installs and still does real work. What cannot happen there is distillation of command output, and a reader was previously left to discover that by measuring.

brew install fajarhide/tap/omni && omni doctor