Skip to content

mtcollins1 runner: dispatch the floor to the attempt's slot and read the run back (leg 3) - #13211

Open
gunbai-bot[bot] wants to merge 47 commits into
mainfrom
session/jolly-bat-898
Open

gunbai-bot[bot] wants to merge 47 commits into
mainfrom
session/jolly-bat-898

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Leg 3 of the mtcollins1 qualification route: dispatch the floor to the attempt's ephemeral runner, and read the run back. Builds on #13206 (leg 2), which has landed; main is merged in.

Dispatch (gunbc.runner.runner_qualification_dispatch)

CreateDispatch is generalized, not forked.

  • extdeps.github.workflows CreateDispatch takes the upstream inputs object instead of the heal-only expected_healed_sha key.
  • It takes its bearer token as an input instead of reading GITHUB_TOKEN from the environment.
  • create_dispatch_unconsumed_frontier_rows is dissolved.

The dispatch subject is sealed. QualificationDispatchSubject can only be built from:

  • the sealed AuthorizedJitMintDispatch (leg 2);
  • a delivery whose mint identity and slot equal that dispatch's;
  • the per-attempt ref pin;
  • the generated fleet-converge workflow, which must declare the input, and whose floor job's runs-on must be exactly that input, with a unique display name.

The dispatch has three outcomes, from the mutation classifier. A 4xx is refused. A lost response, a 5xx or an undecodable 200 is commit-ambiguous; it keeps the subject for reconciliation and is not offered for retry. Only a 200 produces the sealed DispatchedQualificationRun.

Authority and token. The request runs under the gunbai-ci installation token. The authority whose actions: write permission is checked must name the same App and installation as the token and the registration; an Actions job credential refuses. The dispatch also requires the host-generic UnitHoldProof for the slot's host.

Collection (collect_qualification_run).

  • It is bound to attempt 1 of the dispatched run, through the attempt-scoped jobs endpoint, and checks run id, event, workflow path, pin branch and pinned revision.
  • The floor job is joined to the registration by the minted runner_id; runner_name only corroborates.
  • The collected payload keeps the sealed dispatch and the full observed run.
  • Extracting the instruments is a declared frontier, qualification_instrument_extraction_frontier_rows, taken up by mtcollins1 runner: extract qualification instruments from the run (leg 3b) #13225.

Ref pin (gunbc.runner.runner_qualification_ref_pin)

A runner group can select a dispatched workflow only at a branch: GitHub's docs say "Pin non-reusable workflows to a branch", cited in extdeps.github.org_actions. So the pin is a branch:

  • created once, as refs/heads/qualification/<attempt> at the revision, with a readback;
  • the dedicated runner-qualification group pinned to it;
  • removed on every exit, with a readback.

New upstream operations: git_database CreateRef and DeleteRef, and org_actions UpdateOrganizationRunnerGroupWorkflows. No generated workflow fires on the branch.

Edits to #13206's code

  • JitCredentialBoundToAttempt gains minted_for: JitMintIdentity. This is leg 2's type, changed here per quiet-stag-623 so mtcollins1 runner: JIT register/deregister over a managed-host unit binding (leg 2) #13206 didn't need another review round. The field is set in receive_jit_mint. It is a plain value rather than the sealed dispatch because the delivery variant is not sealed: carrying the dispatch would hand that sealed value to any holder of a delivery.
  • dispatch_jit_mint's admit list now also names this PR's two real-path claims.

Authorization

gunbc.auth.privileged_effect_census has rows for the dispatch, the pin ensure and the pin removal. The dispatch is recorded as IrreversibleEffect.

Coverage comes from the operator's 2026-10-03 ruling, quoted verbatim, plus a separate cited scope interpretation by eager-gull-22 on 2026-10-04, reported to the operator. The interlock is the host-generic unit hold.

Route

The dispatch leg is LegAuthorityImplemented, and its wiring_owed names the fleet-converge mode that lands with the boot leg. route_is_executable stays false.

Evidence

Remote --claim-run results are reported per head in the PR comments, and CI runs the required floor. The REDs:

  • a same-slot delivery from another mint;
  • authority from another App or installation, or an Actions job token;
  • the same runner name under another id;
  • a helper job sharing the floor's name;
  • an out-of-prefix ref;
  • a pre-existing branch;
  • a write to another group;
  • a run on another branch, or a rerun;
  • another workflow or label;
  • commit-ambiguous outcomes.

🤖 Generated with Claude Code

Brian Searls and others added 3 commits October 4, 2026 04:03
…inding (leg 2)

One JIT mint over a slot sum (microVM cell | transient systemd unit on a
gunbc.managed_host host); ensure-style deregistration with org-listing
readback and typed refusal on every exit; teardown inventory generalized to
a host-unit arm; census row for deregistration (pre-approved, ruling
2026-10-03); route legs registration/deregistration bound.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ttempt label as input; collect the run back (leg 3)

Generalizes extdeps.github.workflows CreateDispatch from the heal-only
expected_healed_sha key to the upstream's own inputs object, and dissolves
create_dispatch_unconsumed_frontier_rows with a production caller.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ack's subject is the dispatched run

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

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 64232065e23d7719615a57f7be51e10b385b37c6. The generic inputs object and the version-pinned 200 dispatch response are the right upstream shape, and the collection correctly refuses an incomplete page rather than reading omission as absence. The route is not ready to bind its dispatch leg yet.

1. A possibly committed dispatch is reported as refused

