Skip to content

Deployment risk D2: role-following singletons resolve the prod-role holder - #13217

Closed
gunbai-bot[bot] wants to merge 66 commits into
mainfrom
session/sharp-heron-165
Closed

gunbai-bot[bot] wants to merge 66 commits into
mainfrom
session/sharp-heron-165

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

Deployment-risk conformance D2. Prod is switchable. Every role-following singleton follows prod_role_selection, and a role change is assessed against the observed world before any apply acts.

Round-9 blocking items: status on this head

  1. Production apply takes the deployment, not a spec: DONE. live_deploy_apply_with_access, the wet entries and the transaction entries take a DesiredDeployment. gunbc.live_deploy.role_singleton_convergence assess_and_admit(deployment, own) binds it to its desired_deployment_roster row; an unknown instance, a refused roster or a mismatched identity refuses as RoleSingletonDeploymentUnrostered. It then builds the spec from that row with selected_deployment_spec. The only spec-taking step, assess_and_admit_rostered, is sealed (admit_callers) to assess_and_admit. prod_role_realization_in is used by fixtures only.
    • RED: no_caller_supplied_spec_reaches_the_role_singleton_assessment. A spec handed to the assessor does not type (CallArgumentNameUnknown).
    • RED: an_altered_spec_is_refused_at_the_assessor_seal. A spec handed to the sealed step from outside is refused (ConstructorCallAdmissionRefused).
    • Probe scope: both probes import only role_singleton_convergence, one compile per claim (§3 one-interface rule).
    • Control: the_canonical_row_taking_apply_compiles_without_admission_refusal.
    • Mutant R: removing the seal turns the first RED red.
  2. Marker in a readable control directory: DONE. The store root is not widened. Details are below.
  3. Assess, warrant and realize as one operation that never returns the warrant: DONE. The admitted chain runs assess, then the sealed admitted step, then the sealed mutation, then the sealed member-plan step. The member-plan step mints AdmittedLiveDeployApply and realizes it, and returns the intent with the warrant replaced by LiveDeployApplyReported (live_deploy_apply_reported, sealed to that step). Every realizer arm refuses LiveDeployApplyReported. No returned or stored value carries a warrant.
    • REDs, one claim each:
      • an outside member-plan call (the_realizer_receives_an_apply_only_through_the_assessed_composition);
      • an outside warrant mint (no_outside_module_can_mint_a_warrant);
      • an outside warrant literal (no_outside_module_can_write_a_warrant_literal, SoleConstructorViolation).
    • Re-wrap probe, not authorable: re-wrapping an already-extracted warrant into a fresh LiveDeployApply cannot be refused at compile time, because the language cannot seal construction of a coproduct variant. Ruled option A (merry-tern-58): it is recorded on the existing row gunbc.recurring_failure_mode modifier_accepted_on_a_shape_its_check_cannot_see, whose hole is declared at v1.compiler.parse alias_rhs_is_anonymous_record. Trigger: coproduct variant construction wired through sole_constructor_construction_diags. With the warrant never returned, there is no extracted warrant to re-wrap.

Approval writer origin: host-carrying, dialed only on its host

