feat(crosscheck): make the cross-family reviewer lane executable on a named registry - #281
Merged
Merged
Conversation
ruby-dlee
added a commit
that referenced
this pull request
Aug 21, 2026
…edit check The acceptance is two completed cross-family reviews, one over a codex-authored change and one over a claude-authored change. Zero exist. Two attempts were made against #281 on the direct Fireworks lane: the first refused a complete model turn whose final text was not bare JSON, the second timed out in pi at 1800 seconds. Record both, record what is established by execution (the provider answers in under two seconds and streams to DONE, so the hang is in pi rather than the provider), and name unverified `reasoning_content` streaming as a suspicion rather than a cause. Retire the startup-credit decrement check as moot. It assumed Fireworks pay-per-token on Foundry billed as Azure consumption; Fireworks direct bills a Fireworks account and touches no Azure credit, so that charge can never appear in Cost Management. The Foundry account now has zero deployments, so the historical numbers are a record of a dead lane. Also state plainly what this change does not close: the acceptance, the status command that would make a silent fallback impossible, the `reasoning_content` verification, and a per-review spend meter. The provider slot now declares real per-million costs, but the ledger records no token usage at all, so R10's daily_budget_usd still has nothing to bind to.
ruby-dlee
force-pushed
the
feat/crosscheck-cross-family-reviewer
branch
from
August 21, 2026 03:58
08ad000 to
5a21df7
Compare
ruby-dlee
added a commit
that referenced
this pull request
Aug 21, 2026
…edit check The acceptance is two completed cross-family reviews, one over a codex-authored change and one over a claude-authored change. Zero exist. Two attempts were made against #281 on the direct Fireworks lane: the first refused a complete model turn whose final text was not bare JSON, the second timed out in pi at 1800 seconds. Record both, record what is established by execution (the provider answers in under two seconds and streams to DONE, so the hang is in pi rather than the provider), and name unverified `reasoning_content` streaming as a suspicion rather than a cause. Retire the startup-credit decrement check as moot. It assumed Fireworks pay-per-token on Foundry billed as Azure consumption; Fireworks direct bills a Fireworks account and touches no Azure credit, so that charge can never appear in Cost Management. The Foundry account now has zero deployments, so the historical numbers are a record of a dead lane. Also state plainly what this change does not close: the acceptance, the status command that would make a silent fallback impossible, the `reasoning_content` verification, and a per-review spend meter. The provider slot now declares real per-million costs, but the ledger records no token usage at all, so R10's daily_budget_usd still has nothing to bind to.
ruby-dlee
force-pushed
the
feat/crosscheck-cross-family-reviewer
branch
from
August 21, 2026 08:57
d15719c to
264c75c
Compare
…stry R6 requires reviews outside the author's model family. The lane was hardcoded to GLM-5.2 on Azure Foundry's Fireworks partner lane, which has never produced a completed review and cannot on this subscription: Fireworks-published deployments are Marketplace SaaS offers, and a credit-only "Microsoft Azure Sponsorship" subscription cannot purchase them, so `FW-GLM-5.2` returns HTTP 500 `invalid_model_endpoint_authentication` while non-Fireworks publishers on the same account, key and subscription return HTTP 200. Replace the single hardcoded model with CROSS_FAMILY_LANES, a code-side registry of vetted reviewer lanes. Each entry is a complete endpoint allowlist entry: deployment, Pi provider slot, chat-completions api surface, Foundry resource, host, and the one accepted base URL. The roster selects the serving lane by naming the model, so substituting among registered lanes is a config change; admitting a new endpoint stays a reviewed code change, because the allowlist is the security control. Registered: azure-kimi (Kimi-K2.7-Code), azure-deepseek (DeepSeek-V4-Pro), and azure-glm (FW-GLM-5.2), retained as evidence and out of the roster. Two distinct non-OpenAI vendors ahead of the codex fallback means one vendor outage does not drop reviews back to the author's own family. Every security property is preserved per slot rather than for one hardcoded slot: the endpoint allowlist, the refusal of a model-level baseUrl/api override (pi's composer lets a model-level field outrank the provider pin), the fail-closed inspector on a missing, malformed, or unexpected-provider credential, and the model image attestation guard. The lane is always keyed on the reviewer model from the code registry, never on what a credential file declares about itself, and the reviewer identity remains the non-secret Foundry resource/deployment binding. Ledger provenance becomes `cross-family-primary`; the legacy `glm-primary` value stays readable for durable records and stays bound to exactly that lane's model.
…M-5.2 The Azure Foundry partner lane cannot work on this subscription, and the reason is billing, not the vendor. Partner models are Marketplace SaaS offers; a credit-only "Microsoft Azure Sponsorship" subscription cannot purchase them, so every FW-* deployment returns HTTP 500 invalid_model_endpoint_authentication while non-Marketplace publishers on the same account and key return 200. The owner opened a direct Fireworks account, which bypasses Marketplace entirely, so GLM-5.2 is the reviewer again on its merits. Point the lane registry at the single lane fireworks-glm serving accounts/fireworks/models/glm-5p2 at https://api.fireworks.ai/inference/v1, and retire the azure-glm slot along with the two Azure R6 attempt models. The pinned model id, not the glm-5p2-fast router: a router may re-point to a different serving variant, and the reviewer identity this gate records has to name an exact model. Three defenses land with it. Lane matching is exact against the model id or pi's <slot>/<model> form. A lane model id now contains slashes, so the previous rsplit-based identity comparison would have missed the lane entirely. The registry pins each lane's model-level `compat`. `compat` is the second model-level object pi honors after baseUrl/api, and supportsFinishReason: false would blunt the truncated-verdict refusal, so the inspector, the Azure archive gate, and the model guest all refuse any compat that is not the lane's own. The registered lane declares none. Reviewer independence compares the model FAMILY, not the exact id. The first completed cross-family review of this branch reproduced a gpt-5.5 author being admitted a gpt-5.6-sol reviewer with no same-model marker, which is the same-family review R6 exists to prevent (cc-4dcd7873f71a). Registered lanes are their own family, gpt/o1/o3/o4/codex are one OpenAI family, claude-* one Anthropic family, and an unrecognized model stays its own family so nothing that previously passed starts failing. Truncation was already refused on two grounds; an end-to-end test now proves it, emitting a complete schema-valid clear verdict whose only defect is stopReason length and requiring the run to record tool-failure with no citations. Measured live: max_tokens 600 returns finish_reason length with empty visible content, 4000 completes. Also fixes the operator documentation that told captains to provision an openai-codex auth.json for every Pi reviewer, which misprovisions a cross-family lane home (cc-769d7eba2ded).
…fenced verdicts Both defects were found by a real completed crosscheck review of this branch (codex-family fallback lane, head b8deb37) and both were reproduced before being fixed. cc-5ec330d3c74d, high: provider-qualified authors bypassed family separation. pi records a model as <provider-slot>/<model>, and the family classifier fell back to model_identity, which keeps only the final segment. The SAME lane model reached through some other author-side slot therefore read as a different family, so a some-slot/accounts/fireworks/models/glm-5p2 author was admitted the fireworks-glm reviewer with no relaxation and no degraded marker. Family membership is now judged on the final segment against every registered lane, which errs toward calling two models the same family. Lane selection for credentials and providers keeps its exact match, because there the safe error runs the other way. cc-ca5848b19ac3, high: the compat pin checked only one of the layers pi composes. pi builds the effective model from mergeCompat(providerConfig.compat, definition.compat) plus a topmost modelOverrides[<id>] layer that also carries compat and headers, so a credential could still turn off supportsFinishReason behind the lane's empty pin and blunt the truncated-verdict refusal. Naming fields to refuse was the wrong shape: the credential is now an ALLOWLIST at every layer. The provider may carry only baseUrl, api, apiKey and models; a model entry only its descriptive fields plus the lane's pinned compat; and the document only providers. The Azure archive gate and the model guest mirror it. Also: a verdict wrapped in exactly one Markdown code fence spanning the whole message is unwrapped. GLM-5.2 through pi presents its JSON that way, which read as a malformed verdict artifact and cost the lane its first review. Prose around the fence, several fences, and an unclosed fence all still refuse, because each is a case where the reviewer said more than one thing. A truncated verdict never closes its fence, so this cannot turn one into an answer. The refusal now also names a bounded, repr-escaped prefix of the offending text, which the previous message did not, and reviewer output can therefore not inject a line into an operator's log. The registered-lane execution case is driven from the registry itself rather than a one-element loop, which also clears shellcheck SC2043.
…edit check The acceptance is two completed cross-family reviews, one over a codex-authored change and one over a claude-authored change. Zero exist. Two attempts were made against #281 on the direct Fireworks lane: the first refused a complete model turn whose final text was not bare JSON, the second timed out in pi at 1800 seconds. Record both, record what is established by execution (the provider answers in under two seconds and streams to DONE, so the hang is in pi rather than the provider), and name unverified `reasoning_content` streaming as a suspicion rather than a cause. Retire the startup-credit decrement check as moot. It assumed Fireworks pay-per-token on Foundry billed as Azure consumption; Fireworks direct bills a Fireworks account and touches no Azure credit, so that charge can never appear in Cost Management. The Foundry account now has zero deployments, so the historical numbers are a record of a dead lane. Also state plainly what this change does not close: the acceptance, the status command that would make a silent fallback impossible, the `reasoning_content` verification, and a per-review spend meter. The provider slot now declares real per-million costs, but the ledger records no token usage at all, so R10's daily_budget_usd still has nothing to bind to.
…it can evidence Attempt 1 on the Fireworks lane was never a hang. GLM went through pi, completed a turn, and was rejected at the last step on output SHAPE. Measured since, against the gate's exact review instruction and schema: GLM returns its verdict inside one ```json fence, and a bare parse of that fails with `Expecting value: line 1 column 1 (char 0)`, byte for byte the recorded failure. The extractor now unwraps EXACTLY ONE complete fenced block. One block leaves nothing to choose between, so surrounding prose is harmless; several complete blocks still refuse, because then the gate would be picking which one was the verdict. Requiring the fence to span the whole message would re-break the lane the first time a model prefaces its answer with a sentence, and the point of a registry-driven lane is that the next model needs no prompt re-tuning. This tolerates a wrapper, never a truncation. A truncated verdict never closes its fence, yields zero complete blocks, and still fails to parse; the stop-reason ground refuses it one step earlier. Both stay pinned, and the truncation mutation stays red. That pairing earned itself: a later attempt produced a verdict truncated mid-string while pi reported `stopReason: stop`, so only the JSON parse caught it. Also amends R6's acceptance to something the system can actually evidence. Eligibility derives from the task meta's `model` and never from `harness`, the ledger records no author identity at all, and git carries no harness signal by design, so "a claude-authored change" is a declaration rather than a checkable property. The acceptance becomes: the lane completes a review, and the family screen admits it against both a codex-model and a non-codex-model author, with the ledger recording the reviewer model, the family mode, and whether the relaxation was required. Records that declaring a codex author is the safe error and a claude author the dangerous one, and corrects this program's own two runs whose claude authorship was asserted rather than recorded.
…codex lane can run The compartment lane's codex-family path could never work. Two readers derived the same executing-account identity and disagreed by a literal prefix: `account_identity` returned `codex:<id>` / `openai-codex:<id>`, while `create_credential_archive` derived the BARE `<id>` separately and then refused on `archived_identity != reviewer_account_identity`. That comparison was structurally always true, so a live run refused at 04:43Z before any billable resource, and NO codex-family compartment review has ever run. Only the cross-family branch passed, because both sides there read one shared value, which is exactly why it went unnoticed. Fixed at the source of the disagreement rather than by making the comparison lenient: `account_identity_from_credential` is now the single derivation, `account_identity` reads the home's auth.json and calls it, and the archive parses the bytes it is about to package and calls the same function. No third place strips prefixes. Pinned by a test that drives the real readers end to end for BOTH codex-family branches and is RED on the two-derivation code, failing with the exact live message. It also asserts a genuinely different account still refuses, so the fix did not buy the pass by loosening the check. Carries the second finding too: the refusal left no durable record at all. `create_credential_archive` is called outside the window whose handlers persist a run, so this class of error escaped to main()'s catch-all and no ledger, report or data directory was written. It now converts to a tool failure, which is the same treatment the model-image attestation refusal twenty lines above already gets: the attempt is recorded and the roster rotates to the next reviewer account. Both owning documents said the compartment lane needed only a config flip. That was false and is corrected in each: `enabled: false` was masking an independent in-code blocker. This is the second merely-disabled claim this requirement has had to retract without anyone reaching the code, after the stale pi-binary reason.
…l guest
BLOCKER. The previous commit claimed "one executing-account derivation so
the compartment codex lane can run". It could still not run. The model
guest carried a THIRD derivation returning the bare account id while the
host digests the prefixed one, so both codex-family branches refused. Its
own docstring warned against exactly this ("do not fix a future mismatch
by stripping prefixes in a third place") and the third place already
existed.
It was also strictly worse than the bug it replaced. The old refusal fired
in create_credential_archive during stage, before reserve_model_capacity.
This one fires inside a booted, paid VM.
The guest ships self-contained onto the VM and cannot import the host
readers, so the duplication is inherent and the mitigation is coverage:
model_guest_executing_account_unit EXTRACTS the guest's own credential
block from the shipped bytes and EXECUTES it against real archives and the
host readers. It is red on the bare-id form with the exact live message,
and it also proves a foreign account and a credential with no account id
still refuse, so agreement was not bought by dropping the check.
That unit exists because the guest had zero behavioral coverage: the suite
asserted substrings were present in its source and never ran it, which is
why this was invisible. It now also pins registration completeness -
adding a lane to the guest table without a case "$MODEL" arm previously
passed every test and died at runtime with exit 125.
BLOCKER. A truncated verdict was defeated by any preceding complete fence.
A truncated fence contributes zero complete blocks, so a draft or example
fence earlier in the message left the count at one and the gate certified
THAT block, discarding the real answer, with stopReason stop and a
successful parse. Worse than what it replaced: the old shape failed
loudly, this passed quietly with a stale verdict.
Two rules now, and the second was found by the first test written for the
first: any unterminated fence refuses outright, and a single complete
block is unwrapped only when it is the ONLY JSON-bearing content, because
a COMPLETE example fence followed by a truncated BARE verdict also counts
one block. Prose carries no braces, so a wrapper is still tolerated.
Also, all pinned by tests:
- the archive gate's allowlists now reference core's rather than a local
copy that had already drifted weaker (no model-level allowlist, no api
check, so openai-responses could be archived - forbidden by R6)
- family matching normalizes separators, so gpt5.6-sol no longer reads as
its own family, and each lane carries the aliases of its own model, so
an author on GLM-5.2 under any vendor id cannot take the GLM reviewer
unmarked. Lane SELECTION stays exact; only the family screen folds.
- the TOCTOU refusal four lines after the previous durability fix now
persists as a tool failure instead of vanishing
docs/crosscheck.md claimed a truncated verdict "cannot parse against the
review schema either". That was false and is corrected, as are both
owning documents' accounts of the identity fix.
…sh from absence
All three shipped correct and all three were invisible to the suite:
removing each left it green. A guard no test can distinguish from its own
absence is not a control.
The odd-marker rule is not redundant with the brace-remainder rule, which
is why it needed its own case. The shape only it catches is an example
fence followed by a verdict truncated immediately after its OPENING fence:
markers 3, one complete block, and a brace-free remainder, so without the
marker count the example gets certified as the verdict. That is a
plausible truncation point and exactly the failure this lane exists to
close.
The model guest's manifest identity check binds the archive to the
REQUEST, not just the credential to the account, and dropping it admits a
forged manifest - six forgeries now refuse, covering effort, model,
harness, review generation, credential name and credential digest.
The TOCTOU re-proof is extracted into `require_stable_reviewer_credential`
so its exception CLASS can be driven rather than asserted from source.
The test also asserts the two classes stay distinguishable, so it cannot
pass vacuously if AzureCrosscheckError is ever made a CrosscheckToolError
subclass.
Also: `cross_family_provider_host_unit` asserted one hardcoded host for
every lane, so any genuinely new lane failed THERE first and the
registration-completeness guard this repo advertises never ran. It now
checks per-lane consistency instead, and adding a second lane without its
guest `case` arm fails with "lane example-second has no provider dispatch
arm in the guest" - the guard actually claimed for the job.
Records two accepted availability regressions in docs/crosscheck.md: a
verdict whose own string content holds a triple backtick, and a verdict
accompanied by brace-bearing prose, both now refuse. Both fail closed as a
tool-failure that rotates the roster, so they cost a reviewer attempt
rather than correctness - a real cost in a repo full of fences, paid to
make a superseded block impossible to certify.
Records in code that an alias list is inherently incomplete and is not a
security boundary; every listed gap is refused at admission today.
Removes the "refuses a cross-family model whose mapped slot is not that
lane's own slot" claim from docs/crosscheck.md. PI_MODEL_PROVIDERS is
built as {lane["model"]: lane["slot"]}, so that guard is structurally
unreachable. It stays as a cheap consistency assertion, now annotated as
unreachable, and no documentation claims it as a control.
ruby-dlee
force-pushed
the
feat/crosscheck-cross-family-reviewer
branch
from
August 21, 2026 09:08
264c75c to
47db9d7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Read this first: R6 is NOT closed by this PR
The acceptance for R6 is two completed cross-family reviews (a codex-authored change and a claude-authored change, each reviewed by a GLM-backed reviewer with bound identity and the codex lane's evidence discipline). Zero exist. None completed for this change either. Correct wiring is not the acceptance, and this body does not claim it is.
What this PR actually does is make the lane executable at all (it was not), close two reproduced high-severity bypasses, and write down honestly what is still missing.
docs/azure-requirements.mdR6 now opens withStatus: NOT DONE.Why the lane was not executable
R6 was bound to GLM-5.2 on Azure AI Foundry's Fireworks partner lane. That lane cannot work on this subscription, and the reason is Azure Marketplace billing, not the vendor:
FW-GLM-5.2returned HTTP 500invalid_model_endpoint_authenticationin ~0.2s, at every input size, on both routes, with both auth schemes, and from a fresh account in another region.x-ratelimit-*headers, which only happens after the caller is authenticated and metered.DeepSeek-V4-ProandKimi-K2.7-Codereturned HTTP 200; everyFW-*deployment is published by Fireworks AI through Marketplace and failed.Per Microsoft Learn, "Foundry Models from partners and community", credit-only sponsored subscriptions and subscriptions without an active pay-as-you-go billing method cannot purchase Marketplace SaaS offers. This subscription is "Microsoft Azure Sponsorship", and both
Microsoft.SaaSresources and acceptedMicrosoft.MarketplaceOrderingagreements were verified ZERO even after the owner deployed through the Foundry portal. Partner models require Marketplace, so the linkage to the publisher backend can never be established.The owner opened a direct Fireworks account, which bypasses Marketplace entirely. Separately verified: the Foundry account
aif-fm7c799d-eus01now has zero deployments, soazure-glm/FW-GLM-5.2on main are dead references and the pre-existing lane could not have served a review under any circumstances.Design decision: named registry, not a repoint
I generalized to a named cross-family reviewer lane registry rather than repointing the GLM constants. The deciding fact: the serving model changed three times during this work (Azure GLM, then Kimi + DeepSeek, then direct Fireworks GLM), and every repoint would have meant editing five call sites, two mirrored files and three test suites.
bin/fm-crosscheck.pycarriesCROSS_FAMILY_LANES. Each entry is a complete endpoint allowlist entry: model id, Pi provider slot, chat-completions api surface, endpoint host, the one accepted base URL, and the exact model-levelcompata credential may carry.PI_MODEL_PROVIDERS,allowed_profiles, the credential inspector, the family-provenance marker, the Azure provider-host derivation, the credential archive, and the model-guest launcher are all derived from it. The registered-lane execution test is driven from the registry itself, so a lane added there is covered without touching the test.The line I deliberately did not cross: the registry is code, not config. Selecting among registered lanes is a config change (the roster names the model). Admitting a new endpoint stays a reviewed code change, because the endpoint allowlist is the security control. If the pin lived in
$FM_HOME/config, a file outside code review could introduce an endpoint the policy never named, which is exactly what the inspector exists to prevent.This is also strictly less duplication than before:
fm-crosscheck-azure.pymirrored four GLM constants with four assertions; it now mirrors the registry with one whole-dict equality assertion, so a lane added on one side and not the other fails the suite instead of producing a divergent allowlist.Registered lane: slot
fireworks-glm, modelaccounts/fireworks/models/glm-5p2, endpointhttps://api.fireworks.ai/inference/v1, apiopenai-completions, no compat pin. The pinned model, notaccounts/fireworks/routers/glm-5p2-fast: a router may re-point to a different serving variant, and the reviewer identity this gate records must name an exact model. Seconds of latency are not worth an unattributable reviewer on a multi-minute review.Two high-severity bypasses, found by a real review and fixed
Both were found by an actual completed crosscheck review of this branch (codex-family fallback lane), and both were reproduced by the reviewer before being fixed here.
cc-5ec330d3c74d — provider-qualified authors bypassed family separation. pi records a model as
<provider-slot>/<model>, and the family classifier fell back tomodel_identity, which keeps only the final segment. The same lane model reached through another author-side slot therefore read as a different family, so asome-slot/accounts/fireworks/models/glm-5p2author was admitted thefireworks-glmreviewer with no relaxation and no degraded marker. Family membership is now judged on the final segment against every registered lane, which errs toward calling two models the same family. Lane selection for credentials and providers keeps its exact match, because there the safe error runs the other way.cc-ca5848b19ac3 — the compat pin checked one of several layers. pi builds the effective model from
mergeCompat(providerConfig.compat, definition.compat)plus a topmostmodelOverrides[<id>]layer that also carriescompatandheaders(dist/core/provider-composer.js). A credential could still setsupportsFinishReason: falsebehind the lane's empty pin and blunt the truncated-verdict refusal. Naming fields to refuse was the wrong shape: the credential is now an allowlist at every layer — the provider may carry onlybaseUrl,api,apiKey,models; a model entry only its descriptive fields plus the lane's pinned compat; the document onlyproviders. The Azure archive gate and the model guest mirror it.An earlier review of the same branch also produced cc-4dcd7873f71a (independence compared exact model ids, so a
gpt-5.5author was admitted agpt-5.6-solreviewer with no marker) and cc-769d7eba2ded (operator docs required anopenai-codexauth.jsonfor every Pi reviewer, which misprovisions a cross-family home). Both fixed.Truncation is a failed review, not a verdict
GLM-5.2 is a reasoning model and spends the output budget on reasoning first. Measured live:
max_tokens=600returnedfinish_reason: lengthwith empty visible content; 4000 completed cleanly. Empty is the dangerous shape, because it looks like a response while carrying no verdict.Refused on two independent grounds, both pre-existing: pi maps
finish_reason: lengthtostopReason: "length"and the parser refuses any final turn whose stop reason is notstop; and the verdict must parse as JSON against the review schema, which truncated output cannot. Now pinned by an end-to-end test that emits a complete, schema-valid clear verdict whose only defect is the stop reason, and requires the run to recordtool-failurewith no citations. The lane declaresmaxTokens32000.Verdict fence handling (a deliberate parser relaxation — please push back if you disagree)
GLM-5.2 through pi presents its JSON inside a Markdown fence, which read as
malformed verdict artifactand cost the lane its first review attempt. Exactly one fence spanning the whole message is now unwrapped. Prose around the fence, several fences, and an unclosed fence all still refuse, because each is a case where the reviewer said more than one thing and the gate must not choose which part was the verdict. A truncated verdict never closes its fence, so this cannot turn one into an answer. The refusal also now names a bounded, repr-escaped prefix of the offending text — the previous message named no cause at all, which is what made the first lane failure unreadable — and the escaping means reviewer output cannot inject a line into an operator's log.crosscheck-same-model: set tooffIt was
on, a sanctioned degraded state that existed only because no cross-family lane could finish a review, so the codex fallback had to be allowed to review codex-authored work.With a non-OpenAI primary configured, leaving it on would mean a transient provider hiccup silently drops back to same-family review — the exact defect R6 removes — and a degraded relaxation left enabled after its cause is addressed is how a temporary state becomes permanent. With it off, the fallback still serves any author outside the codex family, and a codex-authored PR whose primary is down fails closed rather than being self-reviewed. The single-primary risk is accepted deliberately; flipping it back on is an operator act.
Security properties, all preserved and now per layer
definition.api ?? providerConfig.api, so a model entry's ownbaseUrl/apiwould outrank the provider pin. Still refused, including an override repeating the pinned values, with tests for each field alone and together.fm-crosscheck-azure.pyuntouched and still refusing an image that does not attest the dispatched harness.slot:host/modelbinding; the credential identifier is a digest of host + model + endpoint, provably key-independent (two configs differing only inapiKeyproduce byte-identical identifiers). No key appears in this diff, its tests, or this body.PI_MODEL_PROVIDERSentry does not resolve to its own slot refuses instead of launching.Compartment codex-family lane: it could never have run
Found by a live compartment run and fixed here because this PR already rewrites the file heavily.
Two readers derived the same executing-account identity and disagreed by a literal prefix:
account_identityreturnedcodex:<id>/openai-codex:<id>, whilecreate_credential_archivederived the bare<id>separately and then refused onarchived_identity != reviewer_account_identity. That comparison was structurally always true, so no codex-family compartment review has ever run — a live run refused at 04:43Z before any billable resource. Only the cross-family branch passed, because both sides there read one shared value, which is exactly why it went unnoticed.Fixed at the source of the disagreement, not by loosening the comparison:
account_identity_from_credentialis now the single derivation;account_identityreads the home'sauth.jsonand calls it, and the archive parses the bytes it is about to package and calls the same function. No third place strips prefixes. Pinned by a test that drives the real readers end to end for both codex-family branches, is red on the two-derivation code with the exact live message, and also asserts a genuinely different account still refuses — so the fix did not buy its pass by weakening the check.Second finding, carried: the refusal left no durable record at all.
create_credential_archiveis called outside the window whose handlers persist a run, so this class of error escaped tomain()'s catch-all and no ledger, report, ordata/directory was written. It now converts to a tool failure — the same treatment the model-image attestation refusal twenty lines above already gets — so the attempt is recorded and the roster rotates to the next account.Both owning documents were wrong in the same direction and are corrected: each said the compartment lane needed only a config flip.
enabled: falsewas masking an independent in-code blocker. That is the second merely-disabled claim R6 has had to retract without anyone reaching the code, after the stalepi-binary reason.R6's acceptance is unevidenceable as written, and is amended
Traced from code: eligibility derives from the task meta's
modeland never fromharness; the ledger records no author identity at all (author_account_identity=""); and git carries no harness signal by design. The task meta is the only authorship input and it is a declaration, not a record. "A claude-authored change" is therefore not a checkable property of any artifact this system produces.The acceptance becomes what the system can evidence, which is also what the 2026-08-19 amendment's own reasoning needs:
Recorded with it: declaring a codex author is the safe error (it can only narrow eligibility and forces the relaxation into the record); declaring a claude author is the dangerous one (it widens eligibility silently and prints "relaxation was not required"). And a correction to this program's own artifacts — the two runs in
data/crossfamily-r6-281/assertedharness=claudeover a PR whose real author harness is not recorded anywhere, so they must not be counted toward the acceptance.Second review round: two blockers fixed
Blocker 1 — the identity fix was incomplete, and cost a VM. My previous commit unified the two host derivations; the model guest carried a third that still returned the bare account id, so both codex-family branches refused inside a booted, paid VM rather than during staging. My own docstring warned against a third place; the third place already existed. All three now derive the prefixed identity. The guest ships self-contained onto the VM and cannot import the host readers, so the duplication is inherent and the mitigation is coverage.
Blocker 2 — a truncated verdict was defeated by any preceding complete fence. A truncated fence contributes zero complete blocks, so a draft or example fence earlier in the message left the count at one and the gate certified that block —
stopReason: stop, parse succeeds, real answer discarded. Worse than what it replaced: the old shape failed loudly, this passed quietly with a stale verdict. Two rules now, and the second was found by the first test I wrote for the first: any unterminated fence refuses outright, and a single complete block is unwrapped only when it is the only JSON-bearing content, because a complete example fence followed by a truncated bare verdict also counts one block.HIGH — the model guest had zero behavioral coverage. That is why blocker 1 was invisible: the suite asserted substrings were present in the guest source and never ran it.
model_guest_executing_account_unitnow extracts the guest's own credential block from the shipped bytes and executes it against real archives and the host readers. It also pins registration completeness — a lane added to the guest table without acase "$MODEL"arm previously passed every test and died at runtime with exit 125.MEDIUMs fixed: the archive gate's allowlists now reference core's rather than a local copy that had already drifted weaker (no model-level allowlist, no
apicheck, soopenai-responsescould be archived — forbidden by R6); family matching normalizes separators sogpt5.6-solno longer reads as its own family, and each lane carries its model's aliases so an author onGLM-5.2under any vendor id cannot take the GLM reviewer unmarked (lane selection stays exact — only the family screen folds); and the TOCTOU refusal four lines after the previous durability fix now persists as a tool failure.Stated honestly, not fixed:
AzureCrosscheckErroris a bareRuntimeErrorand roughly 20 further refusals in that region still vanish. Making it subclassCrosscheckToolErrormatches the diagnosis, but it converts every one of those sites into "persist and rotate the roster" and I could not exercise the compartment lane live to confirm that is right at every site. Also unfixed:crosscheck-same-model: offfails closed correctly but files the refusal astool-failure, which the fleet reads as "environment fault, rotate" rather than "this PR is unreviewed" — a policy refusal mislabeled as an environment fault.Ambient-seal interaction (#282), checked rather than assumed
Re-ran all three suites through
tests/run.sh— the authoritative sealed entry point — with the operator environment deliberately present, so the seal had something real to drop. It fired:FM_AMBIENT_SEAL dropped=26 names includingFM_HOMEand the wholeFM_AZURE_*set. All three suites pass sealed: 90 / 17 / 21, exit 0.No crosscheck unit depended on an ambient
FM_*the seal removes — nothing changed state between the unsealed and sealed runs. Worth noting the seal helps this PR:FM_HOMEis exactly the variable my task brief warned madefm-worker-lifecyclenon-hermetic, and #282 now removes it at admission.Your suspicion about sibling scope is confirmed, and it is broader than the two switches you named. The seal is
exact FM_HOME,exact FM_WORKER_PROVIDER_COMMAND,exact FM_SPAWN_CLOUD,prefix FM_AZURE_,prefix AZURE_,prefix ARM_. All 23FM_CROSSCHECK_*variables are unsealed — none matches an exact name or a prefix. That includes the two routing switches you asked about,FM_CROSSCHECK_REVIEWER_CONFIGandFM_CROSSCHECK_EXECUTION_MODE, and also four that redirect executables:FM_CROSSCHECK_PI_BIN,FM_CROSSCHECK_CODEX_BIN,FM_CROSSCHECK_SANDBOX_BIN,FM_CROSSCHECK_PYTHON. Same shape asFM_SPAWN_CLOUDsealed /FM_SPAWN_SECONDMATE_CLOUDnot. Not fixed here — route it to the branch holding that finding.Admit-red mutations
Each removes an admission control; each goes red because the wrong thing was accepted, not because a message changed. The source is restored from a byte copy and the restore is digest-verified.
AssertionError: unusable cross-family credential was accepted: model-level baseUrl/api overrideopenai-codexmodel_familyAssertionError: some-other-slot/accounts/fireworks/models/glm-5p2AssertionError: unusable cross-family credential was accepted: provider-level fields the lane does not pinstopReason: lengthnot ok - truncated cross-family verdict: expected exit 1, got 0(the truncated verdict became a clear verdict)Verification
python3 tests/run-one.py bash tests/<suite>.test.sh,FM_HOMEunset:fm-crosscheck90 ok,fm-crosscheck-azure16 ok,fm-crosscheck-slack21 ok, all exit 0.bin/fm-lint.shclean over 246 scripts.Lane status: five attempts, each failing further along
malformed verdict artifactbounded command timed out after 1800 secondsUnterminated string ... column 3045UNREVIEWED: the reviewer's ownexecuted_reproduction.commanddid not name both SHAsAttempt 4 is the milestone: the ledger recorded a genuine cross-family reviewer record against a codex-model author —
model: accounts/fireworks/models/glm-5p2,review_family_mode: cross-family-primary, nomodel_independencemarker,credential_source: pi-fireworks-glm-models-file,credential_identifier: provider-binding:fireworks-glm:d2a164ff....Measured along the way:
reasoning_contentin streamed deltas is not the problem (390 and 867 reasoning deltas reach[DONE]in 21–24s, max gap 3.4s), which answers R6's own unverified Work item and disproves the suspect this doc previously named; and the token cap is not the truncator (bothmax_completion_tokensandmax_tokenshonored at 20000).What remains is the reviewer obeying the review's own evidence discipline. That must not be relaxed to get a green verdict — a lane that earns its first verdict by lowering the evidence bar is worth less than no lane. Strengthening the instruction is the legitimate next step and is deliberately not done here: that prompt is shared with the codex lane, which satisfies the clause today.
What is still open after this PR
git ls-files --error-unmatch, and line-in-range). The third does not:MAX_LEDGER_PROMPT_BYTES(64,000) andMAX_PROJECTED_FINDINGS(512) are fixed constants and nothing reads the lane's declaredcontextWindow.reasoning_contentin streamed deltas, never verified, and the leading suspect for pi hanging against this provider (hangs at 420s, 900s and 1800s while direct streaming of the same model answers in 1.56s and reaches[DONE]). Labelled a suspicion, not a diagnosis.@earendil-works/pi-ai/dist/models.js), but this change does not make the ledger cost non-zero:bin/fm-crosscheck.pyrecords no token usage at all, so R10'sdaily_budget_usdstill has nothing to bind to.No cloud resources were created by this change. Do not merge on my account.