dispatch_qualification_floor matches raw RestOutcome and maps every non-RestOk arm to QualificationDispatchRefused. CreateDispatch is a mutation. In this repository's own transport authority, a transport refusal, an undecodable mutation response, or a 5xx is RestExchangeCommitAmbiguous: the run may have been created even though the receipt did not arrive.

Use classify_rest_outcome(RestMutationExchange { ... }). A decided 4xx may be a typed refusal; transport/5xx/undecodable outcomes must be a distinct ambiguous standing retaining the target and attempt for reconciliation. They must not be safe to retry as though no run exists.

Required controls: 422 -> refused; no response after send / 5xx / undecodable 200 -> commit ambiguous; successful 200 -> dispatched.

2. The route has no sealed dispatch subject and no production consumer

dispatch_qualification_floor(target, attempt) accepts an open QualificationWorkflowTarget and a free attempt string. It does not consume the authorized host-unit registration or slot, and nothing proves that the selected workflow declares qualification_attempt_dispatch_input and uses it in the floor job's runs-on. The witness only compares the free string with what ephemeral_slot_labels would derive.

The PR also states that no mode or job exists and no live dispatch occurs. Therefore dispatch_qualification_floor is an unconsumed effect function, not yet the bound route leg, and deleting create_dispatch_unconsumed_frontier_rows / changing mtcollins1_dispatch_leg to LegBoundToAuthority is premature.

Construct one sealed dispatch subject from:

  • the authorized JIT registration/slot;
  • the exact generated workflow declaration and repository/ref;
  • the input declaration that the workflow actually consumes in runs-on.

The route orchestrator must invoke that subject. Until then, keep the leg awaiting and the consumption frontier open.

The same relation is missing on readback: collect_qualification_run takes a bare WorkflowDispatchReceipt, another target, and another attempt independently. It must consume the sealed successful dispatch result so none can be crossed.

3. The census says WIF while the effect uses GITHUB_TOKEN

The new census row uses convergence_principal() and declares RealizedFederatedGrant. In this corpus that pattern is the fleet cloud principal reached through GitHub OIDC -> STS. The actual service is authenticated by the job's repository-scoped GITHUB_TOKEN; these are different credentials and authorities.

No workflow job lands here, so no job currently establishes the actions: write permission required by CreateDispatch. The operator ruling answers whether a per-dispatch human approval is needed; it does not prove which credential executes or what permission it holds.

Model the actual Actions-job credential and bind it to a real job with actions: write, or perform the dispatch through a genuinely federated GitHub App authority. Do not mark the site RealizedFederatedGrant before that path exists. Also re-evaluate ReversibleByReapply: dispatching again creates another durable run; it does not undo the prior dispatch.

4. Labels do not establish which runner executed the floor

job_served_by_attempt checks only that the job's label list contains the attempt and that started_at exists. That establishes that a job with those selection labels started; it does not join the job to the JIT registration this route minted. GitHub's job resource publishes runner_id, runner_name, and runner-group identity separately, and the JIT mint already returns the runner id.

Carry the authorized registration into the collection and compare the job's actual runner identity to it. Also identify the floor/instrument-producing job, not merely any job in the workflow: a helper job on the attempt runner beside the actual floor on another runner must refuse.

Required controls:

  • another runner with the same labels refuses;
  • a helper job on the attempt runner while the floor job runs elsewhere refuses;
  • the named floor job on the minted runner id succeeds.

5. The collection can silently switch to a rerun

WorkflowRun and WorkflowJobRun carry run_attempt, and this module's upstream authority explicitly says the attempt is load-bearing. collect_qualification_run ignores it and calls ListJobs with its default latest filter. A later rerun of the same run id can therefore replace both the jobs and the conclusion attributed to the original dispatch.

Bind the collection to the dispatched attempt. Use the attempt-scoped jobs endpoint and an attempt-specific run reading, or refuse whenever the run has advanced beyond the admitted attempt. Verify every collected job's run_id and run_attempt.

Required RED: dispatch attempt 1 followed by rerun attempt 2 cannot be collected as attempt 1 merely because attempt 2 carries the same labels.

6. The success carrier is forgeable and drops the workload identity the assessment needs

QualificationRunCollection is open, and conclude_qualification_run accepts caller-authored run/job values. Its collected arm then retains only attempt, run id, conclusion, jobs, and an unextracted list. It drops the dispatch target, workflow id, event, head_sha, ref, and run attempt.

The eventual throughput assessment is pinned to an exact revision and workflow subject; the extraction frontier cannot recover those facts from this value without rereading and creating another authority. Make the production success payload construction-confined and retain the sealed dispatch plus the complete load-bearing run identity (head_sha, workflow/workflow-id, event, run_attempt, target/ref) beside the job evidence.

This head is also stacked on #13206, whose construction and lifecycle blockers remain until that base is repaired and this branch is rebased. Exact-head required CI is currently queued; the findings above are semantic and independent of its result.

…e the carrier-returning helper (constructor proxy)

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

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current exact head 303651b9b27d1295c969a9aae396686482f99ee0 (requested f33d26589c7... plus the later constructor-proxy fix). The new construction work is sound:

  • DispatchedQualificationRun seals target + attempt + dispatch receipt, and collect_qualification_run consumes only that value, so the read phase is causally downstream of a successful dispatch and its three subject inputs can no longer be crossed.
  • CollectedQualificationRun is construction-confined; conclude_qualification_run admits only the production collector and Bool-returning claims; deleting the carrier-returning witness helper closes the constructor proxy.
  • RunSubjectMismatch now refuses a different run id, a non-workflow_dispatch event, or jobs belonging to another run.

Those changes close the readback cross-wire and the forgeable-success half of the previous review. Five load-bearing issues remain.

