Skip to content

SH-B: Production Rust realization closure now - #8180

Merged
briansrls merged 2 commits into
mainfrom
session/wise-swift-269-b2-value-expressions
Aug 12, 2026
Merged

briansrls merged 2 commits into
mainfrom
session/wise-swift-269-b2-value-expressions

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Auto-opened by session-dashboard for session wise-swift-269.
Pushing to session/wise-swift-269-b2-value-expressions advances this PR.

Worker attestation

Before flipping this PR to ready for review, confirm each item:

  • Title describes the change (not the session id or branch).
  • PR body summarises what and why (replace the TODO below).
  • Tests run: name the command (e.g. npm test, cargo test) and the result.
  • If this closes a work item, the body contains a Closes #N directive.
  • No commits on this branch are surprises (no fork/cherry-pick I did not make).
  • No secrets / credentials / large binaries staged.

Summary

TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.

Test plan

  • TODO: list the commands that ran (or "no tests changed; relied on CI") and the outcome.

…n Rust

Both shapes are already self-closing, so what was missing was spellings on
the production lex rather than structure. Goldens were fixed by compiling
and EXECUTING them with rustc before the emitter was pointed at them, so
they do not share an ancestor with the code that produces them.

The new rules are scoped to a production lex set rather than appended to
rust_fn_add_lex_rules, which is the shared prefix of every other Rust rule
set. Lookup is first-match-wins, and three of these classes are re-spelled
downstream for a different purpose: rust_extended_lex_rules is an ingestion
set with whitespace trivia, spelling kw_let "let", equal "=" and semicolon
";" as bare lexemes, while emission has no trivia pass and bakes spacing in.
Appending to the shared prefix would have silently re-spelled the ingest
lexer's tokens and the loop and type-alias fixtures' semicolon.

Conditional and loop are deliberately excluded: TargetConditionalShape has
no closing token and TargetLoopShape has neither open nor close, so their
braces are supplied by hand-authored fixture scaffolds ending in two rbrace.
Production block close is a single rbrace, so they would emit unbalanced
Rust. That is a shape change, held for the next slice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 12, 2026 10:15
@gunbai-bot

gunbai-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

The ci failure on this PR is not caused by this PR, and there is no fix to push here.

The floor's witness corpus ran fully green, including this PR's three new witnesses. The job then failed at discovery resolve, on a witness in a different lane:

resolve failed for dag/test/claim/self_host_artifact_materialization_real_execution_witness_test.dag:
dag/gunbc/self_host_artifact_materialization.dag:32:3:
error: name 'ArtifactIdentity' not found in module 'v2.compiler.self_host.generation'

That resolve failure fails the discovery corpus, so the ordinary floor worker exits 1 and the coordinator refuses before scoped workers — which is why the whole job reds despite every witness passing.

Cause is on main, from two individually-correct merges landing two minutes apart:

#8177 could not have known about a consumer that had existed for two minutes, and its own title notes it only broke whole-tree. Neither PR is wrong on its own; the pair is. The remedy is one line — either the import moves to GeneratedArtifactIdentity, or generation re-exports the old name — and it belongs to that lane, not to this branch. Reported to SH-A.

This PR is otherwise green with two approvals and no findings.

— sent from wise-swift-269

This branch's ci was red only because main carried a broken import between
#8176 and #8187; the witness corpus here ran fully green.
@gunbai-bot

gunbai-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

The MODELING-COHERENCE unavailable is a rate-limited credential, not a modeling issue — nothing to fix here.

The check never read the diff:

Command failed: gh pr diff 8180 --repo gunb-ai/gunbc
HTTP 403: API rate limit exceeded for installation ID 136673607

It reported unavailable rather than passing, which is the correct fail-closed behaviour — but an unavailable observation is not a verdict about this PR's content, and pushing a change in response would be reacting to an absent measurement.

