Skip to content

Refuse the launch when omp names itself with a version we cannot read - #393

Merged
schickling merged 2 commits into
mainfrom
schickling-assistant/2026-08-30-2026-08-30-omp-gate-fail-closed
Aug 30, 2026
Merged

Refuse the launch when omp names itself with a version we cannot read#393
schickling merged 2 commits into
mainfrom
schickling-assistant/2026-08-30-2026-08-30-omp-gate-fail-closed

Conversation

@schickling-assistant

@schickling-assistant schickling-assistant commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Problem

find_release gives the provider's own <provider>/… token the last word — but only when that
token parses. When it does not, the loop falls through and keeps scanning, so any other release
in the banner can stand in for the one the provider just named.

if let Some(rest) = token.strip_prefix(labelled.as_str()) {
    if let Some(release) = parse_release(rest) {
        return Some((rest, release));
    }
    continue;            // <- the bug
}

#370 is merged, so this is live in shipped st2, not a defect in a PR under review. Driving the
deployed binary (re-resolved at probe time, reporting 0.1.0+c4d6f7a — this PR's base) with a
provider whose banner is omp/18.1.0-rc1 18.0.9:

$ st2 driver omp-session --catalog <throwaway> --identity <host>.gateprobe \
    --runtime-id s6 <fake provider>
omp/18.1.0-rc1 18.0.9        <- the provider was LAUNCHED

omp said it was 18.1.0-rc1. The gate admitted on the stray 18.0.9 and started it — the exact
outcome the function's own doc comment promises to prevent. The same probe reproduced it against
the preceding generation (0.1.0+08b67b3) too, so it is not an artifact of one build.

Goal

The provider's own label decides outright. If what it named cannot be parsed, the gate refuses
instead of reading some other token, and the omp wrapper fails closed rather than launching an
unverified provider.

After, same probe, this branch:

--- banner: omp/18.1.0-rc1 18.0.9 ---
Error: ... --version reported no unambiguous omp release: 'omp/18.1.0-rc1 18.0.9'

--- banner: omp/18.0.11 ---     (what the shipped binary prints)
omp/18.0.11                     <- still admitted, still launches

--- banner: omp/18.1.0 ---
Error: omp 18.1.0 is unverified (admitted minors: 18.0.x); repeat the
docs/vrs/06-omp-driver admission checks before extending the gate

Decisions

return instead of continue, not a rescan. The alternative — keep scanning and let an
agreeing unlabelled release stand in — would reintroduce the bug in a narrower form. The label is
the provider's own claim about itself; an unreadable one is information, not noise.

Both real banners checked, not assumed. The change is only safe because neither shipped
provider reaches the new refusal:

provider banner it prints path through find_release
omp 18.0.11 omp/18.0.11 own label, parses → decides
opencode 1.18.25 1.18.25 bare, never enters the labelled branch

Measured under the environment that actually applies. The gate runs <omp> --version before
the wrapper assembles its offline defaults, so it reads the banner under the ambient environment:

$ env -u PI_OFFLINE -u PI_SKIP_VERSION_CHECK omp --version   # cat -A
omp/18.0.11$
$ PI_OFFLINE=1 PI_SKIP_VERSION_CHECK=1 omp --version         # cat -A
omp/18.0.11$

One token either way on this build, so the defect is latent today — measured, not asserted.

Fixed now rather than when it becomes reachable. Two things make "latent" a thin margin.
DQ-OMP-5 is open on precisely the update banner that would add a second token, and the ambient
call above is exactly the call it would pollute. And the exact-version gate used to mask this: a
stray token had to equal an admitted version exactly. #370 keyed on the minor, so a stray token
now only has to land anywhere in an admitted series — #370 widened the reachable form of this bug,
and this closes it.

Documented one asymmetry rather than changing it. find_release returns on the FIRST
own-labelled token, so two disagreeing own labels resolve by order while two disagreeing
unlabelled releases fail closed. No provider prints two own labels. The second commit records
that in the doc comment and the spec; making the labelled case fail closed too would be more
principled and is a separate question.

Verification

The tests were not inert — that was the finding. This was found by independent verification of
#370 as mutation S6: deleting the labelled fall-through left the suite byte-identical, because
nothing asserted it. The two tests that looked like coverage did not reach it — one used a
parseable label, the other a foreign label.

Two tests now assert it: one on the parser, one driving the production gate
(verify_supported_version) through a fake provider binary. Both were written first and observed
red before the fix, and both go red again under S6 in either shape:

mutation harness_version test omp_session test
S6a — restore the continue FAILED FAILED
S6b — delete the labelled branch outright FAILED FAILED
fixed ok ok

Both tests run in the CI sandbox — read from the run log, not inferred from a green tick,
because nix flake check runs only the hermetic portion of the suite and the production-path test
spawns install and execs a shell script:

st2> test harness_version::tests::an_unreadable_own_label_fails_closed_instead_of_falling_through ... ok
st2> test omp_session::tests::an_unreadable_own_label_cannot_be_rescued_by_a_stray_admitted_version ... ok

CI green on this exact head (25d1854): check-x86_64-linux and check-aarch64-darwin both
success — run
33326134146.

Pre-flip deviation. Local cargo test is green on this branch except
eval_run_e2e::canonical_agents_freeze_the_admitted_route_across_post_boot_catalog_mutation,
which also fails on pristine origin/main (c4d6f7a) in the same worktree — reproduced at the
merge-base, not assumed. It is outside the hermetic set nix flake check runs, which is why main's
CI is green.

cargo fmt --check reports diffs across ~37 files on main, including lines in the two files this
PR touches that it does not modify. flake.nix states outright that fmt and clippy are
intentionally not gated, so formatting was left alone.

Complexity

No new complexity: one branch in one function goes from "keep looking" to "answer now", and the
control flow gets simpler. No new abstraction, dependency, or module boundary. +59 / −9 across
three files, most of it tests and prose.

Concerns

The refusal is strictly stricter than before, so a provider that starts printing
<provider>/<something-unparseable> as its normal banner would now fail to launch where it
previously launched on a stray token. That is the intended direction — failing closed on an
unreadable self-report is the point — but it is a behaviour change, and it would surface as a
launch refusal rather than a warning.

Friction & bottlenecks

Friction. Probing the gate end-to-end requires a throwaway catalog and a fake provider
executable, because a passing gate makes the wrapper actually launch the provider. There is no
read-only "would this version be admitted?" surface, so verifying gate behaviour against a real
binary is not something a reviewer can do casually. Logged rather than fixed here.

Follow-ups

  • MEASURED_CONTEXT_VERSIONS is ["18.0.9", "18.0.3"] while the fleet ships 18.0.11. The launch
    gate admits it; the harness-context token arithmetic was never measured on it, and the pin test
    only asserts measured ⊆ gate, so it stays green either way. omp_session.rs states these two
    questions must not be collapsed — flagged, not touched here, and tracked separately.
  • Whether the wrapper's offline defaults should be passed into the gate's own --version call,
    given DQ-OMP-5.
  • Whether two disagreeing own labels should fail closed, matching the unlabelled rule.

Not in this PR

The codex gate. src/codex_app_server.rs states that semantic-version proximity is not
compatibility evidence for that surface, which forbids the reasoning a version-series gate needs.

References

Posted on behalf of @schickling
field value
agent_identity dev3.dotfiles.omp.admission-18-0-9.worker
session dev3.1788234a
agent_persona worker
agent_supervisor dev3.dotfiles-lead
agent_tool Claude Code
agent_tool_version 2.1.251
agent_runtime Claude Code 2.1.251
tooling_profile dotfiles@6048b77

…rough

`find_release` gave the provider's own `<provider>/…` token the last word, but only
when that token parsed. When it did not, the loop fell through and kept scanning, so
any other release in the banner could stand in for the one the provider had just
named. `omp/18.1.0-rc1 18.0.9` admitted on the stray `18.0.9` and launched a provider
that reported itself as an unverified 18.1 pre-release — the exact outcome the
function's own doc comment promises to prevent.

The label now decides outright: if what the provider named cannot be parsed, the
answer is `None` and the caller fails closed. Both shipped banners are unaffected,
which was checked rather than assumed — omp prints `omp/18.0.11`, which parses, and
opencode prints a bare `1.18.25`, which never enters the labelled branch at all.

Latent today because the real binaries print one token each, but DQ-OMP-5 is open on
precisely the update banner that would add a second one, and the exact-version gate
that used to mask this is gone: keying on the minor means a stray token only has to
land in an admitted series, not match a version exactly.

Found by independent verification of #370 as mutation S6: deleting the fall-through
left the suite byte-identical, so nothing asserted it. Two tests now do, one on the
parser and one driving the production gate through a fake provider. Both go red under
S6 in either shape — restoring the `continue`, or deleting the labelled branch
outright.

agent-identity: dev3.dotfiles.omp.admission-18-0-9.worker
agent-persona: worker
agent-supervisor: dev3.dotfiles-lead
agent-tool: Claude Code
agent-tool-version: 2.1.251
agent-runtime: Claude Code 2.1.251
tooling-profile: dotfiles@6048b77
The rule the previous commit documented — the provider naming itself decides —
is true of the FIRST such token only: `find_release` returns on it. Two
disagreeing own labels are therefore resolved by order, while two disagreeing
UNLABELLED releases fail closed. No provider prints two own labels, so this
records the behaviour rather than changing it; making the labelled case fail
closed too would be more principled and is a separate question.

agent-identity: dev3.dotfiles.omp.admission-18-0-9.worker
agent-persona: worker
agent-supervisor: dev3.dotfiles-lead
agent-tool: Claude Code
agent-tool-version: 2.1.251
agent-runtime: Claude Code 2.1.251
tooling-profile: dotfiles@6048b77
@schickling-assistant
schickling-assistant marked this pull request as ready for review August 30, 2026 18:42
@schickling
schickling merged commit e0a155e into main Aug 30, 2026
2 checks passed
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.

2 participants