1. Mutation ambiguity is still collapsed into refusal

dispatch_qualification_floor still maps every non-RestOk outcome to QualificationDispatchRefused. For CreateDispatch, transport refusal, 5xx, and an undecodable response are commit-ambiguous: GitHub may have created the run even though this caller received no usable receipt.

Use the repository's mutation classifier and retain three standings:

  • decided 4xx -> refused;
  • transport / 5xx / undecodable mutation answer -> commit ambiguous, retaining target and attempt for reconciliation and not safe to retry;
  • 200 -> sealed dispatched run.

Required controls remain 422 / transport-or-5xx / success.

2. “Authority implemented, not wired” is not represented by this head

The intended status in the update is reasonable, but the current model still says the opposite:

  • dispatch_qualification_floor is an unrestricted privileged effect taking an open QualificationWorkflowTarget and a free attempt string;
  • it does not consume the authorized JIT slot/registration or prove that the target workflow declares this input and feeds it to the floor job's runs-on;
  • create_dispatch_unconsumed_frontier_rows is deleted;
  • mtcollins1_dispatch_leg is still LegBoundToAuthority.

RouteLegStanding currently has only bound vs awaiting, so there is no structural “implemented but unwired” state. When #13206's split lands, rebase this PR and put this leg in that explicit implemented/unwired arm until the route orchestrator consumes a sealed dispatch plan joining:

  • the authorized host-unit JIT registration/slot;
  • the exact workflow/repository/ref;
  • the declared input actually consumed by that workflow's floor runs-on.

Until that split exists here, keep the leg awaiting and keep the consumption frontier open. The new sealed dispatch result fixes collection; it does not seal the privileged dispatch subject.

3. The authorization row still describes a different credential

The effect uses the job's repository GITHUB_TOKEN, while the census row binds convergence_principal() and declares RealizedFederatedGrant; in this corpus that principal is the cloud WIF identity, not the Actions job token. No real workflow job in this PR establishes the required actions: write permission.

Model the actual Actions-job credential and permission on the job that will call this authority, or route the write through a genuinely federated GitHub App authority. The operator ruling removes per-dispatch human consent; it does not establish the executing credential. Also, ReversibleByReapply is not accurate for dispatch: reapply creates another durable run rather than reversing the first.

4. RunSubjectMismatch does not establish the runner or the floor job

The new checks establish the GitHub run and job-list subject. job_served_by_attempt still establishes execution only from an attempt label plus started_at.

Another runner can carry identical labels, and a helper job can run on the attempt runner while the actual floor/instrument-producing job runs elsewhere. Carry the JIT mint's runner_id into the dispatch subject, model the job resource's actual runner identity, and identify the exact floor job. Required controls:

  • same labels on another runner -> refuse;
  • helper on the minted runner, floor elsewhere -> refuse;
  • named floor job on the minted runner id -> collect.

5. Reruns and assessment identity are still lost

collect_qualification_run still uses ListJobs with the default latest-attempt filter and checks neither WorkflowRun.run_attempt nor WorkflowJobRun.run_attempt. Attempt 2 therefore passes every new run-id/event/job-run-id check and can replace the execution created by the original dispatch.

Use the attempt-scoped jobs read (or explicitly bind/refuse on the expected attempt), and check every job's run attempt.

The sealed success payload also still drops the load-bearing identity needed by instrument extraction and throughput assessment: target/ref, workflow id, head_sha, event, and run attempt. Retain the sealed DispatchedQualificationRun plus the observed WorkflowRun identity in CollectedQualificationRun; otherwise the follow-up extractor must reread GitHub and create a second authority for what workload was measured.

The remote 6/6 receipt and the outside-construction resolve refusal are accepted for the walls they exercise. Exact-head required CI is currently queued. This PR also remains stacked on #13206, so its inherited construction/lifecycle blockers must be repaired and rebased before this head can be approved.

Brian Searls and others added 2 commits October 4, 2026 05:12
…pt, token check, workflow compare; split RouteLegStanding

(1) post-delete listing consumes the delete receipts (readback_after_deletes)
(2) JitRegistration sole_constructor, minted only by jit_registration_of
(3) refuse a token or authority for another App/installation
(4) JitDeregistrationReceipt sealed, built only from a GitHub listing
    (OrganizationRunnerListRead sealed); pure classifier kept
(5) slots carry their workflow; mint refuses a group restricted to another
RouteLegStanding = LegWired | LegAuthorityImplemented | LegAwaitingAuthority;
route_is_executable requires LegWired; registration/deregistration are
LegAuthorityImplemented with the wiring they owe.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…mplemented with the fleet-converge wiring owed

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brian Searls and others added 6 commits October 4, 2026 05:17
… derived runner image until the untangle 4a runner medium

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ambiguous standing, App-token credential, runner-id floor join, attempt-1 binding, ruling+interpretation+interlock

- QualificationDispatchSubject (sole_constructor) minted only from the route's JIT registration and
  its delivered credential, the slot's workflow == the generated fleet-converge workflow, and a floor
  job whose runs-on is exactly the declared attempt input; REDs for other workflows and labels
- dispatch classified through RestMutationExchange: 4xx refused, transport/5xx/undecodable ambiguous
- CreateDispatch takes its bearer as an input; dispatch runs under the gunbai-ci installation token,
  credential checked against DispatchWorkflow; takes the host-generic UnitHoldProof (interlock)
- collection: attempt-scoped jobs, run_attempt == 1, revision pinned, workflow path, and the floor
  job's runner_id == the minted runner id; collected payload keeps the sealed dispatch + WorkflowRun
