Skip to content

Choose the scan-budget mechanism from the server's own descriptors - #191

Merged
DavidBakerEffendi merged 1 commit into
mainfrom
dave/scan-budget-capability
Sep 2, 2026
Merged

Choose the scan-budget mechanism from the server's own descriptors#191
DavidBakerEffendi merged 1 commit into
mainfrom
dave/scan-budget-capability

Conversation

@DavidBakerEffendi

Copy link
Copy Markdown
Collaborator

The corrected v0.3.3 freeze failed identically to v0.3.2 — 28 cases with
reason=time_budget — and the reason is structural, not a bug in either
earlier attempt alone: the budget mechanism is version-split, and freezes pin
candidates on both sides of the split.

through v0.10.7 (evaluation registry pins v0.10.2) from v0.10.8 (active pin)
max_duration_secs argument the only way to raise the scan deadline rejected outright
BIFROST_MCP_REQUEST_BUDGET_SECS present but does not lift the scan's own deadline the replacement

Sending the argument errors every scan on one side (v0.3.2's failure mode);
withholding it re-measures the other under the 4.5-second interactive default
(v0.3.3's). No fixed behaviour serves both.

The fix

The runner asks the server which side it is on. McpSession gains a standard
tools/list probe, issued once per session, and the scan sends
max_duration_secs exactly when the server's scan_usages_by_location
descriptor advertises it in its inputSchema.

The signal is authoritative on both sides — v0.10.2 authors the property in its
schema; v0.10.8's own test asserts it absent — so this is self-describing
rather than a version table the runner would have to keep true. A server that
does not advertise the scan tool at all is refused loudly rather than
guessed at, because either guess silently mismeasures.

Unchanged: the env var still set at launch, the budget-plus-grace client
backstop, the flag and its recorded invocation metadata.

Dry-run evidence (all three legs, before any freeze)

v0.10.2 (d1a7c0cc), 300s — the acceptance bar was not "the case passes"
but reproduce the frozen v0.3.0 measurement, and it does, byte for byte:

v0.3.0 : failed, unexpected: config.rs 127:60, 128:9, 129:27, 137:28, raw ['found']
dry-run: failed, unexpected: config.rs 127:60, 128:9, 129:27, 137:28, raw ['found']

The scan ran 204 seconds uncut where v0.3.2/v0.3.3 cut it at 4.5.

v0.10.2, 1-second budget — bites immediately, proving the mechanism live
rather than vacuously accepted.

v0.10.8, 300s — still passes: 9 TP, 0 FP, 0 FN, both queries exact.

Consequence worth agreeing on before the next freeze

A v2 re-freeze with this fix reproduces v0.3.0's 17 failures, because it
measures v0.10.2 by construction — that is the registry the preregistered slice
binds. Publishing v0.10.8's improvement on that slice means advancing the
evaluation candidate registry, a reviewed change; #185 should be recast
accordingly. v0.3.2 and v0.3.3 both stand superseded; the site never published
either.

Verification

  • cargo test --locked — 256 passed (4 new), 0 failed
  • python3 -m unittest discover tests — 42 tests, OK
  • validate-reproduction-contract.sh — exit 0
  • The readiness-continuation fixture server now answers tools/list, which
    real servers serve statically before workspace readiness

🤖 Generated with Claude Code

The corrected v0.3.3 freeze failed the same way v0.3.2 did, 28 cases carrying
`reason=time_budget`, and the reason is structural: the budget mechanism is
version-split, and freezes pin candidates on both sides of the split.

Through Bifrost v0.10.7 the scan tools accept a per-call `max_duration_secs`
argument, and that argument is the only way to raise the scan's own deadline.
From v0.10.8 the argument is rejected and the budget is read from
`BIFROST_MCP_REQUEST_BUDGET_SECS` at server launch. The evaluation registry
pins v0.10.2 (`d1a7c0cc`), the active registry pins v0.10.8, and the runner
must measure both correctly. Sending the argument errors every scan on one
side; withholding it re-measures the other under the 4.5-second interactive
default, truncating large scans into failures.

No fixed behaviour serves both, so the runner now asks the server which side it
is on. `McpSession` gains a `tools/list` probe, issued once per session, and
the scan sends `max_duration_secs` exactly when the server's
`scan_usages_by_location` descriptor advertises it. The signal is authoritative
on both sides: v0.10.2 authors the property in its schema, v0.10.8's own test
asserts it absent. A server that does not advertise the scan tool at all is
refused loudly rather than guessed at, because either guess mismeasures.

The environment variable stays set at launch, the client backstop deadline
stays budget-plus-grace, and the flag and its recorded invocation metadata are
unchanged.

Dry-run before any freeze, all three legs:

- v0.10.2 (`d1a7c0cc`), 300s: `real-project-v2-rust-01-1` reproduces the
  frozen v0.3.0 record byte for byte -- `failed`, the same four unexpected
  usages in src/config.rs, zero missing -- with the scan running 204 seconds
  uncut where v0.3.2/v0.3.3 cut it at 4.5.
- v0.10.2, 1-second budget: bites immediately, proving the mechanism live
  rather than vacuously accepted.
- v0.10.8, 300s: the case still passes, 9 true positives, both queries exact.

The readiness-continuation test's scripted server now answers `tools/list`,
which real servers serve statically before workspace readiness.

A consequence worth stating: a v2 re-freeze with this fix reproduces v0.3.0's
17 failures, because it measures v0.10.2 by construction. Publishing v0.10.8's
improvement on that slice means advancing the evaluation candidate registry,
which is a reviewed change, not a re-freeze.

Refs #185

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@DavidBakerEffendi
DavidBakerEffendi merged commit c6ca82d into main Sep 2, 2026
5 checks passed
@DavidBakerEffendi
DavidBakerEffendi deleted the dave/scan-budget-capability branch September 2, 2026 13:20
DavidBakerEffendi added a commit that referenced this pull request Sep 2, 2026
Closes #185, recast: the published v2 result understates Bifrost not
because a
re-freeze is missing, but because the slice's frozen candidate registry
still
pins v0.10.2 — every v2 freeze re-measures last month's analyzer by
construction (v0.3.2/v0.3.3 demonstrated exactly this). The slice's own
README
states the doctrine:

> Any later Bifrost run is evidence for a subsequent snapshot or
release, never
> a replacement for v0.3.0 evidence.

## The change

**`candidates-v0.3.4.json`** — a new frozen identity set, not an edit:

- Bifrost advances to **v0.10.8 @ `8ddf1362`**, the same identity the
active
  development registry pins.
- Everything else is byte-identical to the v0.3.0 registry —
reference-server
identities, profiles, hashes. The corpus, protocol, selection, and
reviewed
  ground truth are untouched.
- `candidates-v0.3.0.json` stays byte-identical as the immutable record
of what
  v0.3.0 measured.

Consumers: the freeze workflow selects the new registry for evaluation
snapshots; shard identities hash both registries and read candidate
identity
from the current one; the reproduction contract asserts the superseding
registry's Bifrost matches the active pin **and** that no non-Bifrost
identity
moved, alongside its existing guard that the historical registry is
unchanged.
Tests cover the "advances only the Bifrost pin" property directly, in
both the
Python and Rust suites. `CITATION.cff` moves to 0.3.4 for freeze
preflight.

## Expected effect on the next v2 freeze

From the repository-tree measurement under v0.10.8: **12 of v0.3.0's 17
failures resolve, 5 remain** (#181 ×2, #182 ×2, #183), **0 new**. The
freeze is
the real acceptance test — please check the aggregate's counts against
this at
the release approval gate rather than taking the prediction on trust; in
particular, any `reason=time_budget` diagnostic means the scan-budget
fix
regressed and the run should not be approved.

Depends on #191 (merged): without the capability fix, a v0.10.8
evaluation
freeze errors every usage scan.

## Verification

- `cargo test --locked` — 257 passed (1 new), 0 failed
- Python suites — 44 tests OK (2 new)
- `validate-reproduction-contract.sh` — exit 0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant