Working Notes

What shipped in Revelara, dated, with the detail that matters and the small stuff folded away.

rvl v1.1.0

Scanner rules without an API key

rvl scan now does something useful before you have a key.

The rules ship in two tiers. The commercial artifact is unchanged, byte for byte. Alongside it there is a second artifact holding the vocabulary lanes, the parts of the ruleset that describe what a construct in your code is rather than whether it is a problem: server and client kinds, emission shapes, configuration keys. That tier carries CDLA-Permissive-2.0 in its envelope and is served unauthenticated.

The CLI syncs the OSS tier into its own store, and when you do have a key the commercial tier layers over it rather than replacing it. rvl cache status reports both.

Judgment lanes, judgments, default bounds, blocking intent and sentinels are structurally absent from the OSS artifact rather than filtered out of it, but exist in the commercial set.

What changes if you already have a key

Nothing about what your scans report. The one behavior change is the exit contract: rvl scan now exits 1 only when neither tier can be loaded, where before it exited 1 when the commercial tier was unavailable. A scan that used to refuse to run in that state now runs against vocabulary rules and tells you which tier it used.

Retired knowledge stops outranking current knowledge

Incident pages, risk citations and the data-lake export now show current knowledge instead of facts the pipeline had already retired.

The pipeline has marked facts, patterns and procedures as superseded since re-extraction and CAST re-observation were introduced, but some read paths kept returning retired rows.

The filter is now applied on almost all read paths. Four deliberately still see retired rows, because of legitimate need, and each says so in the code now. A check runs on every commit so a new read path cannot quietly reintroduce the problem.

Similar findings stop being merged into each other

Two defects meant the knowledge graph was comparing CAST findings on the wrong text and then merging them without keeping what each one was evidence for.

Every signal embedded its rendered template rather than its payload. Ranking was never affected, because a constant prefix shifts every pair alike. What was affected was every absolute threshold calibrated against the squeezed range, including the one governing merges.

Twin-merge then discarded provenance. Only 0.5 to 2.6% of near-duplicate pairs share a source, so a merge at that threshold was collapsing findings drawn from different incidents and attributing the survivor to just one of them. Merges now union provenance, so a combined finding still points at every incident that produced it.

Separately, we fixed a bug where CAST extraction didn’t carry a risk control. Only 107 of 61,870 CAST facts carried a control code, because the model was correctly abstaining from selection.

Findings go to the service you declared

A scan can no longer file its findings into a service you did not name.

A scan carried two independent service-naming channels and nothing cross-checked them. When services declared in a flag and via config disagree, the submission created a catalog entry under the declared name and filed its risks into the other service’s register.

Both halves are fixed. The CLI derives finding attribution from the declared service, falling back to the config project when --service is absent. The server rejects an incoherent submission with a 400 that names the offending findings, lists every foreign service and carries the service it believes you meant, and the check runs before the catalog upsert so a rejected submission doesn’t submit flawed data.

Fixes
  • You can create risks past R-999. An org that reached its thousandth risk code could not create another one at all; codes now grow without a ceiling.
  • find_similar_incidents over MCP returns results instead of UPSTREAM_ERROR. The error was swallowed, so hybrid search had quietly been running two of its three legs.
  • The grouped Teams and Services posture view loads instead of hanging on a skeleton when a registry team has no mapped risks.
  • Reindexing the corpus re-runs knowledge extraction instead of flooding the dead-letter queue. The re-emit had been writing to the wrong input stream.
  • SendGrid status is read from Twilio’s Statuspage feed. status.sendgrid.com was deactivated around 2026-08-12 and had been failing crawls for several days.
  • Crawler bronze writes dedupe per page and stop deleting content when the metadata write fails. One page with 28 links wrote the same object 28 times in six seconds.
  • Uploaded documents go through the same chunker as the incident corpus, so search over your uploads behaves like search over everything else.
  • goindex stops reporting a defer f(&tok, &err) helper as a swallowed error. That is the idiom the named-result check already accepted, one indirection away.
  • A scan carrying the skill’s provenance string submits instead of failing with a 400, and the engine document no longer rides along as an unmapped extra finding set.
  • Trial enforcement counts documents against a column that exists, and owner-email lookup failures surface instead of being reported as a missing owner.
  • A polynomial backtracking case in the TypeScript indexer’s trailing-slash trim is rewritten to the anchored form. Behavior is unchanged.
Improvements
  • The Scan Coverage page distinguishes a repo that assigns ownership per component from one nobody has claimed, with a badge naming the owning teams and a link to their registers.
  • rvl feedback is rate limited to 5 a minute per user, with size caps on the body and attached diagnostics. Oversize returns 413, throttled returns 429 with Retry-After.
  • Continuous distillation runs in production, and no longer re-clusters facts it already fused or compares every pair to find neighbours.
  • The rvl documentation is reorganized around a scanning guide: install, the no-key tier, reading output, exit codes, hooks, suppression, and what does and does not leave your machine.
  • The production spec-signing key is pinned in the CLI alongside the development key, so caches signed by either keep verifying.
  • Both cache fetchers time out after 30 seconds. They had none, so a black-holed network hung instead of degrading.