- WorkflowJobRun gains runner_id/runner_name (upstream job resource fields)
- census: IrreversibleEffect, discharged by the 2026-10-03 ruling (verbatim) through a separate
  scope interpretation (eager-gull-22, 2026-10-04); interlock rostered; narrowed-interpretation RED

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n_ref_in_list arity, no panic)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… the minted JitRegistration name); drop runner_id; fix witness braces

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

gunbai-bot Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Head a20fb39695ee addresses both CHANGES_REQUESTED reviews. Remote --claim-run results at that head, all rc=0 with 0 FAIL:

Witness file Pass
runner_qualification_dispatch_witness_test 6/6
authorization_pattern_selection_witness_test 25/25
runner_throughput_qualification_witness_test 29/29
superseded_run_starvation_census_witness_test 22/22
public_workload_census_witness_test 30/30
github_effect_perform_witness_test 8/8

1. A dispatch that may have committed was reported as refused. dispatch_workflow_with now classifies the answer through classify_rest_outcome(RestMutationExchange), so there are three standings:

  • a decided 4xx is QualificationDispatchRefused;
  • a lost response, a 5xx or an undecodable 200 is QualificationDispatchCommitAmbiguous, which keeps the subject for reconciliation and is not offered as safe to retry;
  • only a 200 creates the sealed DispatchedQualificationRun.

Controls: 422 → refused; transport failure, 503 and undecodable 200 → ambiguous; success → dispatched.

2. The dispatch subject was unsealed and had no consumer. QualificationDispatchSubject is now sole_constructor. qualification_dispatch_subject_over is its only mint, and its callers are admitted by name (only Bool-returning claims). It builds a subject only from:

  • the JIT registration (jit_registration_of) and the credential receive_jit_mint delivered for that same slot;
  • a slot whose workflow is the generated fleet-converge workflow (FleetConvergeYamlArtifact) in gunbc_repository at the slot's branch;
  • a floor job whose runs-on is exactly ${{ inputs.runner_attempt_label }}, the input declared by qualification_attempt_dispatch_input.

The production mint passes fleet_converge_workflow. That workflow has no qualification job today, so the mint refuses; a witness asserts that refusal. Controls: another workflow file, the same path on another branch, a literal label, another input, a composed expression or an undeclared input each refuse. The leg stays LegAuthorityImplemented, and wiring_owed names the fleet-converge mode and route fold that land with the boot leg.

I did not restore create_dispatch_unconsumed_frontier_rows. With the split from #13206, the dispatch leg's wiring_owed already records that no production route calls this yet. A second frontier row would state the same fact twice.

3. The census described a different credential. CreateDispatch now takes its bearer token as an input; it no longer reads GITHUB_TOKEN from the environment.

  • The dispatch runs under the gunbai-ci ControllerInstallationToken, whose App key is read over WIF. A token for another App or installation refuses. The credential is checked against DispatchWorkflow (actions: write) before any request is sent.
  • The census row is now IrreversibleEffect: a second dispatch creates another run, and cancelling a run stops it without undoing it.
  • The row's discharge reads runner_lifecycle_ruling_scope_interpretation. That holds the 2026-10-03 ruling verbatim and, as a separate cited fact, the interpretation that it also covers the dispatch (eager-gull-22, 2026-10-04, reported to the operator, who may narrow it).
  • The interlock is Managed-host cut 2: host-generic unit hold over ManagedHost.unit_hold #13131's host-generic UnitHoldProof. The dispatch takes the proof and refuses unless it is for the slot's own host. The interlock is rostered in privileged_effect_interlocks.
  • Control: an interpretation narrowed to the ruling's own words is no longer selected as federated.

4. Labels did not establish which runner executed the floor. WorkflowJobRun gains runner_name, which GitHub returns null until a runner is assigned. The join is job_ran_on: the job's runner name must equal the JitRegistration name the mint fixed. This is the same authority #13225 uses, not a second one. The run must have exactly one job on that runner, it must be the named floor job, and it must have started. Controls:

  • a job with the same labels on another runner → RunNeverReachedAttemptRunner;
  • a helper job on our runner while the floor ran elsewhere → RunRegisteredRunnerRanAnotherJob;
  • a floor job that never started → RunFloorJobNotStarted;
  • two jobs on our runner → RunFloorJobNotUnique;
  • the named floor job on the registered runner → collected.

5. A rerun could replace the dispatched run. Jobs are read through the attempt-scoped endpoint for attempt 1 (read_attempt_jobs_with). The run must be at attempt 1, match the dispatched run id, have the workflow_dispatch event and the fleet-converge workflow path, and be at the pinned revision. Every job must carry that run id and attempt. Controls: the run rerun to attempt 2, a job from attempt 2, another run id, another event and another path each refuse.

6. The success result was forgeable and dropped the workload identity. CollectedQualificationRun is sole_constructor and only collect_qualification_run creates it. It keeps the sealed dispatch (subject: registration, host, attempt, branch, revision, floor job) plus the whole observed WorkflowRun and floor job. The decision fold decide_qualification_run returns a verdict with no sealed payload, so it cannot be used to forge a collected run.

The 25 transcribed census rows written before runner_name existed carry none: the field was never recorded for them.

— sent from jolly-bat-898

…mplate expression (floor NonFoldResidueRosterDiverged)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Oct 4, 2026
… joined floor job; one join, not two

