Repository navigation
RLM-1: one shared serial launch admission consumed by page, POST /dispatch, and belt timer; ManualReady production mode; remove ordinary AvailableAsOverride - #9696
Conversation
…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
…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
|
review 57466 (codex, dashboard-only, on 2e42e0f) — addressed in dff4386:
Contract digest unchanged ( |
briansrls
left a comment
There was a problem hiding this comment.
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:
-
Capacity still selects the timer’s candidates before the shared admission.
gunbc.roadmap_belt.belt_reconcile_memberscomputesfree, setsspawns = ups.take(n: free), and classifies the rest asdeferred;belt_tick_for_instancesends onlyr.spawnstobelt_tick_admit_and_spawn. Therefore not every reconcile upsert reacheslaunch_admission,LaunchNoCapacityis not the timer’s capacity verdict for excess rows, anddeferredremains a second capacity/eligibility authority. The enrolledwitness_reconcile_capacity_defers_surfacedcurrently pins the defect. Pass every upsert to the admission fold; letreserveddecide the admitted prefix and produce typedLaunchNoCapacityoutcomes 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 beforeCauseNotPermitted. -
The live timer bypasses the shared decision for transition, session-observation, and revision refusals.
belt_tick_for_instancematches transition itself, then observes tmux, then matchesfleet_revision_standingitself; only the converged/sessions-observed arm constructsLaunchHostObservedand callslaunch_admission. Drift, unobserved revision, transition inhibition, and session-observation failure return tick-local string refusals with noLaunchRefusal/SpawnNotAdmitted. This also reads tmux before the revision gate.witness_drift_refuses_ui_and_backend_with_one_reasondrivesbelt_tick_admit_and_spawndirectly 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. -
Unknown per-session liveness is counted as zero capacity.
launch_running_countmapsWorkerProcessUnobservedtofalse. For an unobserved session belonging to another node, the candidate’slaunch_gate_livepasses 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 unrelatedWorkerProcessUnobservedsession, and a Ready candidate. -
The admitted decision identity is constructed and then discarded by every surface. Presentation matches
LaunchAdmitted { identity: _ }; POST and timer do the same.BeltSpawnOutcomecarries only node/session/provider or failure fields, so neither the dispatch response nor the durable tick receipt contains theLaunchIdentity {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 admittedSpawned/SpawnFailedoutcomes 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.
…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
|
Review 5059520727 (HOLD on
Contract digest re-derived on HEAD: |
briansrls
left a comment
There was a problem hiding this comment.
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.
-
A transition-halted tick persists false
Recordedverdicts for verification and publication.belt_tick_for_instancedeliberately setsverify_outcomes: []andpublish_outcomes: []onLaunchHostHalted { gate: HostTransitionGate, ... }. Butbelt_tick_receipt_from_resultfeeds those empty lists tobelt_verify_pass_outcome_fromandbelt_publish_pass_outcome_from; both empty folds returnBeltPassRecorded. 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. -
Session observation is still eager with respect to the node/contract/dependency gates.
belt_launch_host_standing_for_instance_with_capacitycallsbelt_sessions_observe_for_instanceimmediately after revision convergence, beforelaunch_admissionevaluates 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.LaunchHostHaltedcloses 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.
…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
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
|
Review 5059681676 (HOLD on
Review 57491: typed stage instead of Stale notes ( |
…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
|
review 57498 (codex) — addressed in |
|
review 57506 (claude, APPROVE) — agreed on the one nit: the — sent from fierce-badger-326 |
briansrls
left a comment
There was a problem hiding this comment.
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
-
Transition-wholesale receipts — closed.
BeltTickResult.wholesale = TickPassesRun | TickPassesWithheld { refusal }preserves the accepted transition-only wholesale exception.belt_tick_receipt_from_resultderives spawn, teardown, verify, and publish asBeltPassRefusedwith that same transition reason underTickPassesWithheld; revision and other non-transition host refusals remainTickPassesRun, so verify/publish retain their independent evidence. The transition and revision controls discriminate those two cases. -
Session observation ordering — closed. The staged observer now yields
LaunchSessionsPending; typedLaunchGateStagemarks put instance/transition/revision/node/contract/dependencies before the session boundary;launch_admission_stagedreturnsLaunchStageNeedsSessionsonly 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 into030ba3a6df57447c60d7f651db56ab737caa6fea; 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 (
4771cd3dd2a8f2ecedd74bd7620e42701c943d8abefore3426284a0eed99f468e17e6c0d3ab3e69fa6ce4d). - Dashboard review 57506 is an APPROVE; 57498's requested folds are present on this head.
- I independently reconstructed
launch_admission_contract_textand the repository'satom_identity_hash/ NUL-separatedhash_combine/ tagged structural pipeline. It re-derives exactly5e84bbc6bf984c2c.
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.
…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
|
Review 57561 — four findings, four repairs on
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 |
briansrls
left a comment
There was a problem hiding this comment.
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.
…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
|
Review 5060505186 — repaired on
Control, RED first: |
briansrls
left a comment
There was a problem hiding this comment.
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:
LaunchInstanceOutcomeandLaunchTransitionOutcomeare the sole two-arm decision authorities for those gates;launch_gate_instance/launch_gate_transitionproject their ledger verdicts from them.- Identity binding now consumes
instance → transition → revision → nodein that order and returnsLaunchIdentityWithheldat the first refusing outcome. It cannot bind for either of the two formerly constructible counterexamples. witness_identity_withheld_by_instance_and_transition_gates_in_orderdrives 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 base45bb2f7174d907bd8e5efabb808dcd51a2129f84. - Dashboard review
57571is 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 atom874e2dde5a8502ac, final digest8b884d701d7a356e. - 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.
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/mainis merged in (merge commit, no rebase).RLM-0 frozen join (recorded by the manager from producers, 2026-08-29)
f99ac746bee4a784945fb40e85e4547c0827e26d(squash of #9694 headc59d800c26c10124c64a63531a679117690750b8)2272c7137e340cd922b03c85373a68a7585b4f50dag/gunbc/roadmap/roadmap_launch_canary.dag9dea489a2212c4514f59050442b131110604e59a— identical on this headdag/test/manual/roadmap_launch_canary_oracle_test.dag(pin identity =validation_oracle_source_identityover it)79376194dafc2704b5e3e47ed87caa5820be6fa3— identical on this headdocs/plans/roadmap-launch-mvp-plan.md86ac055c6e4482f649902826869839fc8872d49a— identical on this head (the merge took main's plan verbatim)validation_oracle_source_identityover the pinned oracle, viabelt_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)gunbc run --source-root dag --source-root src/v2 --entry dag/gunbc/roadmap/roadmap_launch_canary.dag --function roadmap_launch_canary_criteria_digest_hex→92ef80595cd8e8a4on this head, equal to the joinThe one decision
gunbc.roadmap_launch_admission(new) answers may THIS cause start THIS node NOW? once, purely, over two observed inputs — aLaunchHostStanding(instance posture, transition admission, fleet revision standing, live session set, capacity, spawn mode) and aLaunchGraphStanding(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 modeDeviations 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).Unplannedis not minted: ingunbc.roadmap_spawneran unplanned node is an unsized one, so it is one fact (LaunchSizeMissing) withSchedulingUnplannedas its presentation.LaunchCauseNotPermittedcarries 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
gunbc.roadmap_presentationdispatch_action(lifecycle, admission)viarow_launch_admission(causeOperator); host standing observed once per served render bygunbc.roadmap_belt_actuatebelt_launch_host_standingand threaded byroadmap_pageActionAvailable/PrimaryAction; refused →ActionUnavailable/SecondaryAction, button rendereddisabledwithdata-launch=<wire label>andtitle=<reason>.AvailableAsOverrideis deleted (all 4 emission sites). Static renders (static site, surface probe, fixture pages) passlaunch_host_unobserved_static_renderand refuse at the instance gate — no actuating instance stands behind those bytes.POST /dispatch/{node_id}gunbc.roadmap_belt_actuatebelt_dispatch_node_for_instance→launch_admission(cause: Operator, …)LaunchAdmitted.BeltDispatchResult(14 arms) is deleted; the route returns `BeltSpawnOutcome = Spawnedbelt_tick_for_instance→belt_tick_admit_and_spawnfolds every reconcile upsert throughlaunch_admission(cause: Timer, …)withreserved= launches already admitted this tickSpawnNotAdmitted; the tick's private stale/unobserved/conflict deferral filter andbelt_node_session_liveness/BeltRevisionAdmissionare deleted — those facts are the admission's gates now.ManualReady is production
gunbc.roadmap_beltroadmap_belt_production_spawn_mode: SpawnMode = ManualReady(wasroadmap_belt_default_run_state = Running).BeltRunStateandSpawnModewere one concept and are migrated at the root;belt_reconcileruns in both non-paused modes (teardowns of departed members continue in ManualReady, upserts are planned and then refusedCauseNotPermittedby 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_witnessover the RLM-0 synthetic canary graph and a fabricated host (test.claim.roadmap.roadmap_launch_admission_fixture):witness_blocked_child_refused_dependencies_blocked+witness_blocked_child_fold_reaches_no_actuation— the tick fold over the blocked child yields exactly oneSpawnNotAdmittedand noSpawned/SpawnFailed(actuation in a witness env would answerSpawnFailedat clock/footprint; its absence is the effect-ordering receipt). The route shares the identicalLaunchAdmitted =>guard.witness_timer_in_manual_ready_refused_cause_not_permitted,witness_timer_fold_in_manual_ready_starts_nothing, positive controlwitness_timer_in_automatic_ready_is_admitted; reconcile-layertest.claim.roadmap_belt_witnesswitness_reconcile_manual_ready_still_reaps_and_plans.witness_dependency_gate_is_load_bearing(ledger with dependencies := GatePassed admits) +witness_mutating_a_passing_gate_changes_nothing.witness_drift_refuses_ui_and_backend_with_one_reason— pageWorkAction.label/detail== tick outcome label/reason; page half alsotest.claim.long.roadmap_page_witnesswitness_upcoming_node_dispatch_refused_dependencies_blocked.witness_second_operator_admission_is_already_live.test.claim.roadmap_launch_canary_witnessfinding_unspecified_contract_is_ready_but_environment_refusesflipped from finding to regression control: frontier still Ready, environment still refuses, and the admission answersExecutionContractMissing.Review 5059520727 — the four production boundaries (repaired on HEAD)
test.claim.roadmap.roadmap_launch_admission_witnessunless named)gunbc.roadmap_beltbelt_reconcilereturns every upsert in plan order (free/deferred/takedeleted,BeltReconcile.upserts);gunbc.roadmap_belt_actuatebelt_tick_admitfolds all of them throughlaunch_admissionwithreserved= launches already admitted in this fold, so excess rows are typedLaunchNoCapacity; the tick'sdeferredfigure 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/#3LaunchNoCapacity{occupied:1,max:1}),witness_three_candidates_manual_ready_all_reach_cause_gate(all threeCauseNotPermitted),test.claim.roadmap_belt_witnesswitness_reconcile_returns_every_upsert_regardless_of_capacitybelt_launch_host_standing_for_instancebehind 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 isLaunchHostHalted { 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 → purebelt_tick_launch_pass→ actuation; the tick-local string refusals are deleted andBeltTickResult.host_refusal: LaunchRefusal?islaunch_host_refusal(standing), the same value every per-nodeSpawnNotAdmittedcarries. Verify/publish continue on every arm except a transition halt (the deploy's wholesale refusal, unchanged).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, positivewitness_timer_pass_clear_host_decides_ready_parent; receipt-layerobserve_refused_spawn_does_not_zero_verify_pass(verify continues under a host refusal)launch_occupying_count: a present session whose process is unobserved occupies its slot; only a proven exited / process-absent session frees it (declared inlaunch_occupancy_note; the contract text namesoccupancy=running+unobserved).witness_unobserved_process_occupies_a_slot(cap 1, one unrelatedWorkerProcessUnobserved, Ready candidate →LaunchNoCapacity{1 of 1}), positivewitness_exited_or_absent_process_frees_its_slotSpawned/SpawnFailedcarrylaunch: LaunchIdentity, threaded from the admission site through every actuation step; persisted in the attempt state aslaunch-identity.jsonbesideenvironment-admission.json; emitted in the POST JSON (launchobject) and the durable tick receipt (launches, schemaroadmap-belt-tick-receipt/v3, decoded fail-closed); the page's available action carriesWorkAction.launchrendered asdata-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_witnesswitness_identity_survives_actuation_failureControls landed first (
bd6f9c369a, red againstdff43862a2's shapes), repairs after. The priorbelt_reconcile_capacity_defers_surfacedwitness, which pinned finding 1, is replaced by the every-upsert control.Review 5059681676 — the two remaining boundaries (repaired on HEAD)
Recordedfor passes it skippedBeltTickResult.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 asRefusedwith the transition reason; a non-transition host refusal withholds only the launch passes and verify/publish stay independently derived.test.claim.roadmap_belt_actuate_witnesstransition_halted_tick_refuses_all_four_passes,revision_refusal_keeps_verify_independently_recordedLaunchSessionsPendingis the observer's output;launch_admission_stageddecides the pre-session gates (typedLaunchGateStageon each ledger row) and answersLaunchStageNeedsSessionsonly 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.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 refusesLaunchNoActuatingInstance { reason }(labelno_instance, 403) instead of an empty instance id;LaunchGraphProjectedis flattened once per standing (launch_graph_of_document) — the per-row find and dependency scan remain linear scans, named inlaunch_graph_resolved_note;belt_dispatch_status_noterewritten. Not taken: foldingreservedacross page rows —reservedcounts 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 fromlaunch_ledger_rowsover a ledger with no gate evaluated;launch_contract_text_of_rowsderives thepre_session=…;session=…fragment from eachLaunchGateRow.stage(launch_rows_stages) — the formerhost_stagesliteral is deleted. Mutation controlwitness_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 reproducelaunch_admission_contract_text().Review 57561 — four repairs on HEAD
LaunchIdentityunwoundRoadmapNodeId/GitObjectIdtoStringnode_id: RoadmapNodeId(the node the node gate FOUND, never the request spelling),instance_id: NonEmptyStr(fromHostDashboardInstance.instance_id, theas Stringat 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_stringcollapsedJsonMemberDuplicated/JsonMemberNotAnObjectintononeLaunchGateLedger.identity: LaunchIdentity?+LaunchGateNotEvaluated { "identity" }arm — no authorable REDLaunchDecisionFacts { ledger, identity: LaunchIdentityStanding }where `LaunchIdentityBound { identity }— 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) besideLaunchDependenciesBlocked.blockers: List<String>session_names: List<String>; joined only inlaunch_refusal_reasonwitness_two_sessions_for_one_node_refuse_and_name_bothcounts the listReview 5060505186 — identity binding consumes the four gates in order (repaired on HEAD
10352cd2fab83d22486856b92b6a8198fb59f532)launch_identity_of_observedmatched 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 saidLaunchIdentityBoundbeside 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_orderover the existing observe-only and transition-inhibitedLaunchHostObservedfixtures —facts.identity = Withheld{r}andfacts.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→
8b884d701d7a356eon HEAD4e5061c7aa9c1f4d884e757eb6442840d67d40e6(history:66f7026f0ec62349→d91d73c7b9506bf5after review 5059520727 →5e84bbc6bf984c2cafter review 5059681676:host_stagesnames node,contract,dependencies before sessions and the vocabulary gainedno_instance→8b884d701d7a356eon12507414f5after review 5060354380: the stage fragment is derived from the typed rows, spelledpre_session=…;session=…→8b884d701d7a356eafter 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 hermetictest.claim.roadmap.roadmap_serve_witnesswitness_preview_dispatch_refuses_before_effects(claim_batch's hermetic route refusingFilesystem.List). The fix is structural:LaunchHostStandingcarries aLaunchHostObserveOnly { instance_id }arm andbelt_launch_host_standing_for_instanceshort-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_beltbelt_step/belt_plan/belt_json_value/roadmap_belt_status_jsonand theBeltActionsum are deleted; so is the reconcile'stake(free)(finding 1 above) (with their nine witnesses): a retired spawn-only planner consumed by nothing in production that still selectedfrontier.readyunderAutomaticReady— 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_spawnerspawn_frontier.readyremains 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) citebelt_revision_admissionin a dated verification receipt; left as history. Live comments infleet_converge_cli.dag,deployed_tree_report.dag, and the fleet witness now citelaunch_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