Breaking
  • rvl scan exits 1 only when no rule tier can be loaded, rather than when the commercial tier specifically is unavailable. With the OSS tier reachable without a key, a scan that would previously have refused to run now runs against vocabulary rules. 0, 2 and 3 are unchanged.
  • A scan whose findings reference a service other than the one it declares is rejected with a 400 service_scope_mismatch, closing a path that filed risks into another real service’s register. Nothing changes if you omit --service, the common path. Upgrade to rvl 1.1.0 first, since it derives attribution from the declared service. If you do hit it, resubmit the same scan directory; the findings are unchanged and still on disk.
rvl v1.0.1

A deterministic scanner to go with the agentic one

rvl 1.0 adds a new local scanner mode. It does not replace the agentic scan. The two work together, and the agentic scan is still the default.

The new scanner provides a deterministic pass which reads your code the way a compiler does, through the real language toolchain. Where it can resolve a call site to a reliability risk, it resolves it, and does so the same way every time. The agentic scan then adjudicates what is genuinely hard to decide, working from a smaller surface because the deterministic pass has already settled everything it could. Against the agentic scan alone that is about 40% less scan time and about 65% fewer tokens spent on the agentic portion.

It also runs on its own. When you want a fast, cheap, repeatable answer, most obviously in a pre-commit or pre-push hook, it won’t catch everything the agentic scan will, but what it does catch, it catches in seconds, while the change is still in the working tree and a development agent can fix it on the spot.

The speed is nice, but it isn’t what matters. It’s that a deterministic gate gives the same answer every time. This matters most when an agent is working in a commit loop, fixing one finding and then hitting another. The agent can only commit when the gate says “clean”, and it can only do that if the gate gives the same answer every time it is asked.

Getting the new scanner

brew upgrade is the supported path. Then run rvl doctor. The scanner needs the toolchain for your stack installed, and doctor will tell you what is missing and how to fix it.

rvl doctor now probes what each retriever actually needs, the Go tool, libclang through cindex --engine-check, rust-analyzer, etc. If the toolchain for one of your languages is missing, you find out from doctor.

If you’d like to suppress any particular finding, use the rvl:allow pragma.

Try it out, and give us feedback with rvl feedback if you run into any problems.

Team ownership, and scope flags for evidence and controls

Scans now carry the team ownership declared in your .revelara.yaml file, with a --team flag override for command line use. Near spellings will get a did-you-mean prompt.

rvl evidence takes --team and --service scope flags, and rvl control show reports scope status per team. A control’s status can now be read for the team that actually owns it, rather than flattened into a single org-wide status.

On the platform side, ownership is now a first-class axis. There is a team registry, a scan coverage view, and evidence scoping that resolves worst-of across a team’s services, with inheritance and a queue for anything whose ownership is not yet known. The risk matrix takes a team axis alongside services.

Per-team risk trajectory tracks new versus resolved week over week, with a rollup ranked by direction rather than count. A team or service that is degrading slowly shows up next to a team or service that is bad but improving, which is a trendline that doesn’t get captured when you rank by open count.

Fixes
  • goindex exits non-zero when the Go toolchain is missing. Five silent exit-0 paths are closed, so the lane degrades honestly instead of reporting a successful scan of nothing.
  • pyindex and csindex join the same guard through an unconditional retrieval_stats record, closing three silent paths each.
  • A failed lane’s files are no longer written to the packet index as scanned-and-empty, which used to make the false green permanent.
  • In a polyglot stream, repo_config records now merge. An empty record from one language no longer erases another language’s construction facts under last-wins.
  • rvl doctor probes what each native retriever actually needs (the Go tool, libclang via cindex --engine-check, rust-analyzer) instead of claiming no runtime prerequisites.
  • A cached scan replay reproduces the original status, so a retry cannot clear a blocking gate.
  • Team bindings are re-ingested when a scan replays from cache, and the replay is marked on the wire.
  • The org switcher follows the selected theme. It had been permanently dark since the header tokenization in v1.9.0.
  • Risk category slugs are normalized at every write path, with a backfill.
Improvements
  • Evidence scoping resolves worst-of across a team’s services, with inheritance and a queue for unknown ownership.
  • The risk register gains a multi-select service filter with chips, and a category filter driven by what is actually present rather than a fixed list.
  • Control detail evidence rows are real accordion toggles.
  • Scan output routes practice controls to the /rvl:assess-* skills.
  • Feedback from rvl feedback lands in an operator triage loop with a notifier job.
  • The new rvl-cli README is reorganized into docs/.
Breaking
  • rvl scan exit codes are now a four-value contract: 0 clean, 1 the scan could not complete, 2 usage error, 3 blocking findings remain. v0 exited 1 on blocking findings. Git hooks are unaffected (any non-zero blocks), but CI that branches on the specific number should update.
  • For Homebrew users, nothing renames: the binary is still rvl and configuration is still RVL_*. Hooks installed by v0 keep working across the upgrade, and rvl hook doctor will offer to modernize them. Only the internal beta scanner is retired: the rvlscan binary name and RVLSCAN_* environment variables are gone without a transitional alias, so any script that invoked them must move to rvl and RVL_*.
  • The v0 CLI codebase is archived at rvl-cli-v0.