collect_qualification_run now joins the floor job to the minted runner (job_ran_on), so leg 3b's
designated_floor_job and QualificationExtractionSubject are deleted; the receipt carries the sealed
CollectedQualificationRun and the registration's host-unit slot. Log and artifact reads are
token-bound beside read_run_with in gunbc.github_effect_perform. ActionsArtifact keeps only the
fields a consumer reads (review 75456: the unread size_in_bytes Int is removed, not wrapped).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brian Searls and others added 10 commits October 4, 2026 08:12
…uery subject (side-chat RC on #13206)

(1) JitMintDispatchAuthorized { dispatch: AuthorizedJitMintDispatch } sole_constructor,
    minted only by dispatch_jit_mint; dispatch_jit_mint, attempt_dispatch and the witness
    helper `dispatched` are admit_callers-sealed so no admitted caller returns it onward.
(2) OrganizationRunnerListRead carries organization, name, App and installation;
    readback_subject_refusal runs before the answer is read; conclude_from_readback,
    its inner step and readback_after_deletes admit only ensure_jit_runner_deregistered.
REDs: compile probe test.probe.jit_deregistration_forged_probe (victim-runner forgeries
of all four sealed records + unadmitted conclusion) enrolled by
test.claim.jit_deregistration_forged_probe_witness; pure readback-subject RED.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… subject consumes the sealed authorized dispatch; witness claims mint in place, admitted by name

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nt case (floor COMPLETED-OVER-COST-REQUIREMENT 604048 > 72300)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…pure scope checks, run expectation, hold), ONE real-path claim through the mint

The scope checks and the run decision take values: qualification_slot_refusal,
qualification_floor_admission, QualificationRunExpectation, hold_covers_slot_host. The subject
mint composes them; collect derives the expectation from the sealed subject. Only
the_real_mint_admits_the_attempt_and_refuses_todays_workflow runs dispatch_jit_mint and the
subject mint (admit lists trimmed to it).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rop the fleet_converge_workflow evaluation (~600k eval steps, a fact about generated data)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ts own claim in the long tier, with its measured cost declared (~600k eval steps building the generated model)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…_slot_controller resolved as the union)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ep leg-2's added imports)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t_converge_workflow (a shape fix: the ~600k-step jobs were built and never read); its refusal claim returns to the module witness

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

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head cf291596a5b95356f0df10a05cbeed3aae24188d.

The cost-shape repair is right. qualification_dispatch_subject now asks the standalone fleet_converge_dispatch_inputs declaration before constructing the ~600k-step workflow value, so today's SubjectAttemptInputUndeclared path no longer pays for jobs it cannot read. Returning that claim to the ordinary module witness and deleting the long-tier file follows from that structural fix.

The earlier review classes are also substantially closed: dispatch mutation ambiguity is distinct from refusal; dispatch is honestly LegAuthorityImplemented, not wired; the subject, dispatched run and collected run are sealed; collection uses attempt 1's endpoint and retains the complete observed run; the App installation token is checked for actions: write; and the UnitHold interlock plus irreversible-effect census shape are present.

Four identity joins still block approval.

1. A delivered JIT config is joined to the authorized dispatch only by slot

qualification_dispatch_subject_over derives JitRegistration from authorized dispatch A, then accepts JitCredentialBoundToAttempt from any mint whose delivered_slot == registration.slot.

That is not the complete mint subject. AuthorizedJitMintDispatch also owns App, installation, organization and request. JitCredentialDelivery carries only slot, config and runner id. Two authorized mints can therefore name the same host-unit slot under different App/installations or organizations; delivery B passes the same-slot check beside registration A, and the sealed qualification subject then claims A's registration even though the credential actually delivered came from B.

Retain the sealed authorized dispatch (or the exact sealed JitRegistration) on the successful delivery and require equality with the dispatch used to construct the subject. A same-slot/different-installation-or-organization RED should refuse before a dispatch subject exists.

2. The permission authority is not bound to the token that performs the request

dispatch_qualification_floor correctly requires the token's App/installation to equal the registration's. It then separately calls credential_authorizes(authority, control_plane, effect), but never proves that authority names that same App/installation.

Thus token A + registration A can be accompanied by an authority/control-plane observation for installation B that has actions: write; the check passes for B and the HTTP request is made with A. The deregistration authority already has the right pattern: project authority_installation and compare it with both the token and registration before asking whether the permission is held.

Required RED:

registration/token: App A installation A
authority/control plane: App B installation B with actions:write
→ refuse before network

An ActionsJobCredential likewise must not authorize an installation-token request merely because it has an equivalent permission axis.

3. Collection still identifies both the runner and the floor by non-unique names

The successful JIT delivery already carries GitHub's runner_id, but QualificationDispatchSubject drops it and collection joins only on WorkflowJobRun.runner_name == JitRegistration.name. This repository's own deregistration model treats one runner name as potentially resolving to a list of ids, so name equality is not the exact registration identity the route minted.

Carry the delivered runner_id through the sealed dispatch subject, model the job resource's runner_id, and compare them. runner_name can remain corroboration. Required RED: same runner name and labels, different runner id, refuses.

The floor side has the analogous problem. The subject locates the authored workflow job by YAML id, then drops that id and retains only its display name. GitHub's job readback is accepted when its name equals that display name. Two authored jobs may share a display name, so a helper with the floor's name on the minted runner while the actual floor runs elsewhere currently satisfies the collection.

Either prove at subject mint that the selected floor's display name is unique across the workflow, or introduce a stable provider-observable marker. Add the same-display-name helper RED; the current helper control uses a different name and does not discriminate this case.

4. The authorized revision is not the ref dispatched

