Skip to content

RLM-1: one shared serial launch admission consumed by page, POST /dispatch, and belt timer; ManualReady production mode; remove ordinary AvailableAsOverride - #9696

Merged
gunbai-bot[bot] merged 22 commits into
mainfrom
session/fierce-badger-326
Aug 30, 2026

Conversation

@briansrls

@briansrls briansrls commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor

Gate RLM-1 of the Roadmap Launch MVP (docs/plans/roadmap-launch-mvp-plan.md §3). Stacks on the RLM-0 frozen join below; origin/main is merged in (merge commit, no rebase).

RLM-0 frozen join (recorded by the manager from producers, 2026-08-29)

fact value
RLM-0 merge commit on main f99ac746bee4a784945fb40e85e4547c0827e26d (squash of #9694 head c59d800c26c10124c64a63531a679117690750b8)
git tree 2272c7137e340cd922b03c85373a68a7585b4f50
canary module blob dag/gunbc/roadmap/roadmap_launch_canary.dag 9dea489a2212c4514f59050442b131110604e59a — identical on this head
pinned oracle blob dag/test/manual/roadmap_launch_canary_oracle_test.dag (pin identity = validation_oracle_source_identity over it) 79376194dafc2704b5e3e47ed87caa5820be6fa3 — identical on this head
plan blob docs/plans/roadmap-launch-mvp-plan.md 86ac055c6e4482f649902826869839fc8872d49a — identical on this head (the merge took main's plan verbatim)
oracle source identity (validation_oracle_source_identity over the pinned oracle, via belt_oracle_observe_in_worktree; modeled identity, distinct from the git blob above — the reviewer requires both) 6b24746949c96306 (manager-derived on BuildBuddy against a fresh build of the reviewed tree)
criteria digest producer gunbc run --source-root dag --source-root src/v2 --entry dag/gunbc/roadmap/roadmap_launch_canary.dag --function roadmap_launch_canary_criteria_digest_hex → 92ef80595cd8e8a4 on this head, equal to the join

The one decision

gunbc.roadmap_launch_admission (new) answers may THIS cause start THIS node NOW? once, purely, over two observed inputs — a LaunchHostStanding (instance posture, transition admission, fleet revision standing, live session set, capacity, spawn mode) and a LaunchGraphStanding (the projected roadmap document, or the projection's refusal). Gate order is the plan's, and is the safety property:

instance → transition → revision → node (authority / unknown / superseded / accepted / review-only) → size+contract → dependencies → no live attempt → capacity → cause permitted by mode

LaunchCause      = Operator | Timer
SpawnMode        = Paused | ManualReady | AutomaticReady          (gunbc.roadmap_belt — replaces BeltRunState at the root)
LaunchAdmission  = LaunchAdmitted { identity: LaunchIdentity{node_id, cause, mode, instance_id, revision} }
                 | LaunchRefused  { refusal: LaunchRefusal }
LaunchRefusal    = InstanceActuationRefused | TransitionInhibited | RevisionUnobserved | RevisionDrift
                 | AuthorityUnobserved | NodeUnknown | NodeSuperseded | AlreadyAccepted | ReviewRequired
                 | SizeMissing | ExecutionContractMissing | DependenciesBlocked
                 | SessionsUnobservable | AlreadyLive | StaleSession | LivenessUnobserved | SessionMultiplicityConflict
                 | NoCapacity | CauseNotPermitted | GateNotEvaluated           (all `Launch`-prefixed in source)

Deviations from the plan's sketch, each a §3 "preserve every required refusal" call rather than a new authority: the route already refused stale session, liveness unobserved, multiplicity conflict, sessions unobservable, authority refused, and a superseded node had no arm — those are kept as arms (with their existing wire words). Unplanned is not minted: in gunbc.roadmap_spawner an unplanned node is an unsized one, so it is one fact (LaunchSizeMissing) with SchedulingUnplanned as its presentation. LaunchCauseNotPermitted carries the "cause permitted by mode" gate the sketch listed as a step without a variant.

The decision is a ledger of per-gate verdicts folded in order (launch_gate_ledger / launch_admission_of_ledger), so the plan's mutation control (c) is a data mutation in a witness, not a production escape hatch.

The three surfaces are projections

surface producer what it does now
daily workspace / full page gunbc.roadmap_presentation dispatch_action(lifecycle, admission) via row_launch_admission (cause Operator); host standing observed once per served render by gunbc.roadmap_belt_actuate belt_launch_host_standing and threaded by roadmap_page admitted → ActionAvailable/PrimaryAction; refused → ActionUnavailable/SecondaryAction, button rendered disabled with data-launch=<wire label> and title=<reason>. AvailableAsOverride is deleted (all 4 emission sites). Static renders (static site, surface probe, fixture pages) pass launch_host_unobserved_static_render and refuse at the instance gate — no actuating instance stands behind those bytes.
POST /dispatch/{node_id} gunbc.roadmap_belt_actuate belt_dispatch_node_for_instance → launch_admission(cause: Operator, …) actuates only under LaunchAdmitted. BeltDispatchResult (14 arms) is deleted; the route returns `BeltSpawnOutcome = Spawned
belt timer belt_tick_for_instance → belt_tick_admit_and_spawn folds every reconcile upsert through launch_admission(cause: Timer, …) with reserved = launches already admitted this tick a refusal is recorded whole as SpawnNotAdmitted; the tick's private stale/unobserved/conflict deferral filter and belt_node_session_liveness / BeltRevisionAdmission are deleted — those facts are the admission's gates now.

ManualReady is production

gunbc.roadmap_belt roadmap_belt_production_spawn_mode: SpawnMode = ManualReady (was roadmap_belt_default_run_state = Running). BeltRunState and SpawnMode were one concept and are migrated at the root; belt_reconcile runs in both non-paused modes (teardowns of departed members continue in ManualReady, upserts are planned and then refused CauseNotPermitted by the admission so the receipt shows what the timer would have started), verify/publish arms are untouched and still run. Capacity is unchanged (3) and is now one arithmetic (belt_free_slots_int) read for both causes — the operator's click respects it too.

Red controls (all enrolled, executed via claim_batch)

test.claim.roadmap.roadmap_launch_admission_witness over the RLM-0 synthetic canary graph and a fabricated host (test.claim.roadmap.roadmap_launch_admission_fixture):

  • (a) witness_blocked_child_refused_dependencies_blocked + witness_blocked_child_fold_reaches_no_actuation — the tick fold over the blocked child yields exactly one SpawnNotAdmitted and no Spawned/SpawnFailed (actuation in a witness env would answer SpawnFailed at clock/footprint; its absence is the effect-ordering receipt). The route shares the identical LaunchAdmitted => guard.
  • (b) witness_timer_in_manual_ready_refused_cause_not_permitted, witness_timer_fold_in_manual_ready_starts_nothing, positive control witness_timer_in_automatic_ready_is_admitted; reconcile-layer test.claim.roadmap_belt_witness witness_reconcile_manual_ready_still_reaps_and_plans.
  • (c) witness_dependency_gate_is_load_bearing (ledger with dependencies := GatePassed admits) + witness_mutating_a_passing_gate_changes_nothing.
  • (d) witness_drift_refuses_ui_and_backend_with_one_reason — page WorkAction.label/detail == tick outcome label/reason; page half also test.claim.long.roadmap_page_witness witness_upcoming_node_dispatch_refused_dependencies_blocked.
  • (e) witness_second_operator_admission_is_already_live.
  • (f) test.claim.roadmap_launch_canary_witness finding_unspecified_contract_is_ready_but_environment_refuses flipped from finding to regression control: frontier still Ready, environment still refuses, and the admission answers ExecutionContractMissing.
  • gate order, capacity/reserved, host-unobserved, accepted-parent unlocks child, label totality (20 distinct), contract digest.

Review 5059520727 — the four production boundaries (repaired on HEAD)

finding repair controls (all in test.claim.roadmap.roadmap_launch_admission_witness unless named)
1. capacity pre-selected before admission gunbc.roadmap_belt belt_reconcile returns every upsert in plan order (free / deferred / take deleted, BeltReconcile.upserts); gunbc.roadmap_belt_actuate belt_tick_admit folds all of them through launch_admission with reserved = launches already admitted in this fold, so excess rows are typed LaunchNoCapacity; the tick's deferred figure is derived from those outcomes (belt_tick_deferred_count). The fold is split from actuation (belt_tick_spawn_decided) so it is assertable without entering the actuator. witness_three_candidates_one_slot_all_reach_admission (AutomaticReady: #1 admitted, #2/#3 LaunchNoCapacity{occupied:1,max:1}), witness_three_candidates_manual_ready_all_reach_cause_gate (all three CauseNotPermitted), test.claim.roadmap_belt_witness witness_reconcile_returns_every_upsert_regardless_of_capacity
2. live timer bypassed the shared gate; observation eager One staged observer belt_launch_host_standing_for_instance behind page, POST and timer: posture is a model fact; the transition file is read only for an actuating instance; the fleet revision only under an admitted transition; tmux only under a converged revision. A halt is LaunchHostHalted { gate, refusal } whose later fields do not exist (no constructor for "tmux listed under an inhibited transition"), and its refusal is derived by the admission's own gate fns (launch_host_halt_transition, launch_host_stage_revision). belt_tick_for_instance = observer → pure belt_tick_launch_pass → actuation; the tick-local string refusals are deleted and BeltTickResult.host_refusal: LaunchRefusal? is launch_host_refusal(standing), the same value every per-node SpawnNotAdmitted carries. Verify/publish continue on every arm except a transition halt (the deploy's wholesale refusal, unchanged). real decision on staged standings built by the production producers: witness_timer_pass_transition_halt_refuses_every_desired_node, witness_timer_pass_revision_drift_refuses_before_sessions, witness_timer_pass_sessions_unobserved_is_shared_refusal (each: every desired node withheld, no reconcile/teardown, page label+reason == route label+reason == tick refusal), witness_halted_ledger_marks_later_gates_not_reached, positive witness_timer_pass_clear_host_decides_ready_parent; receipt-layer observe_refused_spawn_does_not_zero_verify_pass (verify continues under a host refusal)
3. unknown liveness failed open in capacity launch_occupying_count: a present session whose process is unobserved occupies its slot; only a proven exited / process-absent session frees it (declared in launch_occupancy_note; the contract text names occupancy=running+unobserved). witness_unobserved_process_occupies_a_slot (cap 1, one unrelated WorkerProcessUnobserved, Ready candidate → LaunchNoCapacity{1 of 1}), positive witness_exited_or_absent_process_frees_its_slot
4. identity constructed then discarded Spawned / SpawnFailed carry launch: LaunchIdentity, threaded from the admission site through every actuation step; persisted in the attempt state as launch-identity.json beside environment-admission.json; emitted in the POST JSON (launch object) and the durable tick receipt (launches, schema roadmap-belt-tick-receipt/v3, decoded fail-closed); the page's available action carries WorkAction.launch rendered as data-launch-cause/mode/instance/revision. Rendered identity is evidence, not authorization — the route still re-observes and re-admits. witness_page_post_timer_bind_one_identity (same node/mode/instance/revision, cause operator/operator/timer), witness_spawn_outcome_json_carries_launch_identity, witness_tick_receipt_round_trips_launch_identities, test.claim.roadmap_belt_actuate_witness witness_identity_survives_actuation_failure

Controls landed first (bd6f9c369a, red against dff43862a2's shapes), repairs after. The prior belt_reconcile_capacity_defers_surfaced witness, which pinned finding 1, is replaced by the every-upsert control.

Review 5059681676 — the two remaining boundaries (repaired on HEAD)

finding repair controls
1. transition-halted tick wrote Recorded for passes it skipped BeltTickResult.wholesale: TickPassesRun | TickPassesWithheld { refusal } — a transition halt (the deploy's atomic wholesale refusal, kept) is a typed fact and the durable receipt derives all four passes as Refused with the transition reason; a non-transition host refusal withholds only the launch passes and verify/publish stay independently derived. test.claim.roadmap_belt_actuate_witness transition_halted_tick_refuses_all_four_passes, revision_refusal_keeps_verify_independently_recorded
2. session observation eager w.r.t. node/contract/dependency gates LaunchSessionsPending is the observer's output; launch_admission_staged decides the pre-session gates (typed LaunchGateStage on each ledger row) and answers LaunchStageNeedsSessions only for a node that passed them. POST completes the standing for that node only (belt_dispatch_node_staged_over); the page completes once when any row needs it (belt_launch_host_standing_for_page); the timer once for its reap pass (belt_launch_host_standing_complete). A pending set that reaches the full decision refuses at the live gate (GateNotReached → gate_not_evaluated), never reads as empty. real route decision path over the canary graph under the hermetic route (a tmux effect would be a route refusal, not a verdict): staged_post_refuses_unknown_node_without_listing_tmux, …contractless_node…, …blocked_child…, staged_post_transition_halt_refuses_without_listing_tmux; pure stage controls in the admission witness (witness_pending_sessions_*, six rows)

Also from review 57491: ledger rows carry a typed stage (no take(6)); a render with no actuating instance refuses LaunchNoActuatingInstance { reason } (label no_instance, 403) instead of an empty instance id; LaunchGraphProjected is flattened once per standing (launch_graph_of_document) — the per-row find and dependency scan remain linear scans, named in launch_graph_resolved_note; belt_dispatch_status_note rewritten. Not taken: folding reserved across page rows — reserved counts launches admitted in one actuation fold; page rows are independent operator decisions and the route admits any single click, so folding would refuse rows the route admits (disagreement in the other direction).

Review 5060354380 — the host-stage roster is derived, not spelled twice (repaired on HEAD)

launch_gate_template() reads the canonical row roster once from launch_ledger_rows over a ledger with no gate evaluated; launch_contract_text_of_rows derives the pre_session=…;session=… fragment from each LaunchGateRow.stage (launch_rows_stages) — the former host_stages literal is deleted. Mutation control witness_contract_binds_the_session_boundary: flipping one row's stage or reordering the rows changes the contract text (and so the digest); the canonical rows reproduce launch_admission_contract_text().

Review 57561 — four repairs on HEAD

finding repair control
LaunchIdentity unwound RoadmapNodeId / GitObjectId to String node_id: RoadmapNodeId (the node the node gate FOUND, never the request spelling), instance_id: NonEmptyStr (from HostDashboardInstance.instance_id, the as String at the belt's standing builder is gone), revision: GitObjectId (the converged oid). Three projections — launch_identity_node_id_text/instance_text/revision_hex — are the only place an identity becomes text (wire, page attrs, reason line). Decode validates the other way: empty ids and non-oid revisions refuse. witness_identity_duplicated_member_names_the_duplication (empty node_id arm)
launch_identity_member_string collapsed JsonMemberDuplicated / JsonMemberNotAnObject into none `LaunchIdentityMemberRead = MemberString MemberRefused { reason }` — absent, duplicated N times, not a string, not an object are four located reasons
LaunchGateLedger.identity: LaunchIdentity? + LaunchGateNotEvaluated { "identity" } arm — no authorable RED The ledger carries only verdicts. LaunchDecisionFacts { ledger, identity: LaunchIdentityStanding } where `LaunchIdentityBound { identity } LaunchIdentityWithheld { refusal } — the identity is a consequence of the first four gates (launch_identity_of_observedoverlaunch_revision_outcome× node lookup), and when withheld the standing carries the withholding gate's own refusal, the same value that gate's row holds. Noidentitygate exists anywhere;launch_admission_of_factsadmits fromBoundand refuses with the carried refusal otherwise.launch_gate_ledger/launch_admission_of_ledger/launch_revision_hex` deleted.
LaunchSessionMultiplicityConflict.session_names: String (comma-joined) beside LaunchDependenciesBlocked.blockers: List<String> session_names: List<String>; joined only in launch_refusal_reason witness_two_sessions_for_one_node_refuse_and_name_both counts the list

Review 5060505186 — identity binding consumes the four gates in order (repaired on HEAD 10352cd2fab83d22486856b92b6a8198fb59f532)

launch_identity_of_observed matched only the revision outcome and the node lookup, so over a fully observed standing whose instance was observe-only or whose transition was inhibited it said LaunchIdentityBound beside a refused row. Now the instance and transition gates each have a two-arm outcome (LaunchInstanceOutcome, LaunchTransitionOutcome) from which the ledger verdict is projected — the same shape the revision gate already had — and the binding consumes instance → transition → revision → node in order, withholding with the FIRST refusing outcome; row and standing carry one value because both are projected from the one outcome. Control (RED first on the unrepaired binding): witness_identity_withheld_by_instance_and_transition_gates_in_order over the existing observe-only and transition-inhibited LaunchHostObserved fixtures — facts.identity = Withheld{r} and facts.ledger.<gate> = GateRefused{r} with the same label; drift / unknown-node / positive controls kept.

Frozen join

Admission contract digest (gate order + closed refusal vocabulary + modes + causes + identity fields + occupancy rule + host stages), producer:
gunbc run --source-root dag --source-root src/v2 --entry dag/gunbc/roadmap/roadmap_launch_admission.dag --function launch_admission_contract_digest_hex
→ 8b884d701d7a356e on HEAD 4e5061c7aa9c1f4d884e757eb6442840d67d40e6 (history: 66f7026f0ec62349 → d91d73c7b9506bf5 after review 5059520727 → 5e84bbc6bf984c2c after review 5059681676: host_stages names node,contract,dependencies before sessions and the vocabulary gained no_instance → 8b884d701d7a356e on 12507414f5 after review 5060354380: the stage fragment is derived from the typed rows, spelled pre_session=…;session=… → 8b884d701d7a356e after review 57561).

An effect-ordering class the verdicts could not see (plan §5.4)

Collapsing N inline gates into "observe the host once, decide purely" preserved every VERDICT and re-ordered the EFFECTS: an observe-only instance still rendered disabled, but only after tmux / fleet-revision / transition reads the old route never performed. No verdict-level witness went red; the only red was the hermetic test.claim.roadmap.roadmap_serve_witness witness_preview_dispatch_refuses_before_effects (claim_batch's hermetic route refusing Filesystem.List). The fix is structural: LaunchHostStanding carries a LaunchHostObserveOnly { instance_id } arm and belt_launch_host_standing_for_instance short-circuits there — the standing builder decides every model-only gate before it observes anything, and the hermetic witness stays enrolled as the effect-order oracle. Rule for the next collapse of this shape: list which gates are decided from the model and which from an observation, and keep observation lazy behind the model-only gates.

No surface re-derives eligibility

  • gunbc.roadmap_belt belt_step / belt_plan / belt_json_value / roadmap_belt_status_json and the BeltAction sum are deleted; so is the reconcile's take(free) (finding 1 above) (with their nine witnesses): a retired spawn-only planner consumed by nothing in production that still selected frontier.ready under AutomaticReady — a second eligibility authority and a §3 attractor. belt_free_slots_int (the one capacity arithmetic) survives and is what the admission reads.
  • gunbc.roadmap_spawner spawn_frontier.ready remains the graph-position authority the admission's dependency gate reads (node_unmet_parent_ids) — one authority, consumed, not a re-derivation.
  • docs/plans/deploy-convergence-observed-side.md (and its .dag) cite belt_revision_admission in a dated verification receipt; left as history. Live comments in fleet_converge_cli.dag, deployed_tree_report.dag, and the fleet witness now cite launch_gate_revision.

Out of scope and untouched: live canary rows, fleet automation, compute fabric, any break-glass path.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx

Brian Searls and others added 5 commits August 29, 2026 20:47
…and the inert two-node canary

docs/plans/roadmap-launch-mvp-plan.md is committed verbatim as the controlling scope of project
RLM and enrolled in gunbc.doc_graph_roots with a PlanRetiresWhen row (retires on the RLM-6
terminal receipt).

The canary is declared once in gunbc.roadmap_launch_canary — stable minted identities for
roadmap-launch-canary / roadmap-launch-canary-followup, the one dependency edge, the ticket
criteria, and the execution contract (gunbc_claim_execution_contract over the pinned oracle) —
and is NOT a row of gunbc.roadmap_authority. The subject is one row in
gunbc.roadmap_launch_canary_sentinel (`pending:<nonce>`); the pinned oracle is
test.manual.roadmap_launch_canary_oracle, outside witness_discovery_scan_dirs so it is the
attempt's oracle and never a required-floor row (it is red on base by construction).

test.claim.roadmap_launch_canary_witness decides the synthetic graph with the live predicates
(gunbc.roadmap_spawner spawn_frontier / next_spawnable / node_unmet_parent_ids,
gunbc.roadmap_presentation work_scheduling) and enrolls every falsifier as a regression control:
edge deleted → decision changes; wrong blocker identity → refused; pending / wrong / missing
nonce → red, exact value → green; and the admission FINDING recorded rather than patched: the
Ready frontier admits an ExecutionContractUnspecified node while dispatch_environment refuses
it — RLM-1's gate.

Executed (claim_batch, arm64 build of main@a5a78c7e84, this session): 11/11 floor witnesses
PASS; the oracle FAILS on base, PASSES on the one authorized mutation (under which
witness_oracle_red_on_base_sentinel FAILS), FAILS on a wrong nonce. Nothing here changes
production behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ArofYWJTW8P6EFwLVV7tsy
…he live sentinel row; plan headings verbatim

The required-floor witness that read gunbc.roadmap_launch_canary_sentinel and asserted it still
pending would red the RLM-5 canary PR itself; red-on-base as a fact about the committed row
belongs only to the pinned oracle. Strip the leading space from the plan's heading lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ArofYWJTW8P6EFwLVV7tsy
…d before the green path

test.claim.roadmap.roadmap_launch_admission_witness (new) and its fixture module pin the plan's
RLM-1 falsifiers over the RLM-0 synthetic canary graph and a fabricated host standing: (a) the
blocked child is refused DependenciesBlocked and the timer fold over it reaches no actuation
outcome; (b) a Timer in ManualReady over a Ready node starts nothing while AutomaticReady admits
it; (c) the ledger with its dependency verdict replaced admits (the dependency gate is
load-bearing) and mutating a passing gate changes nothing; (d) revision drift refuses the page
action and the backend outcome with one label and one reason; (e) a second Operator admission
beside a running session is AlreadyLive; gate order, capacity, host-unobserved, accepted parent
unlocks child, label totality, contract digest.

The existing belt / belt_actuate / serve / presentation / page / component / canary witnesses are
re-pointed at the admission's vocabulary (BeltSpawnOutcome.SpawnNotAdmitted, SpawnMode,
LaunchRefusal labels, disabled dispatch buttons carrying data-launch), and the RLM-0 finding
finding_unspecified_contract_is_ready_but_environment_refuses becomes a regression control.

RED BY CONSTRUCTION at this commit: gunbc.roadmap_launch_admission and the surface wiring land
in the next commit; every witness here refuses to resolve until they do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
…d belt timer; ManualReady production; AvailableAsOverride and belt_step deleted

gunbc.roadmap_launch_admission answers may-THIS-cause-start-THIS-node-NOW once, purely, over an observed LaunchHostStanding and LaunchGraphStanding, in the plan's gate order (instance -> transition -> revision -> node -> size+contract -> dependencies -> live -> capacity -> cause). The daily/full page, POST /dispatch/{node_id} and belt_tick_for_instance are projections of that one decision. BeltRunState -> SpawnMode at the root, production ManualReady. AvailableAsOverride deleted at all four sites; BeltDispatchResult, BeltRevisionAdmission, belt_node_session_liveness, and the retired belt_step/belt_plan planner deleted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
…-326

# Conflicts:
#	dag/test/claim/roadmap/roadmap_launch_canary_witness_test.dag
#	docs/plans/roadmap-launch-mvp-plan.md
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 29, 2026 22:56
…predicates (review 57466 on #9696)

launch_cause_permitted was a variant-to-Bool match the gate immediately re-lifted into a LaunchGateVerdict; the gate now matches mode x cause straight to GatePassed / GateRefused. launch_verdict_passed and launch_admission_is_admitted had no consumer -- the ledger fold and every surface already eliminate the coproducts directly. Contract digest unchanged (66f7026f0ec62349).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
@gunbai-bot

gunbai-bot Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

review 57466 (codex, dashboard-only, on 2e42e0f) — addressed in dff4386:

  • launch_cause_permitted (variant→Bool, immediately re-lifted to a verdict by launch_gate_cause) is gone; launch_gate_cause now matches mode × cause directly to GatePassed / GateRefused { LaunchCauseNotPermitted }.
  • launch_verdict_passed and launch_admission_is_admitted had no consumer (the ledger fold and every surface eliminate the coproducts directly) — deleted.

Contract digest unchanged (66f7026f0ec62349); roadmap_launch_admission_witness 30/30 and roadmap_belt_actuate_witness 36/36 re-run remotely on the fixed tree.

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

RLM manager verdict: HOLD / DO NOT MERGE on exact head dff43862a25170118c5c67d95385810b85913789.

GitHub will not let the connected identity submit REQUEST_CHANGES because it is also this PR's author, so this is posted as a head-anchored review comment. The pure launch_admission fold is good, the prior variant→Bool review is folded, ManualReady is the production value, and the ordinary override is gone. Four production-boundary findings keep RLM-1 open:

  1. Capacity still selects the timer’s candidates before the shared admission. gunbc.roadmap_belt.belt_reconcile_members computes free, sets spawns = ups.take(n: free), and classifies the rest as deferred; belt_tick_for_instance sends only r.spawns to belt_tick_admit_and_spawn. Therefore not every reconcile upsert reaches launch_admission, LaunchNoCapacity is not the timer’s capacity verdict for excess rows, and deferred remains a second capacity/eligibility authority. The enrolled witness_reconcile_capacity_defers_surfaced currently pins the defect. Pass every upsert to the admission fold; let reserved decide the admitted prefix and produce typed LaunchNoCapacity outcomes for the remainder, or derive any deferred summary solely from those outcomes. Add a combined-path control: three Ready rows, cap one, AutomaticReady must exercise admission for all three; ManualReady must exercise it for all three rather than truncate before CauseNotPermitted.

  2. The live timer bypasses the shared decision for transition, session-observation, and revision refusals. belt_tick_for_instance matches transition itself, then observes tmux, then matches fleet_revision_standing itself; only the converged/sessions-observed arm constructs LaunchHostObserved and calls launch_admission. Drift, unobserved revision, transition inhibition, and session-observation failure return tick-local string refusals with no LaunchRefusal/SpawnNotAdmitted. This also reads tmux before the revision gate. witness_drift_refuses_ui_and_backend_with_one_reason drives belt_tick_admit_and_spawn directly with a fabricated drifted standing, so it remains green if the real driver keeps bypassing the gate. Make the real page/POST/timer drivers consume staged host standing through the one admission, with later observations lazy behind earlier gates, while verify/publish continue independently. Enrol real-driver hermetic controls for transition/revision/session-observation refusals and exact label/reason parity.

  3. Unknown per-session liveness is counted as zero capacity. launch_running_count maps WorkerProcessUnobserved to false. For an unobserved session belonging to another node, the candidate’s launch_gate_live passes and the capacity gate undercounts a process that may be running, so a launch can be admitted under unknown occupancy. Unobserved may not mean absent. Either refuse capacity standing when any capacity subject is unobserved, or conservatively count that present session; add a discriminating control with cap one, one unrelated WorkerProcessUnobserved session, and a Ready candidate.

  4. The admitted decision identity is constructed and then discarded by every surface. Presentation matches LaunchAdmitted { identity: _ }; POST and timer do the same. BeltSpawnOutcome carries only node/session/provider or failure fields, so neither the dispatch response nor the durable tick receipt contains the LaunchIdentity {node,cause,mode,instance,revision} that RLM-1’s exit requires all three surfaces to project and RLM-4 must join to the attempt. Carry the exact admitted identity through the page’s decided action/evidence and through admitted Spawned/SpawnFailed outcomes and durable receipts; add a parity control proving page, POST, and timer bind the same identity fields for the same facts. The client must not use the rendered identity as authorization—the POST still re-observes—but the receipt cannot throw it away.

The RLM-0 modeled join is complete (92ef80595cd8e8a4, 6b24746949c96306), the four exact-head GitHub checks are green, and the one RLM-0 file in this effective diff (roadmap_launch_canary_witness_test.dag) is a justified RLM-1 conversion of F3 into a regression control. Dashboard approval 57470 is reported on this head but is outside the GitHub connector I can inspect.

Re-review bar: repair the four boundaries, add the real-driver/multi-candidate/unknown-capacity/identity controls, rerun all four checks and dashboard review on one exact head, and re-derive launch_admission_contract_digest_hex if the closed contract changes.

Brian Searls and others added 3 commits August 30, 2026 00:40
…city, staged real-driver refusals, unknown occupancy, identity parity

Enrolled before the repairs land so each is red against dff4386's shapes: (1) three Ready rows against one slot must all reach the admission; (2) the timer's launch pass over transition-halt / revision-drift / sessions-unobserved standings must refuse every desired node with the shared label and reason the page and route name; (3) a present-but-unobserved process must occupy its slot; (4) page, POST and timer must bind one LaunchIdentity, carried in the route JSON and round-tripped through the durable tick receipt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
1. gunbc.roadmap_belt belt_reconcile returns EVERY upsert (free/deferred/take deleted); the timer's belt_tick_admit folds all of them through the admission with reserved counting admitted launches, so excess rows are typed LaunchNoCapacity and the deferred count is derived from outcomes.
2. One STAGED observer (belt_launch_host_standing_for_instance) behind page, POST and timer: transition read only for an actuating instance, revision only under an admitted transition, tmux only under a converged revision; LaunchHostHalted has no later fields. belt_tick_for_instance = observer -> pure belt_tick_launch_pass -> actuation; tick-local string refusals replaced by host_refusal: LaunchRefusal?.
3. launch_occupying_count: a present-but-unobserved process occupies its slot; only proven exited/absent frees it.
4. Spawned/SpawnFailed carry the admitted LaunchIdentity; it is persisted in the attempt state (launch-identity.json), the POST JSON, the durable tick receipt (launches, schema v3) and rendered as data-launch-* on the page action.

Contract text now names identity fields, occupancy rule and host stages, so the digest changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
… roster), plus the identity-survives-actuation-failure control

The launch pass matched the standing with a wildcard arm, which the non-fold-residue roster refuses for a closed coproduct parameter; every arm is now named and each unreachable one refuses fail-closed through the shared vocabulary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Review 5059520727 (HOLD on dff43862a2) — all four boundaries repaired on c19f7c50ffa820ad729b838fd00e54feb2195a7c; controls landed first in bd6f9c369a (red against the reviewed shapes), repairs in 80eed0c04c, exhaustive-match/NFR fix in c19f7c50ffa820ad729b838fd00e54feb2195a7c.

  1. Capacity pre-selection — belt_reconcile now returns every upsert (free/deferred/take deleted); belt_tick_admit folds all of them through launch_admission with reserved, so excess rows are typed LaunchNoCapacity and the tick's deferred count is derived from outcomes. Controls: 3 Ready / cap 1 — AutomaticReady admits Add SVG viz, test helpers, and makegen scaffold #1 (reserved→1) and answers LaunchNoCapacity{1 of 1} for Codex/graph viz test helpers #2/. #3; ManualReady answers CauseNotPermitted for all three.
  2. Staged observation through the shared gate — one observer belt_launch_host_standing_for_instance behind page/POST/timer: transition read only for an actuating instance, revision only under an admitted transition, tmux only under a converged revision; a halt is LaunchHostHalted { gate, refusal } with no later fields (no constructor for tmux-under-inhibited-transition), refusal derived by the admission's own gate fns. belt_tick_for_instance = observer → pure belt_tick_launch_pass → actuation; tick-local string refusals deleted, BeltTickResult.host_refusal: LaunchRefusal?. Controls run the real pass over staged standings built by the production producers (transition halt / revision drift / sessions unobserved): every desired node withheld with the shared label+reason the page and route name; verify/publish continue under a host refusal (receipt-layer control), except under a transition halt (the deploy's wholesale refusal, unchanged).
  3. Unknown liveness — launch_occupying_count: a present-but-unobserved process occupies its slot; only proven exited/absent frees it (declared in launch_occupancy_note, named in the contract text). Controls: cap 1 + one unrelated WorkerProcessUnobserved → LaunchNoCapacity; exited/absent → admitted.
  4. Identity carriage — Spawned/SpawnFailed carry launch: LaunchIdentity from the admission site through every actuation step; persisted as launch-identity.json in the attempt state, emitted in the POST JSON and the durable tick receipt (launches, schema v3, decoded fail-closed), rendered as data-launch-* on the page action (evidence, not authorization). Controls: page/POST/timer parity (same node/mode/instance/revision, causes operator/operator/timer), JSON carriage, receipt round-trip, identity survives actuation failure.

Contract digest re-derived on HEAD: launch_admission_contract_digest_hex → d91d73c7b9506bf5 (was 66f7026f0ec62349; text gained identity=…, occupancy=…, host_stages=…). Details and the full control roster are in the PR body.

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

RLM manager verdict: HOLD / DO NOT MERGE on exact head c19f7c50ffa820ad729b838fd00e54feb2195a7c.

The four findings from review 5059520727 are materially repaired: every upsert reaches admission, unobserved processes occupy capacity, the real timer uses the shared host refusal vocabulary, and admitted identity survives page/POST/timer outcomes and receipts. Two boundary defects remain.

  1. A transition-halted tick persists false Recorded verdicts for verification and publication. belt_tick_for_instance deliberately sets verify_outcomes: [] and publish_outcomes: [] on LaunchHostHalted { gate: HostTransitionGate, ... }. But belt_tick_receipt_from_result feeds those empty lists to belt_verify_pass_outcome_from and belt_publish_pass_outcome_from; both empty folds return BeltPassRecorded. The durable v3 receipt therefore says verification and publication were recorded when the driver intentionally skipped them. I accept the transition exception itself as the deploy's stronger atomic-wholesale refusal, but its receipt must say Refused, with the exact transition reason, never Recorded. Add a typed transition-pass override (or explicit refused outcomes) and controls proving: transition halt => all four passes refused; revision/session launch refusal => verify/publish remain independently derived and may still record.

  2. Session observation is still eager with respect to the node/contract/dependency gates. belt_launch_host_standing_for_instance_with_capacity calls belt_sessions_observe_for_instance immediately after revision convergence, before launch_admission evaluates NodeUnknown / accepted / review / size / execution contract / dependencies. Thus a blocked or contractless POST still executes tmux observation even though the declared order is node→contract→dependencies→live, and the PR's own effect-order rule says later observations stay lazy behind model-only gates. LaunchHostHalted closes transition/revision ordering, but there is still no structural state for “pre-session gates passed; now observe sessions.” Split the standing/decision into a pre-session stage and a session-completed stage (the page may evaluate all rows first and perform one shared session observation only when at least one row reaches the live gate). Enrol real-driver hermetic controls for unknown, contract-missing, and dependencies-blocked cases proving tmux is unreachable.

Non-blocking cleanup while touching the area: update live notes that still say belt_revision_admission, Running state, or roadmap-belt-tick-receipt/v2; the implementation has deleted/changed all three.

Re-review bar: repair both boundaries; add the transition-receipt and pre-session effect-order controls; rerun all four GitHub checks and dashboard review on one exact head; re-derive launch_admission_contract_digest_hex because the host-stage contract should change. RLM-2 remains closed.

Brian Searls and others added 6 commits August 30, 2026 01:49
…ipt refuses all four passes; session observation unreachable behind node/contract/dependency gates

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
1. A transition-halted tick records TickPassesWithheld{refusal} and the durable receipt derives all four passes as Refused with the transition reason (never Recorded for passes the driver skipped); non-transition host refusals withhold only the launch passes.
2. Session observation is the last observation and lazy behind every model-only gate: the staged observer yields LaunchSessionsPending; launch_admission_staged decides instance..dependencies first and answers LaunchStageNeedsSessions only for a node that passed them; POST completes for that node, the page completes once when any row needs it, the timer once for its reap pass. A pending set reaching the full decision refuses at the live gate.

Stale notes (belt_revision_admission, Running state, receipt v2) corrected. Contract text: host_stages now names node,contract,dependencies before sessions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
… arm, graph resolved once per standing, serve status note rewritten

- LaunchGateRow carries its stage (PreSessionGate | SessionGate) at the one place rows are authored; launch_admission_staged partitions by the mark, not by take(6).
- A render with no actuating instance refuses LaunchNoActuatingInstance { reason } (label no_instance, 403) instead of fabricating an empty instance id.
- LaunchGraphProjected carries nodes and edges flattened once (launch_graph_of_document); the per-row lookup no longer re-walks the document.
- belt_dispatch_status_note names the live status fold over LaunchRefusal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Review 5059681676 (HOLD on c19f7c50ff) and review 57491 — addressed on 2a4e3010b90b2625f88965a7d011a9e0d5cdc093 (controls first in 4771cd3dd2, repairs 3426284a0e, main merged at 6d879e1ebf which dissolved the six stale wave-admission rows CI hit on the earlier merge commit, review-57491 items 2a4e3010b9).

  1. Transition-halted receipt — BeltTickResult.wholesale: TickPassesRun | TickPassesWithheld { refusal }; the receipt derives all four passes as Refused with the transition reason under a halt, never Recorded; a revision/session refusal withholds only the launch passes and verify/publish stay independently derived. Controls: transition_halted_tick_refuses_all_four_passes, revision_refusal_keeps_verify_independently_recorded.
  2. Pre-session stage — observer yields LaunchSessionsPending; launch_admission_staged decides the pre-session gates (each ledger row carries a typed LaunchGateStage) and answers LaunchStageNeedsSessions only when a node passed them; POST completes for that node, the page once when any row needs it, the timer once for reaping. Real route decision path over the canary graph under the hermetic route: unknown / contractless / blocked / transition-halt all refuse with tmux unreachable.

Review 57491: typed stage instead of take(6); LaunchNoActuatingInstance { reason } replaces the empty instance id; LaunchGraphProjected flattened once per standing (launch_graph_of_document; the residual per-row linear find is named in launch_graph_resolved_note); belt_dispatch_status_note rewritten. Not taken: folding reserved across page rows — reserved counts launches admitted within one actuation fold; page rows are independent operator decisions and the route admits any single click, so a folded page would refuse rows the route admits.

Stale notes (belt_revision_admission, Running state, receipt v2) corrected. Digest re-derived on HEAD: launch_admission_contract_digest_hex → 5e84bbc6bf984c2c.

…ace the variant-to-Bool predicates

belt_spawn_ok / belt_spawn_failed / belt_spawn_not_admitted and the NoCapacity filter fold into BeltSpawnTally; launch_ledger_stages partitions rows by their declared stage; the page's sessions-need check folds to BeltPageSessionsNeed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

review 57498 (codex) — addressed in ee617ee17e30e2b6ca4ee7f79d358cfc167f3e15: belt_spawn_ok / belt_spawn_failed / belt_spawn_not_admitted and the NoCapacity filter are replaced by one fold, belt_spawn_tally → BeltSpawnTally { spawned, failed, not_admitted, deferred }, whose fields every tick count reads; the ledger stage filter is a partition fold, launch_ledger_stages → LaunchLedgerStages { pre_session, session }; the page's sessions-need check folds to BeltPageSessionsNeed. No variant-to-Bool fn was added by this PR remains (belt_teardown_ok, belt_tick_is_*, dispatch_band_is_ok predate it). Contract text unchanged, digest unchanged.

@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

review 57506 (claude, APPROVE) — agreed on the one nit: the host_stages=… clause in launch_admission_contract_text is a hand-typed literal beside derived rosters, so a renamed/reordered host stage would leave the digest silent, contradicting the note above it. The right shape is to derive it from the typed LaunchGateStage marks the ledger rows already carry (launch_ledger_stages → the pre-session / session gate names), which also retires the literal. Not pushed on this head: the PR is under an exact-head side-chat re-review with a hold on further pushes; it lands with the next requested-changes round, or as an immediate follow-up if this head merges (digest will change once more when it does).

— sent from fierce-badger-326

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

RLM manager verdict: HOLD / DO NOT MERGE on exact head ee617ee17e30e2b6ca4ee7f79d358cfc167f3e15.

GitHub will not let the connected identity submit REQUEST_CHANGES because it is also this PR's author, so this is a head-anchored review comment.

The two 5059681676 boundaries are closed

  1. Transition-wholesale receipts — closed. BeltTickResult.wholesale = TickPassesRun | TickPassesWithheld { refusal } preserves the accepted transition-only wholesale exception. belt_tick_receipt_from_result derives spawn, teardown, verify, and publish as BeltPassRefused with that same transition reason under TickPassesWithheld; revision and other non-transition host refusals remain TickPassesRun, so verify/publish retain their independent evidence. The transition and revision controls discriminate those two cases.

  2. Session observation ordering — closed. The staged observer now yields LaunchSessionsPending; typed LaunchGateStage marks put instance/transition/revision/node/contract/dependencies before the session boundary; launch_admission_staged returns LaunchStageNeedsSessions only after those gates pass. POST completes only that node, the page completes once only if some row needs sessions, and the timer completes once for its reap/decision pass. The unknown-node, missing-contract, and blocked-child controls drive the production staged dispatch path under the hermetic effect boundary, so moving completion ahead of those verdicts would enter tmux and change the result rather than remain green.

The declined page-wide reserved fold is accepted. Page rows are independent hypothetical operator decisions at one snapshot; the route re-observes and admits one clicked row with reserved = 0. Reserving across render order would make later buttons refuse work that the route would admit as the one click.

Evidence bar satisfied on this head

  • All four exact-head check runs are successful.
  • The tested GitHub merge ref is 2a65286250e202032bcdc2c713db25156484c00f, merging this head into 030ba3a6df57447c60d7f651db56ab737caa6fea; that base contains the three newer-main commits reported in the handoff, including imported-coproduct exhaustiveness. The stale branch pointer is therefore non-blocking for this review. Later main churn has no direct RLM file overlap.
  • The 505968 controls landed before their implementation repair (4771cd3dd2a8f2ecedd74bd7620e42701c943d8a before 3426284a0eed99f468e17e6c0d3ab3e69fa6ce4d).
  • Dashboard review 57506 is an APPROVE; 57498's requested folds are present on this head.
  • I independently reconstructed launch_admission_contract_text and the repository's atom_identity_hash / NUL-separated hash_combine / tagged structural pipeline. It re-derives exactly 5e84bbc6bf984c2c.

One final frozen-join blocker

launch_admission_contract_text still authors this literal beside the typed stage authority:

;host_stages=instance,transition,revision,node,contract,dependencies,sessions

That makes the current value and digest correct, but it does not make the session-order repair load-bearing in the frozen join. Reclassifying a canonical ledger row from PreSessionGate to SessionGate, or changing the typed stage partition/order, can leave this literal—and therefore the contract digest—unchanged. That is precisely the boundary RLM-1 just repaired. It also contradicts launch_admission_contract_note's claim that contract changes are derived from the rosters rather than prose.

Review 57506 identified the same defect and proposed the right direction; I am grading it as blocking here because this PR is creating the frozen admission-contract join, not merely displaying a diagnostic string.

Required repair: derive the host-stage serialization from the canonical LaunchGateRow.stage marks through launch_ledger_stages (or one equivalent typed roster), with the sessions observation boundary represented explicitly. Do not replace this with another synchronized literal. Add one mutation control proving that reclassifying or reordering a canonical stage row changes the serialized contract/digest (or refuses the claimed equality). Then rerun the four checks and one dashboard review on one exact head and re-derive the digest.

No other implementation finding remains. RLM-1 stays the sole active mutating gate; RLM-2 remains unopened until this final contract producer is structural and the merge join is recorded.

Brian Searls and others added 4 commits August 30, 2026 08:48
…rojection of the typed LaunchGateRow.stage marks

launch_gate_template reads the canonical rows once from launch_ledger_rows; gate order, pre_session and session rosters in launch_admission_contract_text are derived from it (the authored host_stages literal is gone). Mutation control: reclassifying one row across the boundary, or reordering the roster, changes the serialized contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
… identity as a gate consequence, listed session names

- LaunchIdentity keeps its authorities' types: node_id: RoadmapNodeId (the node the
  node gate found), instance_id: NonEmptyStr (from the dashboard instance, the belt's
  `as String` is gone), revision: GitObjectId. Three text projections are the only
  place an identity is spelled; decode refuses empty ids and non-oid revisions.
- launch_identity_member_string returns LaunchIdentityMemberRead: absent, duplicated
  N times, not a string, not an object are four located reasons instead of one none.
- LaunchGateLedger carries only verdicts. LaunchDecisionFacts { ledger, identity:
  LaunchIdentityBound | LaunchIdentityWithheld { refusal } }: the identity is a
  consequence of the first four gates (launch_identity_of_observed over
  launch_revision_outcome x node lookup); when withheld the standing carries the
  withholding gate's own refusal. The `identity` pseudo-gate, launch_gate_ledger,
  launch_admission_of_ledger and launch_revision_hex are deleted.
- LaunchSessionMultiplicityConflict.session_names: List<String>.
- Controls: witness_identity_duplicated_member_names_the_duplication,
  witness_withheld_identity_carries_the_withholding_gates_refusal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Review 57561 — four findings, four repairs on 4e5061c7aa (main merged in; commit efd11d9568):

  1. Unit-modeling of LaunchIdentity — node_id: RoadmapNodeId (the node the node gate FOUND in the projected authority, never the request's spelling), instance_id: NonEmptyStr (straight from HostDashboardInstance.instance_id; the belt's as String at the standing builder is gone), revision: GitObjectId (the converged oid). launch_identity_node_id_text / _instance_text / _revision_hex are the only places an identity becomes text (wire, page attributes, reason line); decode validates the other way and refuses empty ids and non-object-id revisions.
  2. Absorbing launch_identity_member_string — returns LaunchIdentityMemberRead = MemberString | MemberRefused { reason }; absent, duplicated N times, not a string, not an object are four located reasons. Control: witness_identity_duplicated_member_names_the_duplication (doubled node_id → "appears 2 times"; non-object document; empty id).
  3. Decorative identity arm — the ledger now carries only verdicts. LaunchDecisionFacts { ledger, identity: LaunchIdentityBound { identity } | LaunchIdentityWithheld { refusal } }: the identity is a consequence of the first four gates (launch_identity_of_observed over launch_revision_outcome × node lookup), and when withheld the standing carries the withholding gate's own refusal — the same value that gate's row holds — so no identity gate exists and no state says "every gate passed, no identity". launch_gate_ledger, launch_admission_of_ledger, launch_revision_hex deleted. Control: witness_withheld_identity_carries_the_withholding_gates_refusal (drifted → deployed_revision_drifted in both the standing and the revision row; unknown node → node_not_found; clear → bound to the found node).
  4. session_names: String → List<String>, joined only in launch_refusal_reason; witness_two_sessions_for_one_node_refuse_and_name_both counts the list.

Executed (claim_batch, hermetic route): admission 51/51 (the new withheld control was RED first on a wrong-label spelling, then green), belt actuate 102 pass + the 7 pre-existing wet belt_exec rows, serve 16/16, presentation 27/27; nfr_roster_receipt ok. Contract digest launch_admission_contract_digest_hex → 8b884d701d7a356e, unchanged from 12507414f5: none of the repairs moved the gate order or the refusal vocabulary.

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

RLM manager verdict: HOLD / DO NOT MERGE on exact head 4e5061c7aa9c1f4d884e757eb6442840d67d40e6.

Review 5060354380 is fully repaired. launch_gate_template() now reads the canonical LaunchGateRow roster, launch_rows_stages is the sole typed partition used by both staged admission and contract serialization, and witness_contract_binds_the_session_boundary discriminates both a stage reclassification and row reorder. I independently reconstructed the 746-byte contract text and the repository's tagged FNV-1a pipeline; the result is exactly 8b884d701d7a356e. All four GitHub checks are successful on this head, and current main's one later commit has no RLM/roadmap overlap.

One construction defect introduced by the review-57561 identity refactor remains:

LaunchIdentityStanding can be Bound beside an earlier refused instance or transition gate. The source note and PR body say the identity is bound exactly when the first four gates pass and otherwise carries the refusal of the gate that withheld it. But launch_identity_of_observed examines only launch_revision_outcome(revision) and the node lookup. launch_decision_facts calls it for every LaunchHostObserved, independently of that standing's actuation and transition fields.

That disagreement is directly writable in the PR's own fixtures. launch_fixture_standing_observe_only() constructs LaunchHostObserved { actuation: DashboardObserveOnly, transition: ActuationAdmitted, revision: converged, ... }; launch_fixture_standing_transition_inhibited() constructs LaunchHostObserved { actuation: DashboardActuating, transition: ActuationRefused, revision: converged, ... }. For a found node, both produce LaunchIdentityBound, while the same LaunchDecisionFacts.ledger respectively carries instance = GateRefused{disabled} or transition = GateRefused{deployment_transition_inhibited}. The existing identity control covers drift, unknown node, and clear standing, but not these first two gates.

launch_admission_of_facts folds the ledger first, so the current production admission still refuses and this is not a live actuation fail-open. The defect is nevertheless blocking for the exact construction claim just adopted: a reusable fact carrier can say an identity is bound beside the gate said to have withheld it, and a future consumer can read facts.identity without repeating the ledger fold.

Required repair: either (a) make identity binding consume instance → transition → revision → node outcomes in that order and carry the exact first refusal, or (b) refine the host-stage carrier so the observed arm supplied to identity binding structurally contains only an actuating instance, admitted transition, converged revision, and found node. Add discriminating controls over the existing observe-only and transition-inhibited LaunchHostObserved fixtures proving LaunchIdentityWithheld carries the same refusal as the corresponding ledger row. Keep the drift/unknown/clear controls. Then rerun four GitHub checks and one dashboard review on one exact head and re-derive the digest; the digest may legitimately remain 8b884d701d7a356e if the serialized contract is unchanged.

No other finding remains. RLM-1 stays the sole mutating gate; RLM-2 remains unopened.

Brian Searls and others added 2 commits August 30, 2026 10:46
…nsition -> revision -> node in order

launch_identity_of_observed matched only the revision outcome and the node lookup, so a
fully observed standing with an observe-only instance or an inhibited transition bound an
identity beside a refused ledger row. The instance and transition gates now have two-arm
outcomes (LaunchInstanceOutcome, LaunchTransitionOutcome) from which their ledger verdicts
are projected, and the binding consumes the four outcomes in gate order, withholding with
the first refusing one -- row and standing carry one value because both are projected
from the one outcome. Control (RED first):
witness_identity_withheld_by_instance_and_transition_gates_in_order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnoHoT3sCrCSocxd86qvwx
@gunbai-bot

gunbai-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Review 5060505186 — repaired on 10352cd2fa (commit 3f6b6073da, main 45bb2f7174 merged in, no roadmap overlap).

launch_identity_of_observed consumed only the revision outcome and the node lookup, so over a fully observed standing with an observe-only instance or an inhibited transition it said LaunchIdentityBound beside a refused row — exactly as you traced with launch_fixture_standing_observe_only / _transition_inhibited. Now the instance and transition gates each have a two-arm outcome (LaunchInstanceOutcome = Actuating | Refused{refusal}, LaunchTransitionOutcome = Clear | Refused{refusal}) from which the ledger verdict is projected — the shape the revision gate already had — and the binding consumes instance → transition → revision → node in order, withholding with the FIRST refusing outcome. Row and standing hold one value because both are projected from the one outcome.

Control, RED first: witness_identity_withheld_by_instance_and_transition_gates_in_order over the two existing fixtures asserts facts.identity = Withheld{r} and facts.ledger.instance / .transition = GateRefused{r} with the same label (disabled, deployment_transition_inhibited). Against the unrepaired binding: 51 PASS, 1 FAIL (this one). After: admission 52/52; belt actuate 102 + the 7 pre-existing wet rows. Drift / unknown-node / positive controls kept. Digest re-derivation on this head is in flight (expected to stay 8b884d701d7a356e — no gate or vocabulary moved); I'll amend the body if it differs.

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

RLM manager verdict: APPROVED / MERGE AUTHORIZED on exact head 10352cd2fab83d22486856b92b6a8198fb59f532.

Review 5060505186 is closed. The prior inconsistency was real: launch_identity_of_observed could bind beside a refused instance or transition row. This head repairs the producer at the right boundary:

  • LaunchInstanceOutcome and LaunchTransitionOutcome are the sole two-arm decision authorities for those gates; launch_gate_instance / launch_gate_transition project their ledger verdicts from them.
  • Identity binding now consumes instance → transition → revision → node in that order and returns LaunchIdentityWithheld at the first refusing outcome. It cannot bind for either of the two formerly constructible counterexamples.
  • witness_identity_withheld_by_instance_and_transition_gates_in_order drives the existing fully-observed observe-only and transition-inhibited fixtures and joins the withheld identity to the corresponding refused ledger row. The reported red-first result (51 pass / this control red before repair; 52/52 after) discriminates the defect. The drift, unknown-node, and admitted positive controls remain.

Evidence bar:

  • All four GitHub checks (witnesses, required-witnesses-floor, required-witnesses-build, rust-unit-tests) completed successfully on this exact head against base 45bb2f7174d907bd8e5efabb808dcd51a2129f84.
  • Dashboard review 57571 is reported APPROVE on this exact head, with no current-head requested changes.
  • The contract-producing roster and serialization are untouched by this repair. I independently reconstructed the 746-byte contract and the repository's tagged FNV-1a pipeline again: contract atom a8ee2100e45ca0ab, tag atom 874e2dde5a8502ac, final digest 8b884d701d7a356e.
  • Main advanced once after these checks to df1d9ac1d0bbf9bcf732240752e7d6d7de575b32; that commit's net diff contains no RLM/roadmap path and does not alter the contract/hash source. Under the same non-overlap rule used in the preceding reviews, this is non-blocking. GitHub still reports the PR mergeable.

Merge authority: merge only while the PR head remains exactly 10352cd2fab83d22486856b92b6a8198fb59f532. Record the actual resulting merge commit and its tree—not the pre-advance synthetic merge ref—together with admission contract digest 8b884d701d7a356e. That recorded tuple closes RLM-1.

RLM-2 may be opened only after that join is recorded. Bring its exact-revision srv1 convergence / live ManualReady / one-RoadmapLaunchDeploymentReceipt gate brief here before dispatch. No other finding remains.

@gunbai-bot
gunbai-bot Bot merged commit 0beef8a into main Aug 30, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/fierce-badger-326 branch August 30, 2026 11:46
@briansrls
briansrls restored the session/fierce-badger-326 branch August 30, 2026 11:47
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