Repository navigation
PR B (add): gunbc-owned runner slot lifecycle — materialize, cap, activate, verify - #7946
gunbai-bot[bot] wants to merge 4 commits into
Conversation
…ivate, verify Split from parked #7931 joint-solver PR per operator ruling (deep-badger-594): this lane owns HostEffect arms, runner_lifecycle ADD sequence, slot provision, disk observation, srv4 width-7 at allocation with apply-time disk preflight, and fleet observation producer scaffolding. Not included here: host_capacity_solve joint enumeration (stays on #7931). Judgement calls in this PR body: - runner_host_deploy / runner_slot_allocation: provisioning materialization target (runner_count=7, disk gate at apply). - fleet_shell_transport_observation, build_cache_endpoint_observation, host_swap_backing, session_residency shell gate: interim observation producers gated on transport; unsure → delivery PR. - host_converge_slice1: under-count is postcondition for provision ADD, not pre-refuse (count equality after RunnerSlotProvision). Local verification: claim_batch on runner_lifecycle, runner_slot_provision, runner_slot_allocation, host_allocation_conservation, runner_host_deploy witnesses (pull_request checks absent repo-wide since 2026-08-06T19:36:59Z). Co-authored-by: Cursor <cursoragent@cursor.com>
Keep producer-Unobserved as Unobserved with dated receipts as provenance only, not authoritative ReachRefused. Refuse fabricated Owned on observed slot dirs until systemd drop-in provenance lands (ownership_of Absent). Co-authored-by: Cursor <cursoragent@cursor.com>
… 49715/49716) Author runner_lifecycle_realize hub so runner_lifecycle_effects_scaffold DeclarationRef resolves; repair witness_runner_lifecycle_host_effects_declared to construct typed HostEffect arms without invalid match wildcards (8/8 PASS). Co-authored-by: Cursor <cursoragent@cursor.com>
Dispatch each lifecycle HostEffect through resolve_host_effect_cell and assert the matching *OnHost cell shapes; refuse BmcController targets. Remove unused String alias rows that duplicated host_layout authority. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Closing as superseded by #7947, per the wind-down consolidation directive. Verified containment, not assumed: every file in this PR also appears in #7947 (21 files here, 38 there, and Why this PR cannot go green on its own: it adds nine Worth recording that this PR sat at 2 approvals on a head that could never merge — approvals are not the bar, and this is the clearest instance of that in this lane. Work continues in #7947. — sent from deep-badger-594 |
* MemberChanged{from,to}: declaration-owned occurrence identity and typed replacement classification
Split MemberAction's collapsed upsert into four arms per operator ruling:
MemberAdded{to} | MemberChanged{from,to} | MemberRemoved{from} |
MemberRemovalRefused{from, cause}.
The discarded `from` was the load-bearing loss. Every safe realization of a
change consumes the prior value: an SCM ref update is a compare-and-swap that
names the expected prior (git update-ref <ref> <new> <old>), and a host resource
whose address moved needs the prior endpoint to retire. With only `to` in hand
the sole reachable realization is the unconditional write. MemberEffect splits
the same way (EffectAdd/EffectReplace{from,to}/EffectRemove) so the apply grain
can retire a prior realization instead of overwriting it blind.
gunbc.host_resource: identity is allocator-minted at declaration, never derived
from unit/path/content. HostResourceIdentity{host, occurrence} +
HostResource<R,V>{identity, role, value}, role a type parameter so a new role is
an instantiation rather than a widened central enum. This is the closed-system
answer to renames: git does not track them, it INFERS them from content
similarity, and DESIGN 4 rules a heuristic never necessary in a closed system --
a content-derived key_of is that inference wearing a bundle. Distinct carrier
from std.occurrence_identity (source-occurrence specific; one shared Int space
would let occurrence_id_eq answer true on a numeric coincidence -- the
cross-family comparison the ContentHash grounding closed). The subject-
parameterized extraction is registered as a trigger, not built under a
load-bearing module with stage0 work in flight.
gunbc.change_realization: the classification table. SCM ref -> atomic CAS;
source declaration move -> atomic SCM tree transformation; same runtime address
-> in-place update; changed host address -> staged replacement with declared
intermediates. Kills a live conflation: a .dag module re-home is an SCM tree
transformation, NOT a host migration. StagedIntermediates is head+tail so an
empty intermediate plan has no constructor, and an undeclared one refuses
(typed, located) rather than degrading to an overwrite -- the srv4 lesson (its
build-cache server died with its cgroup, never unlinked its socket, ran uncached
for weeks) made unwritable rather than warned about in prose.
A change-refusal arm was built during this work and WITHDRAWN: gunbc.ownership
defines Ensured as "requires present but does NOT own (never torn down)", a
constraint on removal, not update. It broke tool_readiness and
repo_local_git_config (whose job is re-installing drifted pins/bindings) and was
unreachable elsewhere. Per DESIGN 4c a carrier note is not evidence, so the
withdrawal is enrolled as w_ensured_member_converges_but_refuses_removal.
All three new guards verified DISCRIMINATING by planting their exact
regressions: reintroducing the change-refusal arm reds the Ensured control
alone; reintroducing the collapse reds the both-sides rows; turning the
staged-replacement refusal into an InPlaceUpdate fallback reds the srv4 row.
Caught during migration: repo_local_git_config's apply fold has a `_ => acc`
wildcard for its construction-unreachable removal arms, and MemberChanged would
have fallen into it -- silently dropping the drift that module exists to
converge. Both write arms are now explicit. live_deploy's EffectReplace is
structurally unreachable at both degenerate poles and emits a loud typed poison
per that module's discipline; its 44 byte-identity goldens pass unchanged.
Witnesses green by execution: membership_reconcile 10/10, change_realization
8/8, live_deploy 44/44, plus tool_readiness, repo_local_git_config,
workflow_reconcile, srv3, host_phase_status, roadmap_belt, host_axis_caps.
Corpus compile carries the same 10 pre-existing diagnostics as main, none mine.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Model runner connectivity recovery controller (PR B replace/repair slice).
Introduce typed RunnerConnectivityAction with incarnation replace, canary-first
fleet breaker, verified postcondition, and per-host reach refusal so wedged
acquire-job slots plan repair without watchdog fanout.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Trigger CI synchronize: #7938 opened with zero check runs (GitHub Actions webhook incident)
The PR opened during the Actions backend incident and no workflow runs were
created at all -- absent, not queued. ci.yml triggers on pull_request
[opened, synchronize, reopened, ready_for_review], so this empty commit fires
a synchronize to get the affected-set run executed against the branch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Add repair effect choreography over shared HostEffect arms.
Project RunnerIncarnationReplace into a four-step observe-replace-observe
sequence with scaffold binds to still-ram-738's HostEffect variants; witnesses
green for replace and noop paths.
Co-authored-by: Cursor <cursoragent@cursor.com>
* PR B (add): gunbc-owned runner slot lifecycle — materialize, cap, activate, verify
Split from parked #7931 joint-solver PR per operator ruling (deep-badger-594):
this lane owns HostEffect arms, runner_lifecycle ADD sequence, slot
provision, disk observation, srv4 width-7 at allocation with apply-time
disk preflight, and fleet observation producer scaffolding.
Not included here: host_capacity_solve joint enumeration (stays on #7931).
Judgement calls in this PR body:
- runner_host_deploy / runner_slot_allocation: provisioning materialization
target (runner_count=7, disk gate at apply).
- fleet_shell_transport_observation, build_cache_endpoint_observation,
host_swap_backing, session_residency shell gate: interim observation
producers gated on transport; unsure → delivery PR.
- host_converge_slice1: under-count is postcondition for provision ADD,
not pre-refuse (count equality after RunnerSlotProvision).
Local verification: claim_batch on runner_lifecycle, runner_slot_provision,
runner_slot_allocation, host_allocation_conservation, runner_host_deploy
witnesses (pull_request checks absent repo-wide since 2026-08-06T19:36:59Z).
Co-authored-by: Cursor <cursoragent@cursor.com>
* Joint runner_slots × build_tokens capacity solve (srv3 compile-pool flip)
Parked off capacity-delivery critical path per operator ruling — NOT
presented as progress toward gunbc apply / seven runners. Lifecycle,
HostEffect arms, and provisioning live on session/still-ram-738-pr-b.
This PR contains only host_capacity_solve + witness and the jobserver
helper split needed for per-token pool sizing in the enumeration.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix shell transport fallback and slot ownership grounding (review 49707)
Keep producer-Unobserved as Unobserved with dated receipts as provenance
only, not authoritative ReachRefused. Refuse fabricated Owned on observed
slot dirs until systemd drop-in provenance lands (ownership_of Absent).
Co-authored-by: Cursor <cursoragent@cursor.com>
* PR C: wire mode=apply to runner_capacity_realize (last mile)
Connects gunbc.apply mode=apply to runner_capacity_plan + disk
preflight + ADD dispatch seam (depends on #7939 plan, #7946 effects).
Witnesses: two additions not count-refusal, disk unobservable refuses
(not width-5 fallback), replay no-op when observed satisfies request.
Local claim_batch: 3/3 PASS on runner_capacity_realize_witness_test.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix review 49712: resolvable witness imports and citation targets.
Import hardware_thread_count_value so the tokens-cap refusal witness resolves;
author srv3_non_compile_residency_observed as a scaffold producer so the dissolve
DeclarationRef cites a live symbol; ground slice_unit via host_layout.gunbc_managed_compile_pool_slice_unit.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix session-present fail-open on live capacity admission (review 49722)
Only SessionsObservedAbsent maps to CapacityEvidenceObserved; observed
session presence caps at candidate-only with an explicit reason. Add
srv1_session_present_live_capacity_is_candidate_only witness.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Port incident broker-progress watchdog into gunbc recovery model.
Models the conjunction predicate (remote offline + broker silence + no worker + grace elapsed), evidence grades, re-read refusal, and derived 10-minute bound with discriminating witnesses wired into runner_connectivity_recovery.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Enroll extdeps.github.actions_runner as a scope carrier
The floor's extdeps scope placement gate refused PR C: dag/extdeps/github/
actions_runner.dag landed as a new extdeps module enrolled in neither
scope_carrier_paths nor the machinery exemption, and the frozen legacy
manifest is not a landing state for anything added after the freeze.
Subject is actions_runner_release_2_334_0 — the cited upstream release row,
which is what GitHub actually publishes and what this module exists to pin.
Appended to the carrier roster rather than re-sorted; re-sorting that list
produced a pointless merge conflict on #7891 earlier today.
carriers_declare_scope_on_disk, frontier_rosters_are_disjoint,
roster_paths_resolve_on_disk and preflight_rows_counted green by execution.
The other diagnostics in that run were the floor refusing to spend budget
once batch 1 went red, not separate defects.
* Fix PR C compile-clean failures and land converged-seven replay no-op.
Add missing HostEffect match arms in fleet_converge_cli, fix lifecycle
witness construction (no bare _ values), and model post-convergence width
via converged-seven roster fixture so a second apply plans zero additions
and succeeds without disk preflight when nothing remains to materialize.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Close PR B fanout and re-read gaps against operator watchdog ruling.
Add fleet plan builder with per-host replacement cap, five-step repair choreography (observe before mutate), SIGPIPE diagnostic-read refusal, and reobservation witnesses.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix reserved-keyword binder and delete a tautological witness
'release' is a reserved word; using it as a match binder made
runner_lifecycle_witness_test.dag unparseable, which killed regen and
heal before they did any work and left ci reporting FloorUpstreamAlreadyRed.
All three red jobs had this one cause.
Underneath it, witness_runner_lifecycle_host_effects_declared constructed
each HostEffect variant and matched it straight back. That establishes only
that the constructor typechecks -- already the typechecker's guarantee -- and
cannot go red on any behavioral defect. The compiler refused each single-arm
match as non-exhaustive over the 31-variant coproduct; answering that with
thirty '=> false' arms would make the tautology compile without making it a
check. Deleted, with its now-dead imports. fleet_converge_cli already carries
the claim by execution: it matches HostEffect exhaustively and fails to
compile if an arm goes missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Ground ReplaceRunnerIncarnation on from/cause and fix witness parse.
ReplaceRunnerIncarnation now carries LocalRunnerSlotIncarnation plus
RunnerReplacementCause so repair plans can refuse stale incarnations and
name why replacement fired; witness binding renamed from release to cited
to avoid the reserved keyword that broke regen.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Name lifecycle realizer scaffold dissolution and retitle PR C honestly.
Add runner_lifecycle_realize_wet_transport_dissolve_on (srv3/srv4 SSH
reach + HostEffectTransport) and cite it from every NotConverged arm;
update runner_capacity_realize authority note to state apply does not
provision until the scaffold retires.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Land Step A: incarnation type-home, in-band admission, seam witnesses.
Move LocalRunnerSlotIncarnation to gunbc.runner_incarnation, add ReplaceRunnerIncarnation seam witnesses, and model in-band ADD admission (LocalShell + derived execution target; Step E void). Capture operator constraints: workflow-only application, srv1 row-6 control, unit/process width authority, imperative caps, and ctrl installer read-before-realize.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Model SlotMaterializedNotActivated and retire residue framing.
srv1 probe shows complete dormant install trees without .runner until JIT start. Add three-state installation carrier and witnesses; correct width notes. srv3/srv4 slot-06/07 state still unobserved from this session.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Refine slot state: stale .runner is history, ADD needs no replace.
Collapse materialized-never-activated and materialized-previously-activated into SlotMaterializedNotActive; unit_active drives readiness. Record srv3 06/07/08 probe, srv3 reach via srv2 jump, and width-8 vs intent-7 ceiling.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add installation observation wrapper; pin vivid-eagle srv3 data.
RunnerSlotInstallationObservation separates observed states from Unobserved/ReachRefused so srv4 never defaults to SlotAbsent. Pin srv3 01-08 mapping from sibling probe; witnesses enforce srv4 blocks add without extract routing.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix PR #7947 CI witnesses: roster wiring, pinned incident width, nfr rows.
Restore srv3 shell transport session receipt for producer-unobserved authority, pin incident desired_width at 22, pass roster into connectivity repair planning, and enroll connectivity verdict predicates in the non-fold-residue roster.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Align CI fixes with deep-badger guidance: observe srv3, derive incident width.
Wire vivid-eagle srv3 shell transport as producer-observed success while srv4 stays unobserved with dated receipt provenance; derive incident desired_width=22 from srv4's five-slot outage pin instead of a naked literal.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add owned CI control plane v0: poll-first queue, executor, Checks projection.
Introduces medium-neutral CiExecutionPlan carriers from ci_spec (not ci.yml), a poll-first ci_control_plane daemon with durable queue and lease reclaim, GitHub Checks create/update, and /ci serve routes for run receipts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49764: authority projection, reconcile ordering, scaffold marker.
Load repo/stages/floor plan from gunbc.owned_ci_seed via eval; persist ledger before check create; mark CheckPublicationPending on API failure without duplicate enqueue; document seed REST transport as dissolve-on scaffold.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49792: typed durations, seed authority, publication honesty.
Ground lease duration on std.measure.Second and queue expiry on NumericDate;
load state root and lease seconds from gunbc.owned_ci_seed in the Rust bin;
fix unpublished-completed conclusion mapping and UpdateRun wire encoding;
dissolve ci_subject_is_fork_refused Bool predicate into direct coproduct match.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Clean up ci_control_plane per review 49812 non-blocking notes.
Drop dead failed/completed ternary on queue move (always completed dir);
remove tautological Rust unit tests — decision procedure stays in .dag witnesses.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49829: fail-closed clock and fork refusal receipts.
Propagate system-clock errors from now_unix instead of fabricating 0;
persist fork PR refusals to index/run ledger so the /ci UI can observe them.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49838: refuse fractional queue lease clocks.
Replace Bool lease expiry with typed reclaim/validity decisions that
carry LeaseClockRefused for fractional NumericDate arms; witness the refusal.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49852: remove poll bypass, enroll token refusal witness.
Delete undocumented OWNED_CI_POLL_MAIN_ONLY escape hatch; model PR poll
GitHub-read admission in owned_ci.dag with witness_poll_without_github_read_refuses;
route Rust refusal strings through owned_ci_seed.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49866: optional head SHA for refused subjects.
ci_subject_head_sha returns CommitSha? with Absent for ForkPullRequestRefused
instead of empty-string sentinel; witness enrolled in bundle.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49870: dry-run leaves queue pending, dedupe checks.
Skip claim/lease in --dry-run (peek-only log); route publication success
through check_conclusion_is_success; delete unused CiRunStage* types.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Register ci_control_plane in self-host frontier for regen parity.
Seed-retained intrinsic was missing from frontier and stage0 crate layout,
so regen_verify stripped pub mod ci_control_plane from lib.rs and CI regen failed.
Alphabetical ci_control_plane before cli_run matches rustfmt and frontier order.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49929: paginate open PR discovery, honest publication causes.
Follow GitHub Link rel=next with per_page=100 and refuse when pagination
cannot complete; pass through actual check-update errors at completion instead
of a canned API-unavailable string.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49931: PR-only token admission, refuse malformed PR payloads.
Missing or empty GITHUB_TOKEN refuses PR discovery only; main mirror subjects
still reconcile. Malformed open-PR API rows fail closed with a typed cause
recorded in the subject ledger instead of silent continue.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 49950: refuse unsafe run_id before filesystem read.
Model owned_ci_run_id_admits in .dag (path_segment_is_safe plus {unix}-{8hex}
grammar), gate serve_ci_run_detail_response, mirror validation in the seed
reader, and harden write_json_atomic with pid-scoped tmp files plus parent fsync.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix CI compile-clean: break owned_ci_serve import cycle.
Remove dead page_render_refusal_body import from roadmap_static_site and
stop importing gunbc.roadmap_serve (cycle through roadmap_serve_witness).
Inline owned_ci_serve_text/page_response helpers locally instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Filter open PR discovery to merge-target branch (v0 scope).
Poll path compares GitHub base.ref to gunbc_default_branch_name from seed authority; non-main same-repo PRs are ledger-recorded refusals (not enqueued). Adds ci_pull_request_targets_merge_branch, witness, and Rust unit tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Route merge-target branch through repo_identity everywhere.
ci_subject_identity_key, mirror fetch, and push-subject discovery use gunbc_default_branch_name via seed authority instead of hardcoded "main".
Co-authored-by: Cursor <cursoragent@cursor.com>
* Retry CI after sccache EAGAIN on build job (infra flake).
Prior run 31155607568: v1-compiler compiled; v1-compiler-tests failed spawning rustc via sccache (os error 11). No code change.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Honor dry-run GitHub boundary and add operator-rerun enqueue.
dry_run skips GitHub REST PR poll (mirror-only discovery); --enqueue-rerun-sha realizes v0 OperatorRerun with rerun:<sha> keys. Witnesses and design doc updated.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Split owned-ci serve file reads into Present/Empty/Missing.
owned_ci_classify_filesystem_text uses Filesystem.Read success vs empty content instead of conflating missing and empty into Optional Absent.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix owned-ci .dag strings that parsed as template variables.
Replace curly-brace grammar placeholders in prose with angle brackets so compile-clean does not treat run_id/unix_seconds as undefined bindings.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fail-closed on missing head.repo.full_name in PR poll.
Malformed pull payloads refuse at poll boundary instead of defaulting to empty and mislabeling as fork refusal; unit test added.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Refuse silent no-ops on index and queue state drift.
update_index_row and move_queue_file return typed errors when the expected row or queue file is absent instead of succeeding unchanged.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Document state-carrier dissolve-on in owned-ci seed scaffold.
Extend owned_ci_control_plane_seed_note to name JSON↔.dag coproduct unification and key-grammar eval alongside the REST bridge; register poll_interval Rust-only debt.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Align queue refused state and per-run summary receipts.
Add QueueRefused to CiRunQueueState with witness; key target summary receipts by run_id under target/owned-ci-run-receipts/.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Register owned-ci-control-plane design doc in doc graph.
The v0 design note was added on this branch without a HandAuthoredDocBind,
so doc_reachability flagged it as an orphan on the discovery-corpus floor.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix review 50233: wire watchdog inputs and reobservation into plan path.
RunnerSlotConnectivitySnapshot now carries broker/worker/grace; plan and decide_for_snapshot read them instead of fabricating wedged inputs. reobservation_refuses_mutation gates replace before actuation; two new witnesses cover offline-alone refusal and stale decision receipt.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address review 50232: Terminal disposition on total-match predicate families.
NFR rows would stale the roster — these helpers are exhaustive matches without top-level wildcard arms (unlike connectivity_verdict_is_*). Document predicate-dissolution at authoring via explicit Terminal disposition rows.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Import RunnerSlotIdentity in runner_incarnation (review 50320).
The type is referenced twice in this module and was resolving only by pool
coincidence — the Class B binding failure the corpus already tracks. Picked up
from session/still-ram-738-pr-c commit 5df7f02, which fixed the same review
finding on the now-superseded branch.
That commit's other half — a stray brace in non_fold_residue_frontier — does
not apply here: it came from that branch's own conflict resolution, and this
branch's roster resolution did not produce one (checked, the list closes clean).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Re-vocabulary runner_slot_provision onto the MemberChanged{from,to} surface.
CI ci job red: 'Refused' not found in module 'gunbc.membership_reconcile'
(imported by 'gunbc.runner_slot_provision'), 3 occurrences. A genuine fold
defect, not a flake and not memory (floor peak 9.85 GB against memory.max
17179869184 = 57%, so the cap was not reached).
CAUSE: runner_slot_provision came from #7947 and was written against the
pre-rework membership_reconcile surface (MemberUpsert / MemberTeardown /
MemberTeardownRefused / membership_upsert_count). The fold also brought in
#7938's MemberChanged{from,to} rework of that module, which splits the old
fused upsert into distinct MemberAdded and MemberChanged arms. Neither PR
was wrong alone; consolidating them created the mismatch.
NOT A MECHANICAL RENAME. The old MemberUpsert fused add and change, and the
operator's Decision 1 split them deliberately, so each site is a decision:
- MemberAdded -> emit the slot extract script. This is the add path.
- MemberChanged -> []. Decision 2 says apply may execute RunnerSlotAdd only
and must refuse Change/Remove/IncarnationReplace; emitting an extract
script for a changed member would materialize a change the apply path is
required to refuse.
- MemberRemoved / MemberRemovalRefused -> [], unchanged in meaning.
- membership_upsert_count -> membership_added_count, NOT the derived
membership_write_count (= adds + changes). The consumer is
runner_slot_provision_has_width_increase: a width INCREASE is adds; counting
changes would overcount. membership_write_count exists for consumers that
want both, and this is not one.
The witness carried the same stale vocabulary and is migrated with it; its
assertion is preserved exactly (7 desired - 2 observed = 5, and the 2 observed
match desired so they are Unchanged, not Changed).
VERIFICATION, stated at its real strength. A local single-entry run does NOT
verify this: a stashed-fix control returns the IDENTICAL 2000 errors and zero
'Refused' reports on the pre-fix tree, so that instrument never surfaces this
defect class -- it dies at parse before resolution, and 2000 is a truncation
cap rather than a count. Verified instead at symbol grain: all 7 symbols
imported from membership_reconcile resolve to declarations, with a positive
control confirming the query reports the two removed names as MISSING. CI's ci
job is the instrument that found this and is the one that settles it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Add the check-publication predicate-family disposition (review 50389).
REQUEST_CHANGES verified against current code rather than taken on trust, and
the finding is correct: gunbc.owned_ci check_publication_reports_github_success
and check_publication_is_pending_honest are coproduct is_*/reports_* predicates
carrying no disposition, while this same diff already dispositions two sibling
families — extdeps.github.ci_runner github_runner_registration_predicate_family_disposition
(review 50232) and gunbc.runner_incarnation runner_slot_installation_predicate_family_disposition.
Inconsistent within one PR is the reviewer's point and it stands.
Adds check_publication_predicate_family_disposition = Terminal, matching the
runner_incarnation precedent's shape and reason.
TOTALITY CHECKED, NOT ASSERTED. The row claims both matches are total, so that
claim is measured: CheckPublicationState has six variants (NotStarted, Queued,
InProgress, Completed, Pending, Failed) and both fns carry an explicit arm for
all six with zero top-level wildcard arms. Positive control: the same query
reports a fictitious variant as missing, so an all-present result is a real
negative rather than a blind read. (First pass counted the type name itself as
a seventh variant and reported a false miss — the query was wrong, the code was
not.)
Terminal rather than NFR enrollment is the right arm for the same reason the
precedent gives: these dissolve at authoring, so an NFR row would stale the
roster. fleet_runner_connectivity connectivity_verdict_is_* is the contrasting
case — wildcard false arms, correctly enrolled in non_fold_residue_frontier.
Scope checked: exactly two Bool fns in gunbc.owned_ci match over a coproduct,
and both are fixed. owned_ci_run_id_char_is_digit / _is_hex take String, not a
coproduct. check_conclusion_is_success lives in extdeps.github.checks, outside
this diff.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Drop #7931's parked capacity solver: it forked the compile-pool topology authority.
OPERATOR DIAGNOSIS, and it corrects mine. I had read the double count as a
property of the current topology and concluded the arithmetic had merely drifted
under a stable defect. Wrong on the load-bearing point: main ALREADY carries the
correct rule, and #7931 reintroduced the defect locally.
THE DEFECT IS A DUAL REPRESENTATION OF ONE SEMANTIC FACT — where compile RSS
lives in the host's resource topology.
Representation 1, the authority (gunbc.fleet_host_budget):
CompilePoolInRunnerSlots => CompilePoolDerived { bytes: byte_size(0) }
CompilePoolSliceDeclared => resolve_compile_pool(...)
Compile memory is CONTAINED in runner-slot accounting under the current
topology, so it is not a second claim on host RAM.
Representation 2, the parked solver (gunbc.host_capacity_solve):
per_slot_runner_pool_charge(CompilePoolInRunnerSlots) = full MemoryMax
compile_pool = jobserver_build_pool_bytes_for_tokens(...) [EVERY placement]
host_allocation_conserves(runner_pool: .., compile_pool: .., ..)
Two additive SIBLING charges. The placement argument steers the runner-side
charge but never gates whether the compile charge is added at all.
A containment relation (compile RSS subset-of runner-slot RSS) flattened into a
partition of disjoint siblings. The solver imports the placement and then
reimplements its accounting meaning, which makes the solver a second authority
over a fact fleet_host_budget already owns.
WHY THE WITNESS FAILED NOW. joint_solve_double_counted_topology_refuses_seven_slots_at_derived_tokens
asserts best.runner_slots < 7 — a NUMERIC CONSEQUENCE, not the invariant its name
claims. At 16GiB slots, 7 x 16 = 112GiB against 125GiB usable, so the doubled
charge pushed 7 over and the witness passed. At 14GiB (#7891's measured ruling,
already on main) 7 x 14 = 98GiB leaves ~27GiB, so the same broken model now fits.
The modeling error is unchanged; only its numerical shadow moved. Flipping the
assertion to >= 7 would have been a tree-copied measurement — a change detector,
which DESIGN section 5 forbids as an oracle.
WHY REMOVAL RATHER THAN REPAIR. #7931 is parked, non-delivery content, and main's
capacity authority already carries the correct placement-dependent decomposition,
so deleting the fork restores single authority with no capability lost. Repairing
in place would mean routing candidate accounting through one placement-aware
charge and replacing the numeric witness with structural placement/charge
controls — real work that belongs to the capacity lane, not to a consolidation.
REVERTED TO MAIN, all four paths #7931's merge touched:
D dag/gunbc/host_capacity_solve.dag (absent on main)
D dag/test/claim/host_capacity_solve_witness_test.dag (absent on main)
M dag/extdeps/jobserver.dag -> main (the _for_tokens split served only the solver)
M dag/gunbc/host_layout.dag -> main (gunbc_managed_compile_pool_slice_unit had
exactly one consumer, the solver; keeping it would
strand an unconsumed carrier)
Checked: zero dangling references to host_capacity_solve,
jobserver_build_pool_bytes_for_tokens, or gunbc_managed_compile_pool_slice_unit
remain in the corpus, and per_slot_runner_pool_charge is gone entirely.
NOT CONFLATED WITH THIS: the live jobserver admits 128 tokens where the declared
formula derives 62. That is an over-admission pointing the opposite way (too
permissive) from the double count (too conservative). Untouched here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Brian Searls <briansearls1@gmail.com>
Summary
Splits the capacity-delivery critical path out of parked #7931. This PR is what replaces #7939's
RunnerCapacityRealizerUnavailablerefusal onceapplywires the realizer, and what vivid-otter-718 should import for repair (HostEffectarms +RunnerSlotIdentity).Included (delivery lane):
gunbc.host_effect/host_effect_realize— lifecycleHostEffectarms (EnsureActionsRunnerRelease,EnsureRunnerSlotDirectory, caps,ActivateRunnerSlot, observations, …)gunbc.runner_lifecycle/runner_lifecycle_realize— ADD step ordering, apply-timerunner_width_disk_preflight, scaffold realize stubsgunbc.runner_slot_provision— gunbc-owned install/extract/ownership (ctrl citations void)gunbc.host_disk_observation— disk headroom observation carrierextdeps.github.actions_runner— cited release + scaffold shell rows bound tohost_effect_applyrunner_host_deploy/runner_slot_allocation— srv4runner_count=7, allocation commits 7 with disk gated at apply (not row-pin to 5)Judgement calls (why these ride with PR B, not #7931):
fleet_shell_transport_observation,build_cache_endpoint_observation,host_swap_backing,session_residencyshell gate — interim observation producers; unsure → delivery PRhost_converge_slice1— under-count is postcondition for provision ADD, not pre-refuse on width mismatchExplicitly NOT in this PR:
host_capacity_solvejoint enumeration (stays on #7931, parked).Scaffold / follow-on (operator review bar)
Realizer stubs return
NotConvergedtoday. Before merge to main for apply:ActivateRunnerSlotsystemctlexit alone)apply.dag/runner_capacity_realizeintegration lands as follow-on on this branch or stacked PR.Test plan
Local execution (required — pull_request check creation stopped repo-wide 2026-08-06T19:36:59Z;
checks:noneon PRs is not a readiness verdict):For vivid-otter-718
HostEffectlifecycle arms andRunnerSlotIdentitynow have a stable home onsession/still-ram-738-pr-b— import from here, do not mint parallel copies.Made with Cursor