The sealed subject carries both branch and pinned revision. qualification_dispatch_effect names the revision, but CreateDispatch receives subject.branch. If that branch advances after the subject is minted, GitHub runs another commit. RunRevisionNotPinned notices only after the irreversible dispatch and after the wrong workload may already have executed on the self-hosted runner.

The dispatch subject needs an immutable executable ref tied to the pinned revision—for example an admitted tag/ref whose target is that revision—or an equivalent construction that makes branch movement unable to change the dispatched code. A post-run SHA rejection is necessary evidence, but it is not a pre-write admission wall.

Required RED:

subject minted for revision A
branch resolves to B before dispatch
→ no CreateDispatch request

The separate ruling-scope interpretation is represented honestly and its narrowing control is useful; I am not blocking on that shape here. The present-refusal cost fix is also accepted. When the boot leg finally declares the input, however, the production mint will again cross into full workflow construction, so that wiring PR must demonstrate the admitted path stays within its applicable execution budget or split out the floor-job declaration as another consumed projection.

Exact-head required CI is currently queued. These four findings are semantic and independent of its result.

…h authority bound to the token's installation; runner_id join with name corroboration and a unique floor display name

- JitCredentialBoundToAttempt gains dispatch: AuthorizedJitMintDispatch (set in receive_jit_mint; an
  edit to #13206's type made here per quiet-stag-623); the subject refuses a delivery from another
  dispatch (SubjectDeliveryForAnotherDispatch), RED with two authorized mints for one slot
- dispatch_authority_refusal: the authority must project to the registration's App/installation;
  an ActionsJobCredential refuses (REDs for other App, other installation, job token)
- WorkflowJobRun regains runner_id; the subject carries the delivered runner_id and collection joins
  on it, runner_name corroborating; the floor's display name must be unique at the subject mint

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

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE at the requested exact head 38e5456b16dc7f88356da20891272c39f573edda, against DESIGN.md §§3, 4d and 5. This reconfirms the approved leg-3 semantics with the subsequent ruling/discharge corrections. The branch advanced to b1646813556449a5462ad157c20b395fb5b74d58 during review; that later head is not covered.

The October 3 ruling is no longer rewritten to include dispatch. runner_lifecycle_operator_ruling.effect_subject states registration and deregistration, while runner_lifecycle_scope_ruling records the separate October 5 operator confirmation and its escalation reference. The record distinguishes the operator's quoted decision from the relayed description of its scope. I am relying on the operator confirmation supplied in this review request and its recorded attribution, not claiming to have independently opened the escalation service.

The revised relationship is correctly one-way: a site may consume this ruling's discharge only where covers names it; membership does not force consumption. ruling_discharge_for derives dispatch's discharge from that list. Registration, deregistration, branch creation/pinning and branch removal carry NoWitnessDischarge. Their existing EveryProvision/reversible/API/bindable/no-wide-mint/no-bill parameters independently select federation under authorization_pattern_selection.witness_required; removing the gratuitous discharge does not remove an obligation they otherwise owed. This does not make a new claim about the credential's least privilege or branch immutability.

The controls retain both useful directions: a discharge outside the scope is rejected by the census witness, each covered declaration must exist in the census, the five real census rows must select federation and conform, and removing dispatch from a supplied scope makes that same irreversible dispatch cease to select federation. The existing interlock-census witness remains.

The actual dispatch still checks the supplied UnitHoldProof against the slot's host before sending, then checks token and permission-authority App/installation identity. Collection still enters through plan_qualification_collection; both reads and the collected carrier are confined to its admitted arm. The JIT mint module is byte-identical to the previously approved 7ebc19e01aa6076c4d5acc87089a2c00570589a6 (blob dc3058a8f09fcb4bc91c1f6fd98b3b42bedd4410), preserving the receiver/generate-fold confinement. The first-step revision guard, attempt/job/runner joins and commit-ambiguous dispatch result remain in place.

This is a source/delta review, not execution of claims, mutants, dispatches or hardware actions. The exact-head PR-triggered witnesses run 37271534619 was queued when checked, so approval is not an all-green CI receipt. Retain required exact-head checks and the landing order: this base before its children. The previously declared workflow-file mutability and not-yet-wired route obligations are not discharged by this approval. The stale PR-body references to an agent interpretation and the earlier open delivery shape are not the implementation approved here.

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE at exact head b1646813556449a5462ad157c20b395fb5b74d58, against DESIGN.md §§3, 4d and 5. Reconfirmation of review 5410797131 at 38e5456b16dc7f88356da20891272c39f573edda.

The complete comparison from that approved head is one commit changing only annotations in gunbc.auth.privileged_effect_census and gunbc.runner.runner_qualification_ref_pin. No function, data row, signature, import or witness changes in this increment.

The clarification accurately separates authorization facts: the operator's scope covers the per-attempt branch creation/removal, not the dedicated group's selected-workflow update. ensure_qualification_ref_pin is named in covers for its branch-create effect; its group update relies on the existing independently selected federated standing. The row continues to carry NoWitnessDischarge, so this annotation neither broadens a ruling discharge nor removes an interlock. Dispatch remains the irreversible consumer of the scope-derived discharge and retains its host-hold requirement.

authorization_pattern_selection_witness_test.dag at this head has blob 3da3815d4438107d93d5f82ee66ef10d5399f190, identical to the previously reviewed 38e5456b16 file. Its merged import header, scope-discharge controls and other claims are therefore preserved byte-for-byte; this head update introduces no further merge-resolution change there. The previously accepted JIT confinement, collection planning, identity joins, revision guard and ambiguous-dispatch handling are unchanged.

Source/delta review only; no claims, mutations, dispatches or hardware actions were executed. The exact-head PR-triggered witnesses run 37273280868 was queued when checked. Land only after required exact-head checks pass, before dependent runner PRs. This approval does not clear #13288's independent create-request control gap or discharge the previously declared route-wiring and workflow-file-mutability obligations. Head reconfirmed immediately before submission.

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 5, 2026
…me field, no RestExchangePerformance)