The approval client dials the declared writer (main #13358: the roadmap or the broker, from approval_writer_authority). Both writers are served by the prod-role holder.

  • The origin carries the holder's host. ApprovalLoopbackOrigin = Resolved { host, origin } | Refused { cause }. Both writer arms set host = holder.instance.host_identity, each with its own endpoint: the roadmap at the holder's listen bind, the broker at its loopback. Under NoProdRole both refuse with the realization's cause.

  • The executor must be that host, before any local HTTP. The dial token cannot be forged. This is a classifier-versus-capability split, the same shape as the role-singleton admission.

    • classify_approval_origin(origin, executor) is pure and fixture-callable. It returns an ApprovalOriginStanding and never a token.
    • URLs are built only from DialableApprovalOrigin (sole_constructor). Its one mint, mint_dialable_approval_origin, is sealed (admit_callers) to approval_origin_admission_here.
    • approval_origin_admission_here reads this executor's own short hostname itself, with gunbc.hostname_read hostname_short_read. It is sealed to the two effectful approval_gate compositions, approval_standing_read and approval_file_stored_request.
    • approval_file_stored_request_at, which does the key read, the envelope write and the POST, is sealed to approval_file_stored_request.
    • Compile REDs, one claim each: an outside call to the mint, an outside token literal, an outside call to the live admission, and an outside call to the filing step are each refused. Control: the canonical filing and standing-read routes compile with no admission refusal.
    • Mutants: an unsealed mint fails the mint RED; an unsealed filing step fails the filing RED.
  • Pair-serving D0 derives its executor and admission host from the writer origin's host instead of a fixed srv1, and refuses under NoProdRole. This runs in the credential-free admission step, before any secret is loaded.

  • REDs (test.claim.approval_request_client_witness_test):
    These run on classify_approval_origin:

    1. srv2 holder, srv2 executor: dialable.
    2. srv2 holder, srv1 executor: refuses before any request.
    3. RoadmapProcess changes the endpoint and keeps the holder's host.
    4. NoProdRole refuses on both arms.
    5. Positive control: today's broker on the srv1 holder, from srv1.

    Also: an executor whose hostname is unreadable refuses. The client file is 15/15. Mutant H, with the host comparison dropped, fails RED 2. The D0 admission claims pass with the writer host supplied.

Cross-host observation: self-report (#13339)

Under the operator ruling of 2026-10-05, cross-host observation is host self-report, delivered by #13339, which is stacked on this branch. Each host's services report their own role-singleton state through RoleSingletonReadingSource.

Today, on this head, nonlocal hosts refuse as unobservable until #13339 lands. The production source is local_only_reading_source:

  • The applying host is read over the apply's own route.
  • Every other roster host is HostRoleSingletonsUnobservable (cause: self-report has not landed), and no transport is built for it.
  • role_singleton_convergence names no ssh transport at all: the former ssh probe source (ssh_probe_reading_source / transport_to) is deleted. So a later ssh grant cannot revive cross-host reads.
  • Claim a_valid_ssh_credential_cannot_make_a_nonlocal_roster_host_observable: an ssh credential to srv2, handed in as the apply's own transport, still leaves srv2 unobservable.

Consequence: every apply refuses before member planning until #13339 lands. This is fail-closed, with manual resume (#13124).

The read authority is named. The observer is the deploy job principal: gunbc.fabric_storage_placement fabric_storage_marker_observer, which is srv1_ci_deploy_runner_principal job_user, ghrunner. The store marker lives in a control directory whose derived mode admits that principal to list and traverse it. The store root's derived mode admits only the service and is not widened. A reader that lands as another account and is denied reads as unobservable, never as absent.

The model

  • Holder.

    • gunbc.roadmap_dashboard_instance prod_role_realization resolves the role over the instance roster (one lookup) and mints the only holder token, ProdRoleHolder (sole_constructor).
    • gunbc.live_deploy.desired prod_role_actuated_realization joins that holder to the one desired_deployment_roster. A holder with no row (MacBook, srv2-preview, srv2's lab) refuses as ProdRoleHolderUnactuatable. Every role follower reads the joined realization.
  • Role singletons as one subject. A spec carries the token as RoleSingletonHolding. These members are all built from that token, never from an instance id:

    • the broker unit and front door;
    • the fabric store unit, route, socket, root and directories;
    • the store ownership marker, which uses the dashboard marker naming rule dashboard_ownership_marker_in (fabric_storage_ownership_marker_path).
  • Convergence (gunbc.live_deploy.role_singleton_convergence).

    • Observation. Sightings are kept at (host, deployment, member) until multiplicity is adjudicated, and every roster deployment's members are probed on every roster host.
    • Possible outcomes:
      • RoleSingletonNoop: exactly one complete realization of the desired holder on its own target.
      • RoleSingletonInstall: nothing observed anywhere.
      • RoleSingletonRefused: one of move, removal, desired-side (only ProdRoleUnheld means no prod), unobservable, two-hosts, misplaced, two-on-one, ambiguous, broker-without-store, partial, stale-spec.
  • Admission. RoleSingletonAdmission (sole_constructor) binds the exact spec.

    • Mint: its only mint is sealed (admit_callers) to assess_and_admit_rostered, which only assess_and_admit(deployment, own) may call.
    • No caller-supplied spec. The production apply and transaction entries take a DesiredDeployment, never a DeploymentSpec. assess_and_admit binds the row to desired_deployment_roster with bind_desired_selected_identity; an unknown instance, a refused roster or a mismatched identity refuses as RoleSingletonDeploymentUnrostered. It then builds the spec from the bound row itself. The assessor reads the actuated desired realization and the observation through RoleSingletonReadingSource itself, so no caller can supply a spec, a decision or a desired realization. prod_role_realization_in remains for fixtures only.
    • Assessor and consumers: the assessor, the token's readers and every admitted apply step are sealed to the two canonical assess-and-act compositions (live_deploy_apply_via_transport, live_deploy_transaction_decided). So a token can be neither forged, obtained elsewhere nor replayed.
    • Refused before member planning: a stale spec, a holding spec under NoProdRole, and a non-holder spec during Install.
    • Receipt: the apply receipt (schema v3) records role_singleton_disposition: Noop, Installed or RefusedBeforeApply.
  • Unmarked legacy store (no automated adoption). A realization with its broker and fabric unit but no store ownership marker refuses as RoleSingletonUnmarked. Today's srv1 holder is that case, and under docs/plans: dogfood route manual interventions, dispositioned #13124 it waits for the operator. The refusal text names the one-time disposition; the plan and the failure-mode row record it too:

    • on srv1, create the control directory /opt/gunbc/fabric-storage-control: mode 2750, owner briansrls, group ghrunner, as fabric_storage_control_directory derives;
    • in it, write .gunbc-dashboard-instance-srv1-live;
    • mode 0644, owner briansrls;
    • content deployment=srv1-daily-workspace\nhost=srv1\n; group briansrls.

    The holder spec's own marker ensure writes the same content.

  • Effect boundary. LiveDeployApply { warrant: AdmittedLiveDeployApply }. The warrant is sole_constructor in the leaf gunbc.live_deploy.apply_warrant. Its one mint is sealed to live_deploy_apply_member_plan_via_transport, which is sealed to the admitted mutation.

    • The warrant never leaves that sealed step. It is minted and realized there, and the intent it returns has the warrant replaced by LiveDeployApplyReported { spec, candidate }, which every realizer arm refuses (IncompatibleCell). No returned or stored value carries a warrant.
    • Compile REDs: an outside member-plan call, an outside warrant mint, and an outside warrant literal are each refused. The canonical live_deploy_apply_with_access still compiles.
    • Unauthorable part: re-wrapping an already-extracted warrant into a fresh LiveDeployApply cannot be refused at compile time, because the language cannot seal construction of a coproduct variant (sole_constructor is discarded on a coproduct right-hand side). This is recorded as a receipt on the existing row gunbc.recurring_failure_mode modifier_accepted_on_a_shape_its_check_cannot_see. The hole itself is declared at v1.compiler.parse alias_rhs_is_anonymous_record. Trigger: coproduct variant construction wired through sole_constructor_construction_diags; then the variant is sealed and the re-wrap probe becomes enrollable.
  • Store ownership marker: one authority. FabricStorageOwnershipMarkerSpec { path, content, mode, owner, group } is carried by the holder spec and by RoleSingletonUnmarked, whose refusal text renders it.

    • It is installed with deploy_stage_install_owned_command.
    • It lives in the store's control directory fabric-storage-control, not the store root: fabric_storage_control_directory is owned by the service, with the observer as its group needing read and traverse, and its mode is derived from those two users.
    • The readback has three distinct outcomes, decided in marker_sighting_of: an exact readback is the marker; a listing without the marker is absent; a denied listing or read is unobservable.
    • It is read back field by field (bytes, and stat mode, owner and group; shell.Stat.GroupOf is added).
    • Only an exact match is the marker. A mismatch refuses as RoleSingletonMarkerMismatched, distinct from absent (RoleSingletonUnmarked).
    • Deliberately Ensured, not an Owned (retract-removed) member: a retract that removed the marker while the Ensured store survived would reopen the 'old store reads as nothing' hole the marker exists to close. Its install is owned (owner, group and mode from the row).
  • Broker. The role decides only WHERE the broker is installed. Placement binds GUNBC_APPROVAL_BROKER_BASE_URL into the broker unit and the enrolment verb's env. Broker-side modules read what they are given and never import the instance roster, which keeps Native broker 1A: narrow the approval broker's closure (435 → 196 modules, 1091 → 253 native errors) #13132's closure. Trust derives from approval_broker_listen_host, the row the broker unit's --host is emitted from.

  • Other followers. The GCP IAM job's runner (a refused job under NoProdRole), the approvals app host, the enrolment step's target host and host check, the approval client origin, and DashboardProtectedSibling all follow placement or the realization.

  • Fixes along the way.

    • MacBook's serve_function was bound to srv1-live's handler. A per-instance control now checks every instance.
    • The fleet-converge broker-host step names are host-neutral. The YAML is regenerated, and generated passes.
  • Fabric domain move (warm-badger-442 notified). The runtime placement fabric_storage_placement() moved to gunbc.fabric_storage_binding, so it can read the actuated realization without a cycle through live_deploy.spec.

Evidence

  • CI on ee1d7e6: floor, emit-build, generated and witnesses all PASS. CI on 3b04d32: emit-build and generated pass, floor pending.

  • Remote claims on eba4db2 (and the tightened control-directory claim on 947702b) (claim_batch, EstimatedMemory=24GB, cgroup bind):

    File Result
    deployment_risk 33/33
    repository_transition_admission 2/2
  • New in round 9:

    • no_caller_supplied_spec_reaches_the_role_singleton_assessment: three compile probes.
      • (a) Passing a spec to the production entry is refused with CallArgumentNameUnknown.
      • (b) Passing an altered spec to assess_and_admit_rostered from outside is refused with ConstructorCallAdmissionRefused.
      • (c) The canonical row-taking call compiles.
    • the_marker_observer_reads_the_control_directory_and_not_the_store_root: with the real observer and the derived modes, the control directory admits the observer as its group and grants the other class nothing, and the store root does not admit the observer.
    • the_marker_readback_has_three_distinct_outcomes: exact, absent, denied listing and denied read.

    Limit: the three-outcome claim drives the step that decides the outcome from the observer's leg results. The runner executes as root, so it cannot produce a real permission-denied read.

  • Earlier runs, same code for these files: roadmap_launch_deployment_receipt 48/48, apply_receipt 5/5, submission 12/12, emit_test 84/84, devboot 20/20, enrolment 7/7, serve control 7/7, apply 14, client 9, store 9, approvals app 19, fabric roster 7, deployed tree 11, readback seals 2.

  • Mutants. Each turns its RED to FAIL:

    Mutant RED that fails
    multiplicity dropped two-hosts
    desired refusals read as no-prod desired-side
    unmarked refusal skipped unmarked
    admission ignores staleness stale-spec
    marker owner check skipped per-field marker
    holding pinned to srv1-live move
    MacBook bound back to srv1-live's handler macbook_local_serves_itself
    assess_and_admit_rostered seal removed no_caller_supplied_spec
    control directory group reverted to the service (derivation then grants other r-x) marker observer / control directory
    observer dependent dropped from the control directory marker observer / control directory

Honest non-pins and residue

  • The nullary serve/belt/dispatch/launch entry points are srv1-live's own entry points, so they must not follow the role.
  • fleet_reach_endpoint is a host-shared account fact.
  • belt_dispatch_instance_cli's absent-env default stays with the belt lane.
  • srv1_gunbc_approval_broker_root naming is D4 residue.
  • The one-time srv1 marker disposition is owed by the operator.
  • Move/Remove convergence is the next deliverable after D2 (operator ruling 2026-10-05).
  • No class-dependent policy: no risk class self-repairs; every disposition stays manual (docs/plans: dogfood route manual interventions, dispositioned #13124).
  • Item 2 (interface, harm bet, disposition gate) is deferred until an operator policy differs by class.

🤖 Generated with Claude Code

gunbc-ci-auto-heal and others added 13 commits October 3, 2026 17:18
…eployment, wrong-instance failure-mode row, RED witness

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lver it describes (DESIGN 4c)

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

prod_role_realization in gunbc.roadmap_dashboard_instance maps prod_role_selection to its
HostDashboardInstance (the roster filtered by deployment) and refuses with a typed cause under
NoProdRole, an unrealized or an ambiguous holder. The approval broker owner, fabric storage
placement, approval client origin and identity trust follow it; ProductionPeer becomes
DashboardProtectedSibling derived by host and class. RED + positive control in
test.claim.deployment_risk_witness_test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e subject (side-chat ruling)

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…y broker owner and fabric placement (review 75352)

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

Review 75352 is addressed in 35dcd87. The dangling instance_realizes_prod_role_in is deleted. Its replacement is gunbc.roadmap_dashboard_instance prod_role_realized_by(realization, instance_id), now the only holder-identity comparison, called by both instance_owns_the_host_approval_broker_under and fabric_storage_placed_on_under. I re-ran the claims remotely on 35dcd87: deployment_risk_witness_test all PASS, the 4 protected-sibling claims PASS. A mutant that pins that single comparison back to "srv1-live" makes the RED FAIL, while the positive control still passes.

Brian Searls and others added 7 commits October 4, 2026 08:23
…ence gate (side-chat NO-LAND blockers 1-5)

- ProdRoleHolder sole_constructor token; specs carry RoleSingletonHolding and build broker,
  front door, fabric unit/route/socket/root/directories from it (no instance_id ownership).
- Fabric store directories leave the srv1 target and land on the holder's spec.
- gunbc.live_deploy.role_singleton_convergence: desired realization x observation of every roster
  host gates each apply: Noop | Install (nothing observed) | typed refusal (move, removal,
  unobservable, ambiguous, ownerless broker).
- GCP IAM converge job runs on the holder's host; refused job under NoProdRole.
- macbook_local serves roadmap_serve_handle_macbook_local; roster-wide serve_function control.
- Failure-mode row and plan agree on honest non-pins and the next trigger.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tance callers main added (queue dequeue on failed floor)

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

Main moved the approval modules onto gunbc.srv1_dashboard_bind and added approval_broker_host_identity
consumers (link base, device enrolment, approvals app). Those now read the typed ApprovalBrokerAddress
derived from the prod-role holder, each refusing through its own arm; fabric_storage_binding follows
the placement's refused arm and per-instance door socket.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ty route (/healthz is release-only)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…es receive their host and bind

Broker-side modules (endpoint, decision store, submission, device enrolment) no longer import the
instance roster, keeping #13132's narrowed native-broker closure: the serving broker reads its own host
(approval_broker_serving_base_url), and identity trust derives from approval_broker_listen_host, the one
row the broker unit's --host is emitted from. Role-dependent placement for non-broker readers (GCP IAM
job, approvals app) moves to gunbc.auth.approval_broker_placement. srv1_dashboard_bind is deleted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brian Searls and others added 4 commits October 4, 2026 10:09
… read; hostname_read's closure cycled back through live_deploy.spec)

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

Reverts the /proc/sys/kernel/hostname leaf read (a second route beside gunbc.hostname_read, Linux-only).
Placement binds the base URL from the holder token into the broker unit's Environment= line and the
enrolment verb's env prefix (GUNBC_APPROVAL_BROKER_BASE_URL); the broker reads the value it was given
and refuses an unbound or non-https value. The submission and enrolment folds take the address as a
supplied value; the enrolment step's ssh target, dispatch host check and binding follow placement
instead of srv1. Moved-role control at the emitted-unit boundary.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…host-neutral); fix approval_broker_serve import

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ator output faf2324); serve_function control passes request_headers

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Base automatically changed from session/merry-tern-58 to main October 4, 2026 12:59
Brian Searls and others added 3 commits October 4, 2026 13:01
…ost_srv1 now from gunbc.fleet_host_identity

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… role_singletons (census: the only literal without it)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ldResidueRosterDiverged: wildcard over a closed coproduct)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brian Searls and others added 9 commits October 5, 2026 03:53
…oduction entry so only the label wall can refuse, and asserts zero seal refusals (the probe at the sealed assessor drew only ConstructorCallAdmissionRefused); census cost receipt on entry_scoped_typecheck_rss_tracks_the_name_census

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ly; every nonlocal roster host is unobservable (self-report has not landed, #13339) with no transport built; the ssh probe source is deleted and the module names no ssh transport

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ter (#13358), each writer arm resolving through the prod-role realization and refusing under NoProdRole; fleet-converge.yml main + host-neutral step names

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hrough a sealed DialableApprovalOrigin minted when this executor's short hostname is that host; pair-serving D0 derives its executor host from the approval writer's (refusing under NoProdRole); REDs 1-5

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… token; the DialableApprovalOrigin mint is sealed to approval_origin_admission_here, which reads the hostname itself and is sealed to the two approval_gate compositions; approval_file_stored_request_at sealed to approval_file_stored_request; compile REDs for forged mint, literal, outside admission and outside filing step, with the canonical-routes control

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… reading its refusal by class and subject (the census memoizes by source), cutting the floor's probe compiles from 14 to 5 after the floor hit its 90-minute cap

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ep names, regen-verified next)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 5, 2026
…'s RoleSingletonsNoop import)

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 6, 2026
Brian Searls and others added 3 commits October 6, 2026 05:30
…itted by v2.workflow.bash_emit (echo of the typed cause to stderr, exit 1), gated at generation like the release-bins scripts; RED: byte-equal to the modeled emission (review 76898)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (main made the match exhaustive); census-cost receipt: the probe peak moves with main

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…host-neutral step names, regen-verified next)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d rust stderr-capture; fleet-converge.yml regenerated = main + D2 host-neutral step names)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brian Searls and others added 2 commits October 8, 2026 17:22
…reading onto the typed SystemdRunCommand