Verified rather than assumed: the exhausted quota belongs to installation 136673607 (the dashboard app's credential). From this session gh api rate_limit reports core: 4900/5000, and gh pr diff 8180 succeeds and returns exactly the two files this PR touches:

  • src/v2/extdeps/languages/rust.dag
  • src/v2/test/claim/emit/rust_production_closure_let_emit_test.dag

So the merge of main did not widen the change either. It should clear on re-run once the installation's quota resets.

Separately: the earlier ci red on this branch was the ArtifactIdentity break on main, fixed by #8187 and merged in here, so CI is re-running on that basis.

— sent from wise-swift-269

@briansrls
briansrls merged commit 511208e into main Aug 12, 2026
5 checks passed
@briansrls
briansrls deleted the session/wise-swift-269-b2-value-expressions branch August 12, 2026 15:56
gunbai-bot Bot pushed a commit that referenced this pull request Aug 12, 2026
build and ci both refused with `expected expression, found EqEq`. The three roster-identity claims
wrapped their comparison so that `== ContentHashEqual` began a line, and the .dag grammar does not
continue an expression across that break. A line-leading `&&` does continue — the existing
classify_normalize_diagnostics relies on it — so the shape is specific to the equality operator
rather than to operators generally.

Also merges origin/main, which had moved four commits ahead (#8180, #8164, #8185, #8189).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DBBegUkJygQyr1zMHiv2eK
gunbai-bot Bot pushed a commit that referenced this pull request Aug 12, 2026
…decode repair

witness_capacity_unknown_is_not_capacity asserted LimitObservationUnobserved,
the subject-less arm the bridge returned BEFORE review 51237. That repair moved
CapacityUnknown onto LimitSubjectUnobserved so an undecoded bucket refuses its
own draw without refusing a measured sibling; the witness was not re-read after
the producer changed, so it reported the repair as a regression.

The assertion is now stronger than a swapped label: it matches the subject and
pins native_key, which is the fact the repair added and the whole reason the
subject-carrying arm exists. The CI red is the discriminating receipt — the old
arm fails against the current producer, the new one passes.

Also merges main (#8182, #8180, #8164).
briansrls pushed a commit that referenced this pull request Aug 12, 2026
* Prepare the frontend world once for the whole census instead of per member

The census rebuilt the world fifteen times. classify_source called dag_lex_rules() and
dag_grammar() itself, so every member revalidated the grammar and recomputed the FIRST/nullable
analysis for a grammar that does not vary between members.

The seam already existed and this reuses it rather than inventing one: 02_parse's
prepared_grammar_carrier_note records that prepare_grammar exists so a K-module walk validates
once instead of K times, with parse_module_prepared as the per-module half. take_frontend_census
prepares once, classifies every rostered member through that one world, and reconciles the
population before anything may read it as a census.

THREE STATES, NOT ONE LIST. FrontendCensusReceipt is CensusTaken | CensusRefusedAtWorld |
CensusRefusedAtReconciliation. A refused world is a fact about the world, not about any member —
reporting it as fifteen ParseRefused stages would say fifteen members failed to parse when none
was ever examined. CensusTaken is reachable only through the exact-reconciliation arm, so an
unreconciled population cannot present as a census.

classify_source keeps preparing LAZILY and now returns an Outcome. The laziness is a budget fact:
a lex refusal is decided before any grammar is consulted, which is the only reason the LEX claim
executes in the fast lane at all. The Outcome is because preparation can refuse and no
FrontendStage honestly represents that.

Roster identity is a structural hash fold over the paths in order, parameterized by the rows so
discrimination is testable — an identity function that can only see the live roster is
unfalsifiable. It does NOT cover labels: the only Symbol-to-text surface is symbol_lexeme, whose
.dag body is a self-recursive host-intercepted stub, and grounding a provenance claim on that
would be worse than the gap. Recorded as a known limit with the one case it cannot separate.

NOT DELIVERED: the census still does not execute as a required gate. This removes a fifteen-times
multiplier from a cost that may still exceed the budget alone, and I have not measured the result
— there is no claim_batch binary in this container, so any figure would be invented. The
enrollment decision is deferred to a measurement, not to an opinion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DBBegUkJygQyr1zMHiv2eK

* Bind the hash comparisons to a let: a line-leading == is a parse error

build and ci both refused with `expected expression, found EqEq`. The three roster-identity claims
wrapped their comparison so that `== ContentHashEqual` began a line, and the .dag grammar does not
continue an expression across that break. A line-leading `&&` does continue — the existing
classify_normalize_diagnostics relies on it — so the shape is specific to the equality operator
rather than to operators generally.

Also merges origin/main, which had moved four commits ahead (#8180, #8164, #8185, #8189).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DBBegUkJygQyr1zMHiv2eK

* Route the six probes through an Outcome-matching renderer, per review 51409

REQUEST_CHANGES was correct and the finding is exact: classify_source now returns
Outcome<FrontendStage>, and the six p_* wall-attribution probes still fed that straight into
frontend_stage_label, which takes a bare FrontendStage. The witness was updated; the probes were
not. Same six probes, same omission, second increment running — I changed a callee's type and
did not check its callers.

render_classified matches both arms. A refused world renders WORLD_PREPARATION_REFUSED rather
than any stage word, because no stage was reached and borrowing one would be the conflation the
receipt's three states exist to prevent, repeated at the presentation boundary.

I then swept every symbol this PR added, renamed or retyped for surviving references. Two stale
prose mentions of observe_roster now name take_frontend_census. The remaining mentions of
count_stage and classify_normalize_reasons are deliberate — they are the notes recording why
those were removed, and they should outlive the code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DBBegUkJygQyr1zMHiv2eK

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Aug 14, 2026
…decode repair

witness_capacity_unknown_is_not_capacity asserted LimitObservationUnobserved,
the subject-less arm the bridge returned BEFORE review 51237. That repair moved
CapacityUnknown onto LimitSubjectUnobserved so an undecoded bucket refuses its
own draw without refusing a measured sibling; the witness was not re-read after
the producer changed, so it reported the repair as a regression.

The assertion is now stronger than a swapped label: it matches the subject and
pins native_key, which is the fact the repair added and the whole reason the
subject-carrying arm exists. The CI red is the discriminating receipt — the old
arm fails against the current producer, the new one passes.

Also merges main (#8182, #8180, #8164).
gunbai-bot Bot pushed a commit that referenced this pull request Aug 15, 2026
…decode repair

witness_capacity_unknown_is_not_capacity asserted LimitObservationUnobserved,
the subject-less arm the bridge returned BEFORE review 51237. That repair moved
CapacityUnknown onto LimitSubjectUnobserved so an undecoded bucket refuses its
own draw without refusing a measured sibling; the witness was not re-read after
the producer changed, so it reported the repair as a regression.

The assertion is now stronger than a swapped label: it matches the subject and
pins native_key, which is the fact the repair added and the whole reason the
subject-carrying arm exists. The CI red is the discriminating receipt — the old
arm fails against the current producer, the new one passes.

Also merges main (#8182, #8180, #8164).
briansrls added a commit that referenced this pull request Aug 18, 2026
… standing (#8166)

* PRESS-0 step 2: bridge codex press rate-limit buckets into the standing limit axis

gunbc.codex_app_server_press already decoded per-bucket rate limits, and
gunbc.provider_standing already modelled the limit axis, and nothing
joined them — so the probe bridge left limits Unobserved and a measured
bucket produced the same verdict as an unmeasured one.

account_standing_authenticated_rate_limits_readable_note specifies this
consumer by name and carries its own dissolution: read
evidence.rate_limit_buckets per limit_id, project each
CodexRateLimitBucketVerdict onto ProviderNativeLimitBucket, never route
through ProviderAccountLimit, and never use CodexPressAccountReady as
the limit axis. That is what this does.

Bucket scope is load-bearing rather than stylistic: the account
classification gates on the codex bucket alone, so it reports Ready
while a sibling is exhausted. The discriminating witness is the case the
note names — one observation set with codex exhausted and
codex_bengalfox available yields DIFFERENT verdicts under draws naming
each bucket. An account-scoped projection cannot produce that and fails
the witness.

CapacityUnknown becomes LimitObservationUnobserved, never
LimitObservedAvailable: could-not-decode read as nothing-is-wrong would
admit a dispatch against a bucket nobody measured. An empty bucket list
becomes one located Unobserved; limit_observations_are_selection_eligible
already refuses empty, so this adds a reason rather than changing the
verdict.

NOT DONE, and it still blocks selection: the entitlement axis. No probe
grounds ProviderEntitlementObservation for codex, and
EntitlementObservationUnobserved is not selection-eligible, so codex
remains ineligible on the six-axis conjunction even with limits now
observed. The press note is explicit that account standing is not
account-wide entitlement, so I have not fabricated one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Dissolve the dead belt_observe entry points and correct a stale roster note

Both raised by review on #8132 as non-blocking follow-ups.

belt_observe and belt_observe_for_instance are deleted. They observed
the tmux session NAME set only, which is precisely why a retained dead
pane read as a live agent; dispatch and tick both call
belt_sessions_observe_for_instance now, so the pair had no production
caller left.

belt_observe_from_result SURVIVES deliberately and is now witness-only.
Its subject is the tmux stdout parse — nonzero exit with no documented
no-server marker, unparseable line, empty set — reached by eight
witnesses. Deleting it with its caller would have deleted that evidence,
and §4b is explicit that a climb dissolves the lower-rung production
handling and never the discriminating evidence. Its dissolve-on is named
on the carrier: re-point those parse witnesses at
classify_tmux_session_list with the refusal cases preserved one-for-one.

belt_dispatch_all_status_labels_note said an "8th variant" makes the
label match non-exhaustive, which stopped being true at twelve. The
mechanism it describes is unchanged, so the sentence now names a new
variant rather than a count — a count in prose beside a live roster is
the stale-citation class, and it rots without anyone touching it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Carry the bucket subject on an undecoded limit, per review 51237

CONFIRMED, and the root is worse than the symptom. The bridge matched
CodexBucketCapacityUnknown { limit_id: _ } — discarding a limit_id the
producer already held — and mapped it to LimitObservationUnobserved,
which limit_observation_blocks_dispatch answers `true` for against EVERY
draw. So one bucket failing to decode refused a draw naming a measured,
available sibling.

This is a fail-CLOSED error, which is why the existing sibling witness
did not catch it: the exhausted/available pair still discriminated
because neither was unknown. Over-refusal reads as caution while it
prices out capacity the observation proved was there.

ProviderLimitObservation gains LimitSubjectUnobserved { subject,
native_token, reason }, routed through limit_subject_covers_dispatch_draw
like every other subject-bearing arm. It blocks a draw naming that
bucket, and still blocks Unspecified and Automatic draws because those
cover every native bucket — precision where the draw names a subject,
fail-closed where it does not. It is distinct from
LimitCapabilityGapUnobservable, which means the axis is unobservable on
this surface at all and correctly never blocks.

The zero-bucket case keeps the subject-less LimitObservationUnobserved:
with no buckets there is no subject to carry and refusing every draw is
right.

Witnessed both directions — the sibling draw is admitted, and the
undecoded bucket's own draw plus Unspecified and Automatic are all
refused. limit_observation_blocks_dispatch was the only exhaustive match
over the coproduct; the cursor standing match carries a catch-all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Carry the native cause token on an exhausted bucket; name the dropped reset

native_token carried limit_id, which the subject's native_key already
carries — so the field restated the bucket and dropped the only thing it
could have said. It now carries reached_type when the wire supplied one,
falling back to limit_id because native_token is not optional and a
bucket that reported exhaustion without a reached_type still has to name
something actionable.

resets_at is STILL DROPPED, and is now named on the carrier rather than
discarded quietly. ProviderLimitObservation has no field to hold it —
LimitUnavailableUntilReset is { subject, native_token, reason } — so
"when does waiting become valid again" has nowhere to land. That is a
model gap, not a projection choice, and serializing a timestamp into
`reason` would put a machine fact in a prose field no consumer can read.
Dissolve-on: LimitUnavailableUntilReset gains a typed reset instant.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Derive the limit credential from the press receipt, not from the caller

Handback item 1 (review artifact 51237).
provider_limit_observations_from_codex_press_evidence took the credential
identity and the evidence as INDEPENDENT arguments, so a caller could
take buckets observed under one CODEX_HOME and publish them as the
standing of a different credential. The standing would be honestly
decoded and attached to the wrong account — a forgery no arm refuses,
because both arguments are individually valid.

The receipt already carries the answer:
CodexAccountPreflightSubject.credential is the exact ambient context the
trip ran under. provider_limit_observations_from_codex_press_receipt
derives the identity from it and gives the caller nowhere to supply one.

CODEX_HOME is the identity because it IS the isolation mechanism
(codex_state_root_isolation_note): two homes are two credential stores.

codex_press_credential_identity takes the AmbientCodexCredentialContext
rather than the whole subject — it depends on exactly what it needs, and
that is also what makes it witnessable without a four-field fixture.
Witnessed: two homes never collide, and the label is the home.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* JWKS: model the key set and the verification seam, which cannot yet verify

The entitlement axis needs authenticated claims, and codex_auth's parse
boundary is explicit that reading unverified claims is fine for showing
which account a credential names and illegitimate for authorization.
Selection eligibility is authorization. So the missing piece is
signature verification, and this is its first half.

Cited as three documents rather than one local vocabulary: RFC 7517 for
the JWK and its members, RFC 7518 section 6.3.1 for the RSA parameters
n and e, section 3.3 for RS256. They are independently versioned and
independently extended.

WHAT IT DELIBERATELY CANNOT DO. RSASSA-PKCS1-v1_5 is s^e mod n over a
2048-bit modulus plus a padding and digest check, and this corpus has no
bignum, no modular exponentiation and no RSA primitive. That is a
capability gap, not a modeling gap, and no amount of .dag closes it —
it needs a realization handler bound to a host primitive, or bignum
grounded in the substrate. So there is no fn here returning Bool for
signature validity: such a function could only fabricate, and a
`verified` that is always true because nothing checked it is the
fabricated-plausible-output failure aimed at a security decision.
jws_verification_unavailable is what every caller gets, naming the
missing capability, counted rather than absorbed.

n and e stay base64url strings for the same reason: decoding them needs
the arithmetic the module does not have, so an Int carrier would claim
a capability it lacks.

Key selection is 0/1/many. RFC 7517 does not require kid uniqueness, so
duplicates are a real wire state, and first-wins would decide which key
authenticates a credential on list order. Absent and ambiguous stay
distinct because refreshing the key set fixes one and not the other.

An unmodeled key type decodes without material rather than failing the
set — the key we need may be beside it.

Recorded for whoever binds the handler: it must check the JWS header alg
against what the key permits rather than trusting the header, because
substituting none or an HMAC alg against an RSA public key is the
standard JWS confusion attack. Dissolve-on: a bound handler with an
executed positive control and a discriminating negative control.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* JWS: refuse the algorithm-confusion attacks, which need no bignum

The header does not get to choose the algorithm. Both historical JWS
confusion attacks work because the verifier read `alg` out of the token
and did what the token asked: `none` strips the signature entirely, and
HS256 against an RSA verifier signs with the PUBLIC key as an HMAC
secret — public being the point. The defense is not a stronger
primitive, it is deciding agreement from the KEY and only ever checking
the header's claim against it.

That is structure, not arithmetic, so it lands now rather than waiting
for the exponentiation. Witnessed as attacks: an unsigned token refused
against every key type, and HS256 against an RSA key as a mismatch.

SCOPED DOWN MID-WRITE, and the reason is recorded on the carrier. I
first modelled the compact split and header decode too, then found that
string_split, drop, code_point_string and uint8_int do not exist — I had
written four fabricated primitives, the same class as two earlier slips
this session. The corpus consumes text by positional char folds
(extdeps.auth.jwt jwt_first_colon_from is the idiom), so the split needs
that fold written properly, and writing it badly means a verifier
reading the wrong bytes.

So the split, the segment-count check that refuses an unsecured JWS and
a JWE, and the signing input are deferred with their dissolve-on named,
rather than shipped as plausible code over primitives that do not exist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Partition tick sessions by the coproduct, not a parallel string vocabulary

Review artifact 51309 called this a follow-up modeling cleanup rather
than a correctness gap, and that is right, but it is the nicknaming
class so it is worth closing.

belt_tick_session_is(s, arm: "running") compared string arms — a second
naming scheme for BeltSessionLiveness, which already names exactly those
states. The string version is strictly worse than the coproduct: a typo
in an arm name compiles and silently empties a bucket, where a missing
coproduct arm is a non-exhaustive match the compiler catches.

belt_tick_session_class now returns BeltSessionLiveness and the four
predicates match its arms. Node-awareness stays at this layer rather
than moving into belt_session_liveness_from_process, because
multiplicity is a fact about a node's ROWS while that function sees one
row's process — the same split belt_node_session_liveness makes on the
dispatch path.

Landed on the stacked PR rather than #8132, which has three approvals
sitting on a head whose CI run is finally in progress; restarting it for
a cleanup the reviewer explicitly called non-blocking would trade a
verified merge candidate for a tidier one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* JWS: land the compact split, so the segment count is checked

The deferred half of the JWS structure. RFC 7515 section 7.1 is exactly
three base64url segments joined by two periods, and the COUNT is a
security fact rather than a parsing detail: two segments with an empty
third is the unsecured JWS of section 6, five is a JWE. Verifiers that
split on periods and read position two have accepted both as signed
tokens, so each wrong count refuses by its own name.

The signing input is carried on the split rather than rebuilt later,
because RFC 7515 section 5.2 step 8 covers the ENCODED segments joined
by a period, and reconstructing that from decoded parts is where a
re-encoding difference silently produces different bytes than the ones
the signature covers.

THE SEGMENTER IS LOCAL AND THAT IS A COMPROMISE, stated on the carrier
rather than hidden. Splitting on a separator is not a JWS concept and
belongs in std, but no general split, index-of or substring exists in
this corpus, and minting a std primitive is not a decision to take
inside a JWS module. jws_segments is scoped and named for its subject.
Dissolve-on: a general std separator fold lands and this becomes a call
to it; a second consumer needing the same scan first is the signal to
promote rather than copy.

This closes the fabricated-primitive gap from the earlier commit — the
scan is written against char_at / code_point / string_length, the
idiom extdeps.auth.jwt already uses, rather than the string_split and
drop I had invented.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Remove JWS/JWKS from the critical path; delete the rebindable evidence entry

TWO CORRECTIONS, THE FIRST OF WHICH IS MINE TO OWN.

JWT/JWKS IS NOT THE ENTITLEMENT JOIN. I built a signature-verification
lane on the premise that entitlement required authenticated id_token
claims, citing codex_auth's parse boundary. That note is correct and
governs the auth.json id_token — a different surface. The app-server
account/read response already carries the account realm, and the proof
was a captured wire specimen in this repository's own test tree:

  {"result":{"account":{"type":"chatgpt","planType":"pro",...}}}

That is the admitted Codex process answering an authenticated control
request, not gunbc decoding an untrusted bearer token, so the prohibition
never applied. I grepped for entitlement producers and for planType in
the extdeps models; I did not read the response fixture, which settles
it in one line.

The lane is preserved on branch jwks-security-substrate rather than
deleted — it is cited and correct as far as it goes — but it has no
production consumer, cannot authenticate any constructible value, and
brings this branch no closer to a provider turn. Keeping it here would
make entitlement depend on a capability that does not exist.

THE CREDENTIAL FIX WAS INCOMPLETE. Adding the receipt-taking entry point
did not remove the forgery: provider_limit_observations_from_codex_press_evidence
still took credential and evidence as independent arguments, so evidence
observed under one CODEX_HOME could still be published as another
account's standing. A safe path added beside an unsafe path leaves the
unsafe path. Deleted — the only production entry consumes the complete
receipt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Fix the stale CapacityUnknown arm assertion left behind by the mixed-decode repair

witness_capacity_unknown_is_not_capacity asserted LimitObservationUnobserved,
the subject-less arm the bridge returned BEFORE review 51237. That repair moved
CapacityUnknown onto LimitSubjectUnobserved so an undecoded bucket refuses its
own draw without refusing a measured sibling; the witness was not re-read after
the producer changed, so it reported the repair as a regression.

The assertion is now stronger than a swapped label: it matches the subject and
pins native_key, which is the fact the repair added and the whole reason the
subject-carrying arm exists. The CI red is the discriminating receipt — the old
arm fails against the current producer, the new one passes.

Also merges main (#8182, #8180, #8164).

* Decode the Codex account union and project entitlement from what it reports

account/read's `account` member was decoded to "the object was nonempty" and
stopped there, so an apiKey platform credential, an amazonBedrock contract and
a personal ChatGPT subscription all reached the same Ready. Those are three
different credential realizations, and a profile requiring personal ChatGPT
OAuth is satisfied by exactly one of them.

Grounded in the upstream schema rather than in our captured fixture: read from
`codex app-server generate-json-schema --out` against @openai/codex 0.147.0,
v2/GetAccountResponse.json. Two facts from it changed the design:

  Account is a CLOSED oneOf discriminated on `type` — apiKey | chatgpt |
  amazonBedrock — so the realm is an enumerable decode, not an open string.

  PlanType is a CLOSED 15-value enum whose members INCLUDE `unknown`. I was
  about to treat any nonempty planType as entitling, which would have read
  upstream's own I-do-not-know as a yes. It decodes cleanly and entitles
  nothing.

The two negatives stay distinct because their remedies differ. A Bedrock
account is REJECTED — read successfully, wrong realization, permanent until the
credential changes. A plan or type outside the cited enum is UNOBSERVED — our
pin is stale, repaired by editing a roster. Both refuse dispatch today, which
is exactly why a witness asserting only "not available" would pass while the
model conflated them.

Entitlement policy lives at the bridge, not in the decoder: the press says what
upstream reported, gunbc.codex_plan_is_entitling says which plans we draw on.
OpenAI publishes no machine-readable plan→entitlement map, so the roster is a
declared policy budget with `free` and `unknown` excluded by name, not a
fabricated citation.

Six witnesses, green by execution; mutating the roster's CodexPlanUnknown arm
reds witness_upstream_unknown_plan_is_not_a_yes and leaves its sibling green.
Downstream consumers re-run at full roster count: press 22/22, bridge 21/21,
dispatch_selection 19/19, roadmap_dispatch_actuator 36/36.

* Give the exhausted-bucket arm a typed cause and a typed reset

LimitUnavailableUntilReset carried a required native_token and no reset, which
produced two defects from one shape:

  The cause was `match reached_type { Present{t} => t  Absent => limit_id }`.
  A bucket that reported exhaustion without naming a cause got the bucket's own
  id in the cause slot, so a reader could not tell an upstream-reported
  rate_limit_reached from our substitution. The substitution was not chosen —
  it was the only expression that typechecked, because a required field whose
  producer lacks a value MANUFACTURES a fallback.

  resets_at decoded off the wire and was dropped, so an arm named UntilReset
  could not say until when.

Now ProviderNativeLimitCause (Reported{token} | Unreported) and
ProviderLimitReset (ResetAt{at} | ResetInstantUnobserved). Both absent arms are
reachable: rateLimitReachedType and resetsAt are optional upstream. An
unobserved reset is not an immediate one — zero is the BEST possible reset and
therefore the worst available default.

THE WITNESS WAS PINNING THE DEFECT. witness_codex_bucket_projects_to_native_
bucket_never_account_scope asserted `t == "codex"` against a fixture declaring
reached_type: none — so the value it held in place was the fallback itself. It
now asserts NativeCauseUnreported, and a second fixture carrying both a cause
and a reset proves the two project differently.

Scope, declared: LimitWarning/Rejected/Exhausted/RateLimited keep bare tokens.
They have one producer between them and no observed case of a missing token, so
converting them would add an unreachable state. Trigger recorded on the arm.

The superseded note is replaced rather than left beside the fix — it had named
this exact change as its dissolve-on.

Full-roster reruns: bridge 23/23, provider_standing 17/17, standing_selection
7/7, dispatch_selection 19/19, roadmap_dispatch_actuator 36/36, press 22/22.

* Make the account-realm matches exhaustive instead of rostering new residue

The non-fold residue ratchet found one unrostered site (stale count zero): my
realm decoder matched JsonNull and JsonObject and swept the rest into `_`, and
realm_from_account_read_frame did the same over the JsonMember arms.

Rostering it was the wrong repair. A roster row carries a reason AND a
dissolution trigger, so it is a net-new active dissolution obligation, and
DESIGN §5 is explicit that a trigger is a lifecycle fact rather than permission
— absent an operator verdict on this exact population, marked debt is refused
exactly like unmarked debt. The obligation also would not have been true: there
was nothing to dissolve to, because the exhaustive form was available now.

Both matches now name every variant. JsonValue's four non-object non-null arms
route through one named function rather than four copies of the same record, so
exhaustiveness costs nothing in duplication.

Verified: unrostered and stale counts both 0, the ratchet witness passes, and
its two RED controls still hold. Bridge 23/23, press 22/22.

* Cite the account schema from the pinned runtime, and narrow the entitling roster

Two P0 corrections from analyst review, both real and both mine.

P0-1 — the branch modeled a protocol no executable implements. I read the
account schema from the codex that happened to be installed on this build host,
0.147.0, while every other fact in the module and the materialized bundle the
turn executes are pinned at 0.146.0. I noticed the version gap while working
and declared the new number in my notes instead of resolving the mixture.

Measured against both executables rather than assumed: the Account union is
structurally identical, but PlanType is 13 values at 0.146.0 and 15 at 0.147.0
— self_serve_business_prolite and enterprise_cbp_automation do not exist for
the pinned runtime. So the model named two plans our executable cannot emit.
The identical union is exactly why it was easy to miss: the part that changed
was the part nobody diffed.

Restated to 0.146.0, the version the runtime receipt admits. The two later
strings now decode as CodexPlanTypeUnrecognized — a stale pin, which is the
honest reading — and a witness asserts that rather than the note claiming it.
The rule is not which version wins: a protocol fact comes from the exact
executable the runtime admits, and a bump moves manifest, lock, artifacts,
receipt and every fact together.

P0-2 — the roster was a broad allow path from a locally authored assumption.
It admitted every plan but free and unknown, in a note that simultaneously
said no upstream plan-to-entitlement mapping exists. Narrowed to the single
plan actually observed on the deployed credential, behind an explicit
CodexPersonalSubscriptionPolicy so the set is unmistakably authored policy.

A non-admitted plan reads UNOBSERVED, not REJECTED, on the same remedy line the
module already draws: ratifying a plan is our-side like a stale pin, while an
apiKey or Bedrock account is repaired only by changing the credential.

Every previous assertion here was about a REFUSAL, which is why no witness
noticed the widening. witness_deployed_profile_admits_exactly_the_observed_plan
pins the admitted set at exactly one member, so a future admission reds first.

Bridge 26/26, press 22/22, provider_standing 17/17, standing_selection 7/7,
dispatch_selection 19/19, roadmap_dispatch_actuator 36/36, residue ratchet green
with both RED controls holding.

* PRESS-0 step 2: credential liveness split and HeldLease supervised turn

Split account/read realm from rate-limit liveness: authentication derives
from CodexCredentialLive (buckets returned) via the press receipt bridge,
not from account/read alone — the continuity-receipt trap where chatgpt/pro
coexists with a dead token.

Model supervised execution on std.temporal_effect HeldLease in
gunbc.codex_supervised_turn: turn/start admission refuses idle threads
without a runner-written terminal receipt, and witnesses pin the trap.

Wet srv1 shell-typed-invocation turn remains open on this vertical.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix parse error in codex_supervised_turn witness test.

Leading == on a continuation line is invalid syntax; bind the policy
comparison via let so the file parses and corpus discovery can run.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Pin unobserved credential liveness in witness; mutation-checked traps.

Extend witness_credential_liveness_splits_from_realm to assert
CodexCredentialLivenessUnobserved maps to AuthenticationStandingUnobserved,
not AuthenticationAuthenticated. Mutation checks confirm both traps red:
idle-thread admission and unobserved-liveness authentication.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix compile-clean: remove unattached // prose from witness file.

Standalone leading comment blocks lacked module-item subjects and
failed dag_compile_clean_gate (64 annotation refusals). Rationale
already lives in typed data witness_note rows per DESIGN 4c.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Revert "Merge annotation-grain fix from #8215 to clear inherited compile-clean red."

This reverts commit 50e02c3, reversing
changes made to 1c97fc0.

* Add supervised Codex turn runner for bounded wet execution.

Implements run_codex_supervised_turn with HeldLease fencing, bundled-native
app-server script (thread/start then turn/start), stdout parse, and
runner-written terminal receipt with client_command_id; cites srv1 execution
receipt for supervised-lease selection over detached reconciliation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix non-fold residue in serialize_supervised_rpc_error.

Replace JsonValue wildcard arm with exhaustive match over all six
variants so non_fold_residue_no_unrostered_or_stale passes without a
roster row.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add wet supervised-turn witness and srv1 handoff entry.

Runner was already realized; this wires a falsifier-rehomed wet witness
that materializes the bundled native, preflights via rateLimits, runs
run_codex_supervised_turn, and asserts terminal receipt + client_command_id
without belt dispatch.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix wet supervised-turn witness parse errors.

Match-arm bodies need braces; split client_command_id check into let
bindings so multi-line == does not follow && inside a match arm.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix wet witness parse: trailing == on continuation lines.

Parser rejects continuation lines that START with == but accepts ||
(measured on main binary). Move == to end of prior line in wet_supervised_turn_terminal_ok;
record defect in std.language_target_identity continuation_eq_operator_shape_note
with hermetic witness repro controls.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Record srv1 wet OOM receipt and add bisect witnesses.

Full supervised wet witness killed at ~89 GiB with no child processes;
move file to OfflineLocalRecipe, remove falsifier roster row, add bisect
fns with declared MemoryMax envelopes for operator-bounded srv1 runs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix wet bisect witness: bind registry_artifacts in match arm.

Pattern used registry_artifacts: _ then referenced the name in the body,
which fails compile before bisect step 1 can run.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Isolate materialize leak: prepare split + lookup fold fix.

Bisect confirmed leak in package_delivery.materialize_codex_runtime_bundle on main. Split prepare_codex_runtime_closure for narrower wet bisect; eliminate filter allocation in package_lock_lookup_node; record production defect note. No wet re-run until capped confirmation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add wet bisect step 2b: select_codex_exact_target_closure only.

Isolates closure selection and codex_wrapper_lookup / codex_target_package_lookup from materialize shell path for capped srv1 run at MemoryMax=2G.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Drop wet bisect from witness enrollment until materialize is fixed.

Convert bisect entrypoints from test fn to plain fn so Phase 0(b) admission does not count five UnclassifiedPathDeferral rows with zero consumers. Remove OfflineLocalRecipe exclusion row; file stays as operator-only reproduction via gunbc run.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add wet bisect step 2c: wrapper tarball acquire only.

Isolates acquire_and_admit_registry_artifact for the wrapper after closure prepare — DownloadToFile plus sha512 verify, no platform pair or npm path.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add pre-shell materialize bisect entries after instrumented trace.

Split source construct, prepare, and through_prepare from full materialize body eval; remove pre-mktemp from materialize wet witness so bisect has zero shell before materialize.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix wet bisect parse: data decl value must be same line as '='.

Continuation-operator fix (#8259) does not cover declaration '=' breaks; corpus has zero split-line data rows.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add wet bisect rung: platform artifact acquire only.

Ladder through through-prepare passes at 72f6ead; full materialize pins 8G — next isolate native platform tarball acquire.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add final materialize bisect seams: pair, npm ci, finish.

Extract codex_runtime_install_from_prepared for install-through-verify; add through_pair_acquire, through_npm_ci_offline, and finish_after_install bisect entrypoints.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add finish-stage observe bisect rungs for materialize leak (#8166).

One wet entrypoint per observe inside finish_codex_runtime_materialization_after_install plus a two-file tiny-tree fixture for observe_package_materialized_tree isolation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Remove duplicate npm import after main merge.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add single-file protocol schema interpreter bisect rung.

wet_finish_parse_canonicalize_largest_protocol_schema_only installs Codex,
generates the schema dir, then parse_json + canonicalize_json_value on only
codex_app_server_protocol.schemas.json — no fold, manifest, serialize, or sha512.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Restore package_delivery.dag after rebase conflict resolution.

Auto-resolving rebase conflicts with main dropped PR materialize/bisect
changes; restore the full #8166 package_delivery surface from pre-rebase tip.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Take main's rack_power.dag to fix annotation grain CI red.

Branch revert 45f81ae re-introduced body-grain // comments that main had
already hoisted to module-item grain above PowerRefusal and rack_power_budget.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Take main's spatial_projection and planar_projection annotation fixes.

Complete the 45f81ae revert undo: hoist // comments to module-item grain in
the two remaining files that rack_power fix alone did not cover.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Drop false continuation_eq note; update accounting note for #8259.

#8259 dissolved operator-leading continuation asymmetry for == and +.
Remove the PR-only continuation_eq_operator_shape_note, restore main's
continuation witness at src/v1/tests/claim/, and rewrite
accounting_arithmetic_shape_note as a receipt rather than stale parser advice.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add parse-only rung for protocol schema JSON memory split. (#8272)

Confirmed on srv1 under MemoryMax=8G: parse-only and parse+canonicalize
both pin 8 GiB on codex_app_server_protocol.schemas.json (506,984 bytes,
~16,500x input). Defect is parse_json / JsonValue construction.

Rebased onto press-0-step2 (8990f2a).

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Brian Searls <11205878+briansrls@users.noreply.github.com>

* Address supervised-turn correctness floor for #8166.

Keep stdin open through turn terminal, supervise process groups, persist
client_command_id before turn/start, refresh in-session standing, and
use generation-scoped lease binding; witnesses cover exit-0-not-observed.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix supervised turn JSON escaping and remove Python thread-id fork (#8166).

Route turn/start through codex_app_server_turn_start_request_line (serialize_json), delete the embedded Python extractor, add typed supervised_thread_id_from_line for post-hoc decode, and mark the concat-built fifo coprocess as declared scaffold with dissolution toward host_effect_apply Phase 2.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Document thread-id sed substitution assumption for supervised turn (#8166).

Record that in-shell sed into the turn/start JSON line is interim and charset-unbound until protocol schema or typed transport lands.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Delete supervised turn bash coprocess; wire typed host_effect transport (#8166).

Operator RejectAndFinishNow: remove codex_supervised_turn_script (36 concat), retained_transport, and sed thread-id substitution. Add extdeps.llm.codex_app_server_stdio_session intent, CodexSupervisedWorkerTurn host effect, and gunbc.codex_supervised_turn_run routing through host_effect_apply; realize refuses until native session driver lands.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Land Codex app-server stdio session driver for supervised turns (#8166).

Native coprocess driver (pipe stdin, setsid, poll-to-terminal, process-group kill) ships as target/release/codex_app_server_stdio_session and is invoked through gunbc.host_effect_codex_supervised_turn via host_effect_apply CodexSupervisedWorkerTurn. turn/start uses JSON parse+re-serialize (no sed). ShellEffectApplied carries stderr for capture wiring.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Register codex_app_server_stdio_session in self-host frontier for regen.

Seed-retained intrinsic registration drives stage0_crate_layout_generated
and emitted gunbc_stage0_crate_layout_generated so lib.rs and fresh self-compile
agree on pub mod codex_app_server_stdio_session. Includes workflow regen drift
from generated_artifact_gate main_wet.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Enroll codex_app_server_stdio_session in extdeps scope frontier.

Declare ExternalModelScope for the Codex app-server stdio session transport
(citing github.com/openai/codex) and add the module to scope_carrier_paths
so extdeps_scope_placement_gate admits the new extdeps file.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add CodexSupervisedWorkerTurn arm to converge_cli_applied_knob_count.

Fleet converge CLI does not count supervised worker turns as applied knobs;
return fallback like neighboring non-ConvergePlan HostEffect variants.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fail closed when Codex session preamble file cannot be read.

Return exit 1 before spawning the coprocess instead of substituting
an empty preamble (review 52819 / composer-2.5 REQUEST_CHANGES).

Co-authored-by: Cursor <cursoragent@cursor.com>

* Refuse invalid thread/turn-start-id argv instead of silent defaults.

When --thread-start-id or --turn-start-id is present, a missing value or
non-integer parse fails with exit 2 before spawn — same idiom as other
required argv and the preamble read fix (review 52819 second instance).
Omitted flags still default to 4 and 5.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix generation-store conflation for supervised turn exactly-once fence.

Replace Int? collapse with CodexSupervisedGenerationRead coproduct, route store failures through codex_supervised_turn_generation_admission into turn-start refusals, and refuse when generation fence write fails.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix stdio session driver RPC id matching to exact JSON parse.

Replace substring "id":{rpc_id} pre-filter with parsed id comparison so id 41 cannot satisfy rpc_id 4; aligns seed driver with codex_supervised_turn.dag exact-id matching.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Remove frontier_probe_survey from release bin pack after roster deletion.

Main deleted the bin with the self-host frontier roster; merge incorrectly re-enlisted it. Regenerate CI workflows via main_wet.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Regen compiler_tests.rs after #8344 frontier roster deletion.

Fresh seed rebuild + regen_stage0 --verify (divergence 0); removes stale Witness coercion/is_copy assertions dropped when frontier probe witnesses were deleted on main.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: gunbai-bot[bot] <289086189+gunbai-bot[bot]@users.noreply.github.com>
Co-authored-by: Brian Searls <11205878+briansrls@users.noreply.github.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