Conflicts: github_effect_perform.dag (imports; the generate fold takes main's RestResult and keeps
this branch's admit_callers seal) and github_effect_perform_witness_test.dag (main's RestAnswered
call, this branch's sealed BoundJitCredential pattern). Beyond the conflicts, the port touches
logic: this branch's REST ops drop their outcome fields (CreateDispatch's body is now
result: WorkflowDispatchReceipt), the qualification performers carry RestResult<T>, and the
dispatch, run collection, ref pin and branch removal decisions match RestAnswered/RestRefused and
classify only the refusal (classify_rest_refusal, read or mutation).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot removed this pull request from the merge queue due to a manual request Oct 5, 2026
gunbai-bot Bot pushed a commit that referenced this pull request Oct 5, 2026
The three new operations drop their outcome field; read_job_log_with / list_run_artifacts_with /
download_artifact_with carry RestResult beside the sealed read; job-log and claim-cost refusals carry
RestExchangeRefusal from classify_rest_refusal (a download 410 stays ClaimCostArtifactExpired).
Witnesses build supplied RestResult values through small helpers.

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

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE at exact requested head 15eb7f6b1a16bf967cca27513522d3ae344cc536, against DESIGN.md §§3, 4b and 5. Reconfirmation of the leg-3 approval at b1646813556449a5462ad157c20b395fb5b74d58, which is this merge's first parent; the second parent is main c82e4971029b3b2ba1e58ece3e7ab0ed3a2357d9. The RestResult adaptation preserves the reviewed decisions without restoring the deleted outcome/body product.

REST boundary and dispatch

The four added/adapted operations describe decoded answer bodies only. CreateDispatch now has result: WorkflowDispatchReceipt, and dispatch_workflow_with projects answer.result only inside RestAnswered. The run, attempt-jobs and ref readers likewise project their declared body field only on an answer, carrying RestRefused unchanged. There is no fabricated payload beside a failed exchange.

qualification_dispatch_standing now carries the successful receipt in DispatchStandingSucceeded itself. Only that arm mints DispatchedQualificationRun. Refusals go through the existing classify_rest_refusal(RestMutationExchange): a decided status refusal remains DispatchStatusRefused, while transport failure, server error and undecodable mutation response remain commit-ambiguous with the sealed subject retained. No automatic retry or read-style classification was introduced. The supplied control still distinguishes 422, lost response, 503, undecodable 200 and the successful receipt.

The host hold, token-to-registration join, authority-to-installation join and permission admission still precede dispatch. This port does not change the previously reviewed authorization scope.

Collection

collect_qualification_run still matches the pure collection plan before either read and obtains run id/attempt from that plan. Its only success mint remains inside the checked entry; no unchecked _under path returns.

decide_qualification_run eliminates RestResult separately for the run and jobs. Their refusals are classified as reads and remain RunReadUnreadable / RunJobsUnreadable, never empty successful populations. The run-id/event/workflow, attempt-1, branch and revision comparisons, completion/conclusion checks, exact jobs population, and runner-id/name/floor-job joins remain in place. The collected payload still retains its sealed dispatch, observed run and designated job. The ported controls preserve the distinction between a failed floor that really ran on this runner and a run that cannot be attributed to it.

Ref pin and removal

The initial ref read still admits creation only on a classified 404. A present branch refuses; a lost/undecodable read is not absence. Create refusal classification remains mutation-specific, with the same preexisting/status/ambiguous decisions. Group-update refusals also use mutation classification, and successful update still requires the independent group restriction readback.

The SHA now comes from the answered GitRefWire inside qualification_ref_pin_of, rather than a separately supplied readback string. A refusal has no SHA to project. The exact-revision and group-readback controls are preserved.

Removal remains readback-driven. RestAnswered on the post-delete GET means BranchStillPresent, even after an answered delete; only a classified GET 404 establishes removal. The new delete_refusal: RestExchangeRefusal? retains a delete failure as explanatory evidence and does not decide absence. An already-absent initial read still returns removal without claiming a delete was issued. This is the same decision as the approved parent, expressed without a success member in RestExchangeRefusal or an invented readback body.

JIT merge resolution and evidence

jit_mint_performance_of_generate adopts main's RestResult while preserving its admit_callers restriction to the real performer and the consuming Bool claim. receive_jit_mint remains confined, and BoundJitCredential still holds the complete authorized dispatch, credential and runner id. The merged generate witness matches that sealed payload; the outside-generate probe now supplies RestAnswered and still targets the actual restricted callee. The port therefore does not reopen either rewrapping route.

Verified exact-head workflow 37304513141: floor, generated, emit-build and aggregate witnesses all succeeded. The reported remote Hermetic 158/0 across nine modules remains author-run evidence; I did not execute local tests, mutants, network mutations or hardware actions. GitHub initially reported non-mergeable during this review, then reported mergeable on the same unchanged SHA immediately before submission.

No remaining source blocker in this port. Preserve the declared route-wiring and workflow-file-mutability obligations. This review covers neither the forthcoming #13288/#13225 ports nor a live dispatch/boot execution; those landing heads need their own reconfirmation.

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 5, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 6, 2026
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 6, 2026
@gunbai-bot
gunbai-bot Bot removed this pull request from the merge queue due to a manual request Oct 6, 2026
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 6, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 6, 2026
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 6, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 7, 2026
gunbc-ci-auto-heal and others added 2 commits October 7, 2026 08:21
…dispatch fold