#13257 typed the enrolment-code verb (enrolment_code_issue_remote_command -> SystemdRunCommandReading,
sudo_elevate_words, Ready/Refused arms). Kept that shape whole and added only D2's facts:
- enrolment_code_issue_remote_command takes base_url and adds the APPROVAL_BROKER_BASE_URL EnvSet;
- enrolment_code_issue_remote takes host + base_url; ssh target is enrolment_code_issue_ssh_target(host);
- refuses_off_srv1 replaced by D2's enrolment_code_issue_refuses_off_broker_host (placement-derived);
- witness: main's typed the_remote_verb_words_carry_the_release_shape gains the base_url word; D2's
  placed-host claim replaces the srv1 pin claim.
fleet-converge.yml regenerated (generated_artifact_gate main_wet_one) = main + D2 host-neutral step names.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…al (srv2 admitted, srv1 refused), so a refusal re-pinned to srv1 reds

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot marked this pull request as draft October 9, 2026 17:09
@briansrls
briansrls marked this pull request as ready for review October 9, 2026 17:25
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-09T17:29:43.074090Z 777cbea Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 777cbea532

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

fn fleet_converge_approval_keyring_converge_step() -> Step {
RunStep {
name: Present { value: "Materialize approval MAC keys on srv1 (SM pinned -> sha256 prefix -> /etc/gunbc-roadmap 0640)" },
name: Present { value: "Materialize approval MAC keys on the approval broker host (SM pinned -> sha256 prefix -> /etc/gunbc-roadmap 0640)" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Resolve the keyring target from broker placement

When prod_role_selection moves the broker to another host, this renamed step still invokes approval_keyring_converge_wet, whose approval_keyring_converge_refuses_off_srv1 rejects the correct non-srv1 dispatch and whose keyring_ssh_target always targets operator_host_srv1. The new broker therefore cannot receive its MAC keys and approval operations will fail with unavailable keyrings; derive both the admission and SSH target from prod_role_actuated_realization rather than only relabeling the workflow step.

Useful? React with 👍 / 👎.

fn fleet_converge_approval_broker_dark_install_step() -> Step {
RunStep {
name: Present { value: "Install the approval broker dark on srv1 (additive unit + slice; does not restart gunbc-roadmap.service)" },
name: Present { value: "Install the approval broker dark on the approval broker host (additive unit + slice; does not restart gunbc-roadmap.service)" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bind dark install to the selected broker host

If the prod role is assigned to srv2-deploy, this step still calls approval_broker_dark_install_srv1_wet; that entry constructs deployment_spec_srv1() and uses ci_deploy_srv1_access. Under the new holding logic the srv1 spec has HoldsNoRoleSingleton, so the emitter refuses instead of installing the broker on srv2, making the advertised broker-host mode unusable after a role move.

Useful? React with 👍 / 👎.

fn role_singleton_holding_under(realization: ProdRoleRealization, instance: HostDashboardInstance) -> RoleSingletonHolding {
match realization {
ProdRoleRealizedBy { holder } =>
if holder.instance.deployment == instance.deployment { HoldsProdRoleSingletons { holder: holder } } else { HoldsNoRoleSingleton }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Move the broker memory charge with its holder

When the selected holder is on srv2, this branch makes that deployment's spec install the approval broker and its dedicated slice there, but gunbc.live_deploy.served_slice_charge::served_deployment_placement remains pinned to srv1 while its allowance includes approval_broker_slice_memory_max. Consequently host_served_deployment_claim reports no broker allowance on srv2, allowing the fleet budget to admit workloads without accounting for the broker's reserved memory; derive or split the charge according to the role holder's target.

Useful? React with 👍 / 👎.

@gunbai-bot

gunbai-bot Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Closed without folding in the v1 closeout bankruptcy (#13641). D2: red floor and conflicting with main; the deployment-risk D2 stack is not complete. Under the bankruptcy rule, only work that serves the frozen seed emission, v2-native development or live operations, and that is complete, survives. The branch is kept for archaeology; no follow-up obligation is created. — sent from neat-wolf-604

@gunbai-bot gunbai-bot Bot closed this Oct 9, 2026
@gunbai-bot gunbai-bot Bot mentioned this pull request Oct 10, 2026
gunbai-bot Bot added a commit that referenced this pull request Oct 11, 2026
…role holder (#13688)

* Deployment risk D2 layer 1: role-following singletons resolve the prod-role holder (re-derives #13217)

ProdRoleHolder token and resolver in roadmap_dashboard_instance, actuated join in live_deploy.desired,
fabric placement, approval broker placement and emit repointed off the srv1 pin. Claims in
test.claim.prod_role_realization_witness_test.

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

* Deployment risk D2 layer 1: witness fixtures follow the role-following signatures

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

* Deployment risk D2 layer 1: enrolment-code issuer follows the placed broker host; apply witness stays at main's fixture

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

* Deployment risk D2 layer 1: emit_test and release fixture follow RoleSingletonHolding and instance-keyed fabric paths

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

* Deployment risk D2 layer 1: drop unconsumed shell.Stat GroupOf; pin ProdRoleHolderUnactuatable in the actuated-join claim (review 78458)

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

* Deployment risk D2 layer 1: unit-emission oracle follows role_singletons and the broker base-url environment

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

* Regenerate fleet-converge.yml and gunbc-ghrunner.sudoers via main_wet (absorbs main's drift from #13690/#13705/#13710)

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

* Cite approval_broker_host_under at its home module (review 78559)

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

* Drop the L2-only role_singleton_disposition fixture edit from the launch receipt witness (not a field on main's LiveDeployApplyReceipt)

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

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants