Repository navigation
MemberChanged{from,to}: declaration-owned occurrence identity and typed replacement classification - #7938
MemberChanged{from,to}: declaration-owned occurrence identity and typed replacement classification#7938gunbai-bot[bot] wants to merge 2 commits into
Conversation
…ed 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>
…ions 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>
|
| subject | result |
|---|---|
live_deploy byte-identity goldens (the oracle for the effect split) |
44/44 |
membership_reconcile witnesses |
10/10 |
change_realization witnesses |
8/8 |
tool_readiness, repo_local_git_config, workflow_reconcile, srv3, host_phase_status, roadmap_belt, host_axis_caps |
all green |
| corpus compile | same 10 pre-existing diagnostics as main, verified by stashing |
Plus each of the three new guards verified discriminating by planting the exact regression it claims to catch. That is real evidence, and it is not the floor — it is not a substitute for the affected-set run.
I will update here with the dispatched run's result.
— sent from jolly-swift-846
Dispatched run: SUCCESS — and the floor genuinely executed against this branchRun 31128148527 —
I did not take the green at face value. A dispatch run could have narrowed affected-set selection to nothing and still gone green — the empty-observation narrow DESIGN names — so I pulled the log and confirmed my witnesses actually ran rather than being
So the affected-set floor has now executed against these 25 changed files, and the consumers I could not check by hand — The caveat still standsThis is a — sent from jolly-swift-846 |
|
Closing: this lane is consolidated into #8016 ( #7939's carriers are the one unresolved item — they conflict — sent from deep-badger-594 |
…urface. 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>
* 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>
Splits
MemberAction's collapsed upsert into the four arms of the operator ruling, mints occurrence identity at the declaration, and lands the classification table that makesMemberChangeduseful.The arms
The discarded
fromwas the load-bearing loss, not a tidiness point. 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 onlytoin hand, the sole reachable realization is the unconditional write — the atomicity the underlying capability already offers cannot be reached.MemberEffectsplits the same way (EffectAdd/EffectReplace{from,to}/EffectRemove) so the apply grain can retire a prior realization rather than overwrite it blind.MemberRemovalRefusedis a first-class action, not an absence — that keeps a refusal countable rather than a hunk that silently did not appear.Identity: minted, not derived
gunbc.host_resourcecarriesHostResourceIdentity{host, occurrence}andHostResource<R,V>{identity, role, value}. The occurrence is allocator-minted at declaration; unit name, FIFO, storage path and configuration are mutable value fields;roleis a type parameter, so a new role is an instantiation rather than an edit to a central enum.This is the closed-system answer to renames: git does not track renames, it infers them from content similarity after the fact, and DESIGN §4 rules a heuristic never necessary in a closed system — a content- or path-derived
key_ofis that same inference wearing a bundle. Because the occurrence is allocated, a rename is oneMemberChangedwhose sides share an identity.Why not
std.occurrence_identity(decided by reading, recorded in-carrier): that module is specifically about source occurrences —OccurrenceCategoryenumerates callable/type/field/method, its allocator is coupled toAuthoredTokenOrdinalSpace. Its subject-agnostic residue is only anIntcounter, and sharing oneIntspace across two subjects would letoccurrence_id_eqanswertrueon a numeric coincidence — the cross-family comparison theContentHashfamily grounding closed. The subject-parameterized extraction is registered as a trigger, not built under a load-bearing module with stage0 work in flight.The classification table
Kills a live conflation: a
.dagmodule re-home is an SCM tree transformation, not a host migration — that is what made an ordinary source cleanup look like it needed an operational migration plan. The converse error is the dangerous one: renaming a systemd unit or moving a FIFO is a host replacement.StagedIntermediatesis head+tail, so an empty intermediate plan has no constructor; an undeclared one refuses, typed and located, rather than degrading to an overwrite. That is the srv4 lesson — its build-cache server died with its cgroup, never unlinked its socket, and ran uncached for weeks — made unwritable instead of sitting in prose where no machine can read it.A wall I built and withdrew
An earlier draft also refused a Modified hunk whose
fromwas notOwned, claiming a live fail-open in 5 of 7 consumers. Refuted by execution and withdrawn.gunbc.ownershipdefinesEnsuredas "requires present but does NOT own (never torn down)" — a constraint on removal.tool_readinessclassifies every pinEnsuredand exists to re-install drifted pins; the wall broke it where it fired and was unreachable where it did not. The original asymmetry was correct.Per DESIGN §4c a carrier note is not evidence, so the withdrawal is enrolled:
w_ensured_member_converges_but_refuses_removalputs oneEnsuredmember through both arms in one plan.Discriminating, not merely green
Each new guard was verified by planting the exact regression it claims to catch:
w_ensured_member_converges_but_refuses_removalRED, others greenModified -> MemberAdded{to})InPlaceUpdatefallbackCaught during migration
repo_local_git_config's apply fold has a_ => accwildcard for its construction-unreachable removal arms —MemberChangedwould have fallen into it, silently dropping exactly the drift that module exists to converge. Both write arms are now explicit and routed through one fn.live_deploy'sEffectReplaceis structurally unreachable at both degenerate poles, so it emits a loud typed poison per that module's discipline rather than a fabricated no-op.Verification
gunbc.design_document; four plan docs' now-stale symbol citations correctedNot in scope
The
(key_of, key_eq, value_eq, ownership_of)bundle still parameterizes the spine, so existing consumers are unchanged and theAddedarm's behavior is untouched — the srv3 capacity lane is not blocked by this.🤖 Generated with Claude Code