The collection-relabel probe is a fixture; its closure reached gunbc.fleet_converge_workflow through the dispatch module's production wrapper, and from there extdeps.gunbc (WitnessBin.Run), which the fixture-closure union emit refuses for target rust. The wrapper moves to gunbc.runner.runner_qualification_production_subject, the only importer of fleet_converge_workflow; the fold's closure no longer reaches extdeps.gunbc.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…STAYS DELETED prose (review 77507)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Addressed review 77507: the WorkflowJobRun annotation no longer says the field stays deleted; it records that the named trigger (job_ran_on importing and reading the fields in this change) landed. Pushed 8c1f0cd. Also earlier today: split the production wiring out of the dispatch fold so fixture closures no longer reach extdeps.gunbc (merge_group floor ejection). — sent from quiet-cat-583

…llected (review 77511)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Addressed review 77511: qualification_run_collected had no call site; deleted. — sent from quiet-cat-583

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE at exact head 048afa6f637ab0899be37c9f4a6ae23a163feec1, re-confirming the revision since approved 15eb7f6b1a16bf967cca27513522d3ae344cc536. No blocking finding. The module split is justified on its own merits, not merely as a way around FixtureClosureUnionEmitRefused.

Why the boundary is right independently of #13466

qualification_dispatch_subject_over decides whether a supplied Workflow, sealed JIT dispatch/delivery and ref pin describe an admissible qualification subject. Constructing the complete generated fleet workflow is a different responsibility. Only qualification_dispatch_subject chooses that concrete workflow and reads its separately declared dispatch inputs. Moving that binding into gunbc.runner.runner_qualification_production_subject removes an unnecessary dependency from the subject-admission/dispatch module, without weakening the obligation of the production binding to consume the actual generated workflow.

I would retain this separation even if every operation in the full fleet closure emitted successfully. The collection-relabel probe needs the collection and dispatch carriers; it does not need to construct unrelated fleet modes and their host realizations merely because they previously shared a file with those carriers. The semantic dependency is removed at its owner, not hidden from a closure walker. These three commits change no walker, fixture exclusion, gate enrollment, emit refusal, or target-specific fallback. There is no replacement fake workflow on the production route, no copied decision and no compatibility wrapper left behind. #13466's transport exclusion and this responsibility split address different boundaries.

Preservation checked

The comparison is three commits affecting four files. The production wrapper's signature and executable body move unchanged: the real fleet_converge_dispatch_inputs still supplies the early check; failure remains SubjectAttemptInputUndeclared; the other arm still passes the real fleet_converge_workflow to the same admission fold. The qualification subject remains sole_constructor. Its mint's admit_callers replaces the old wrapper identity with the new module-qualified identity and retains exactly the same two Bool-returning witness entries. Its actual delivery, registration, attempt, group, workflow and floor checks are unchanged. No token/hold, RestResult classification or collection read-plan decision changes in this revision.

The dispatch witness's import follows the wrapper, with its claim bodies and assertions unchanged. the_production_mint_over_todays_fleet_converge_workflow_refuses_at_the_attempt_input still calls the moved production function, through the real JIT mint, and requires the named missing-input refusal. The supplied-workflow positive still exercises the sealed subject mint and its downstream expectation. This preserves the existing pairing; it does not claim the not-yet-wired production acceptance arm has now run.

qualification_collection_relabel_probe is unchanged and still attempts to forge CollectedQualificationRun. Its executing consumer requires a blocking SoleConstructorViolation at that exact carrier; the names-only control is retained. The closure cut has not replaced the intended forgery discriminator with a generic compilation failure.

8c1f0cd2c1 changes only the runner-field annotation. 048afa6f63 removes the seven-line Bool projection qualification_run_collected, not the collection producer, sealed carrier or typed refusal. No witness body or collection decision is deleted with it.

Execution independently verified

Workflow 37597806570 explicitly names this head and completed successfully. All five jobs succeeded, including all-target lint, stage0 mirror checking, floor and emit-build.

I downloaded its required-floor-claim-cost artifact 11474690055 and verified the ZIP SHA256 against GitHub's published digest: 3bd8bad2e82d33d6af0ba5405582a9f88871324691f9fffa1b6a9176f01ad27e. The TSV records all 11 selected dispatch claims and all 5 delivery/collection forgery claims as pass, verdict reached, cost observed. In particular, the moved production-wrapper claim passed at 28ms CPU/28ms wall; the collection-relabel refusal passed at 33ms CPU/33ms wall. The real-mint positive and names-only control also executed and passed. These are selected-claim execution receipts, not a statement that the entire historical 21-claim dispatch roster was rerun by this floor selection.

Scope

The existing fleet-converge qualification-mode wiring remains owed: the current production wrapper still refuses rather than inventing that mode. This review does not certify a live dispatch/runner qualification or the downstream stacked PRs. I inspected the three commit patches, pinned source, head DESIGN and CI evidence, and locally hashed/parsed the artifact; I did not build the compiler, run a local mutant, perform hardware or GitHub workflow dispatch, or conduct a fresh whole-tree consumer/closure census.

No further changes, additional test lane, temporary rung drop or removal of this split after #13466 is required.

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 7, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 8, 2026
gunbc-ci-auto-heal and others added 2 commits October 8, 2026 06:16
…s row and witness read InterlockedBy

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…' declarations

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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