Skip to content

Arm recovery as convergence: observe, assess, plan at arm grain; delete the leftover classifier - #13365

Closed
gunbai-bot[bot] wants to merge 647 commits into
mainfrom
session/cool-carp-342-arm-converge
Closed

gunbai-bot[bot] wants to merge 647 commits into
mainfrom
session/cool-carp-342-arm-converge

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

Replaces the native arm's leftover classifier with convergence: every launch, rollback and recovery observes every rank, assesses against a desired arm (std.goal_assessment), and decides (std.upsert_decision). Design: docs/plans/arm-convergence-recovery-design.md. Stacked on #13319.

Why. On 2026-10-05 (run 37254736596), Group A was left in a state that matched no enumerated leftover kind: three ranks had rolled back and were running, and the head was fence-only and stopped. Group A stayed down until a human intervened. The fence's 2 h horizon was a timer standing in for "is the writer dead".

What changes

  • gunbc.spark.arm_convergence (new). Per rank, it reads:
    • the unit file hash vs the planned unit's (hashed on the host);
    • the unit's ActiveState;
    • the container's incarnation;
    • the committed-arm record.
      Each fact is read, absent, or unread, and unread is never treated as absent. The decision is Noop / Refuse / Apply. Apply carries the whole desired arm and nothing that could name a subset.
  • native_serving_apply is one loop: observe → (a realized arm is read back first) Noop / Refuse / Apply → preflight → stop every rank and read each container gone → vacancy → apply head first → readback → commit (write the committed-arm record). On failure it returns to the last committed arm, through the same readback, and only if the re-render reproduces the recorded unit hashes. With no committed arm to return to, or a failed return, every rank is stopped.
  • Deleted:
    • the leftover classifier, the horizon, adoption and marker recovery;
    • the fence, the incumbent backup and the dispositions;
    • the declared-incumbent bracket and the incumbent dispatch input;
    • the adoption-only admission.
  • The arm's own units are the converge's subject, not occupants. Vacancy is judged after the stop. A foreign occupant still refuses.
  • Serialization: there is no fence. fleet_converge_mode_writes_arm is a total per-mode answer, read against each mode's concurrency domain; Group A keeps srv1's key on purpose. Hand actuation is unserialized and is caught only as a deviation or an unknown.

Switching Group A between model families (GLM to V4.1, or back): what this now requires. The declared-incumbent bracket is gone, so a launch whose hosts carry ANOTHER arm's running units refuses at admission ("occupied by a serving unit that is not this arm's"). Stopping the other arm by hand is not the route: the stop must be a declared converge of that arm toward a desired state of VACANT (every rank stopped, its container read gone, its committed-arm record cleared), run through the same loop and the same concurrency group. That converge toward vacant is not in this PR; it is the named follow-up, and until it lands a cross-family switch on Group A refuses. Within one family (every GLM staircase step), switching is an ordinary converge, because the arm's own units are its subject.

Evidence. Remote claim_batch at 4da4570:

witness file pass
arm_convergence 11/11
fleet_converge_arm_writer 3/3
native_serving_resumable_apply 17/17
native_serving_roce_transport 17/17
group_arm_launch 14/14
v41_group_a_launch 26/26

The converge itself has not run on hardware; its first execution is a Group A launch, held until the parent says go.

🤖 Generated with Claude Code

Brian Searls and others added 30 commits October 2, 2026 17:30
…ocabulary spells sliding windows

ExpectedKvRoster carries the vLLM revision its arithmetic is modelled against, and both census folds
compare a layout or plan to the roster's revision rather than the global one. ExpectedGroupKind gains
ExpectSlidingWindowKind { window } (window is part of the kind); the kind match and the KPool interval
are spelled over every KvGroupKind with no wildcard. Comment no longer claims unchanged messages.

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…xternal retry obligation (deforking cut 6)

gunbc.serving.caller_retry_contract models the operator's 2026-10-02 durable-retry contract as seven
properties with standings; caller_backlog_standing derives DurableCallerBacklog only when every property
is established, else ExternalRetryObligation naming the unmet ones. No server queue. The harness's
pending-placement exit names the standing. Every serving-route row is unestablished with its trigger,
including two the modelling surfaced: pending exits with the refusal's status, and a retry mints a new
attempt identity.

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

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

Review finding (73243/73540/73709/73856, DESIGN §5): every \uXXXX went through
from_code_point, which answers "" for a surrogate, so a valid pair such as
😀 parsed as Some("") and the character vanished. A high surrogate
must now be followed by \u + a low surrogate and the pair decodes to its one
scalar; a lone high, lone low, reversed pair, or high followed by anything else
returns None (the escape-set refusal the grammar maps to its parse failure).

Witness (json_parse_witness_test): a_surrogate_pair_decodes_to_its_one_scalar
(positive control) and an_unpaired_surrogate_refuses_rather_than_vanishing
(red). Both fail on the old kernel; 23/23 pass on the new. v1_rt.rs and
v1_compiler_runtime_rust.rs regenerated (fixed point verified).

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


Operator default ruling 2026-10-02 (move-oauth-growth-out-keep-unsplit): the
hand-authored seed growth in cli_run.rs (cookie header in, response headers out,
302) serves the roadmap OAuth product, not v2 self-host, so it fails the
v1_seed_standing PURPOSE test. cli_run.rs is restored to main; everything in
.dag that existed only to use it goes with it, so no .dag path emits a header
or a 302 the seed would silently drop (DESIGN §5):

- extdeps.http.server: the headers field, ServeResponseHeader and the
  with-headers constructors.
- roadmap_serve: the /auth/login, /auth/google/callback and /auth/logout
  routes, their handlers and helpers, the cookie_header params, and the
  no-store headers. Requests read as AuthAbsent. The comment-recorded answer
  is now 200 with the deep link in the body instead of 303+Location.
- gunbc.auth.login_transaction_store (no remaining importer); session.dag's
  callback-mint admit entry.
- extdeps.uri's percent decode (its only consumer was the callback) restored
  to main; extdeps_uri.rs regenerated.
- The Sign in / Switch account / Sign out UI, its client script and CSS.
  The witnesses now assert those controls and routes are absent. The CSS
  digest pin is re-derived.

Kept (shared with /auth/session, the approval broker or profile projection):
gunbc.auth.session, session_store, request_authentication, oidc config and
verification, the /auth/session route and the hidden session span.

This also carries the one-line NFR edits batch 1 made in roadmap_serve,
roadmap_page and roadmap_served_observation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Main's #12610 non-fold-residue check, scoped to the diff, refused 151
wildcard-over-closed-coproduct sites in branch content
(NonFoldResidueRosterDiverged). Per the parent's ruling (construction first,
real rows only, no boilerplate):

- 147 eliminated: each wildcard arm became the explicit remaining variants,
  each carrying the old wildcard body, so behavior is unchanged. A long body
  that would otherwise be cloned moved to one private helper called from
  each arm (e.g. submission_segment_from_handoff, load_repository_whole,
  issue_comment_readback_response).
- 4 rostered in gunbc.non_fold_residue: three single-directive extractions in
  unit_standing and one ExecStart predicate in runner_microvm_slot_unit,
  each over the 26/27-variant SystemdServiceDirective. Each row has a
  site-specific reason and a dissolution trigger.
- 0 left for a ruling.

std_algebra.rs regenerated from dag/std/algebra.dag; fixed point verified.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…untime bound, not a zero one (review 74213)

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	dag/gunbc/spark/glm_group_b_launch.dag
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… arms, rostered

#12980 armed the typed census on main; the live line's wildcard arms predate it. Each gets its own
row under nfr_reason_landed_with_the_live_line with the owning-fold dissolution, as main did for
the arms that landed during its review; the stale roadmap_standings_read row is deleted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- roadmap_authority.dag: main's #12993 bound test-reference-discipline-remainder
  to its closing contract witness; re-expressed in the branch's node schema as
  execution: bound(contract: test_reference_discipline_remainder_execution_contract()).
  The closing witness passes (1/1).
- non_fold_residue.dag: main's typed census (#12610 follow-up) rosters 84 of
  the 151 diff-scoped sites. 80 of those are now exhaustive matches on this
  branch, so their rows would be stale and are deleted. The 4 SystemdServiceDirective
  sites keep main's rows, and the branch's duplicate rows are dropped.
- Generated: v1_compiler_infer_method.rs and v1_interpreter_dispatch_generated.rs
  regenerated (byte-identical to the textual union; fixed point verified);
  ROADMAP.md re-derived by main_wet.

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

Another writer pushed 8 commits to the branch while this work was in flight
(the parent confirms it is not theirs). Their line is merged, not overwritten:
- Kept: the 13 over-budget roadmap witness removals (operator ruling 2026-10-02),
  the main merges, and the allocation page's stylesheet parameter.
- non_fold_residue.dag: their 67 rows under the single shared reason
  nfr_reason_landed_with_the_live_line were all at sites this branch now spells
  exhaustively, so those rows (plus 5 census rows at eliminated sites) are
  stale and deleted, along with the now-unused blanket reason (parent ruling:
  no blanket rows).
- roadmap_serve: the allocation page keeps the stylesheet argument, but not the
  no-store header (the response-header channel moved to the OAuth follow-up).
  allocation_serve_witness keeps this branch's side (the headers field is gone).
- roadmap_authority: kept their inline execution contract for
  test-reference-discipline-remainder; dropped the equivalent helper.
- Generated files regenerated from the merged authorities; fixed point verified.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- #12799 renamed EdgeLabel Named to Authored (now Authored | StructuralLabel |
  Positional): object_table_json's exhaustive MemberRead arms and
  integer.dag's widened v2.std.node import use the new constructor.
- floor_expected_red: the branch's chunk list plus main's new
  infer_checking_mode chunk (#12935).
Regen first generation equal; fixed point verified; scm object-table,
commit-closure and integer witnesses green.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
floor_cost_debt proven chunk 04: each side removed one row (the branch the
retired roadmap_row witness, main #12995 the split receipt-continuity
conjunction); both removals kept. design-rung-drops.md takes main's side per
the driver's route; the heal workflow re-derives it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Ledger-Repair-Judged: docs/design-rung-drops.md
Ledger-Rows-Repaired: docs/design-rung-drops.md edited_bin_witness_wet_rows_not_executed_by_ci
Heal-Candidate-Run: 37063203332
The NFR conversion of decode_runner_labels widened the file's imports, so
the file no longer carries the bare get pair; the floor refused the row as
RosterStale (run 37063203332).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in #12997, which retires annotation_channel_test#SubstrateInputsOnly
as ImportsFixed; without it the floor refused the branch head as
RosterRetirementChanged (run 37069606557). Clean merge: no generated path
changed on both sides.

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

Same class and remedy as 2ebc110's operator ruling 2026-10-02: a new
roadmap witness at 187,825 eval steps against the 72,300 budget (run
37070637789), already over on the branch before the merge lane. Its real
path still executes: dispatch_doc_html_for(ready_dispatch_node()) in
roadmap_page_witness line 294, and dispatch_client_js_source() in ~30
claims. The follow-up PR restores it cheapened to one interface.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tness step budget)

Same class and remedy as 2ebc110's operator ruling 2026-10-02: a new
roadmap witness at 94,116 eval steps against the 72,300 budget (run
37070637789), already over on the branch before the merge lane. Its real
path still executes: daily_workspace_view and workspace_markup run in
roadmap_presentation_witness (16 calls), and the refused-arm rendering
(activity-line-error-refusal) is asserted by witness_parity_refused_arm_labels_and_discloses,
witness_red_flagged_attempt_row and witness_issue_table_is_summary_only_...
The fx_shell_* fixture chain had no other consumer and is removed with it,
along with the two imports only it used. The follow-up PR restores it
cheapened to one interface.

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

Same class and remedy as 2ebc110's operator ruling 2026-10-02: a new
roadmap witness at 164,611 eval steps against the 72,300 budget (run
37070637789), already over on the branch before the merge lane. Its real
path still executes: sandbox_html() renders the whole real page in
witness_sandbox_carries_auditioner_control and the claim at the top of the
file. The four imports only it used are dropped, and the fixture comment
names the surviving whole-page claim. The follow-up PR restores it cheapened
to one interface.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
….1 moved onto it; GLM Group A staircase plans through it

- gunbc.spark.group_arm_launch: the deployment-independent plan / units / steps / admission /
  dry-run report / wet entry / transaction, parameterized by a FabricServingAssignment, profile,
  deployment env and names; the declared incumbent (option A): declared units only, read back
  active, stopped, hosts re-read vacant, restored and read back on anything but a commit.
- gunbc.spark.v41_group_a_launch: its generic body deleted; it supplies V4.1's inputs (held image,
  Engram staging, SM120 gate, candidate successor key, spec-key conjunct). 26/26 witnesses pass.
- gunbc.spark.glm53_arm_staircase: closed FP8 8/12/16/24 and NVFP4 8/16/24/32 steps, each through
  scheduler_envelope over the cold 8 x 262144 floor; successor key derived for D0.
- gunbc.spark.arm_group_launch_dispatch + modes spark_arm_group_launch(_plan) with closed
  arm / staircase_step / incumbent inputs (spark_v41_group_a_launch renamed; no new jobs).
- glm_group_b_launch and glm_serving_load stay frozen consumers of the old shape.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ve); staircase floor imports the GLM arm's rows (review 74379)

- gunbc.spark.group_arm_launch group_arm_occupancy_observe_session: per host, running service units,
  running containers (extdeps.docker.cli docker_ps_running_all_command) and GPU compute processes with
  their owning cgroup; changes nothing. How the handed-over incumbent is recorded as data.
- group_b_serving_capacity: group_b_restoration_max_batched_tokens named; the staircase imports the
  window, the eight cold seats and the batch ceiling instead of restating them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
std_algebra.rs (generated mirror of dag/std/algebra.dag) changed on both sides:
main via #12846 (Nat de-fork), the branch via the NFR exhaustive arms. It was
regenerated from the composed authority; the first generation equals the
textual union byte for byte, and the fixed point is verified. This merge is
what took #12787 out of the merge queue as UNMERGEABLE.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbc-ci-auto-heal and others added 6 commits October 5, 2026 15:29
… sweep is the waits/preflight claim plus one apply-and-convergence claim

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…apped only where the refusal is rendered (review 76631)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…om StartedAt, after a start) or ObservedNow (window from the host clock, of a running arm); the converge's satisfied check observes now, every post-start readback asks readiness from start, and the verdict refuses a trace that answered the other question (run 37328076674 restarted a healthy three-hour-old arm)

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Oct 5, 2026
… / stat_file_type_reading, lifted from gunbc.fabric.fabric_cell_acquire, which now consumes it); path_observation reads through it with HostPathKind (no PathKindRead collision); the byte bound is std.measure ByteSize; the #13365 frontier and its trigger are stated at the module

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbc-ci-auto-heal and others added 12 commits October 5, 2026 17:35
… rows StatFileType (stat.File.FileType, now with stderr) and HeadBytes (new extdeps coreutils.Head.Bytes, head -c), and gunbc.host.path_observation path_kind / small_file_read deciding regular, other, absent (stat's ENOENT text only) and unread in .dag

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… / stat_file_type_reading, lifted from gunbc.fabric.fabric_cell_acquire, which now consumes it); path_observation reads through it with HostPathKind (no PathKindRead collision); the byte bound is std.measure ByteSize; the #13365 frontier and its trigger are stated at the module

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…atches the kind exhaustively (RegularFileKind reads, every other variant is unread), the unused directory predicate is gone

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-775): arm_rank_observe reads the unit hash (stat + Sha256SumFile against sha256_hex of the planned text), ActiveState (SystemctlShowProperty), the container (docker inspect, decoded by extdeps.docker.container_inspect), the committed record (small_file_read) and the retired files (host_path_kind) through the privileged per-host ArgvRun; arm_rank_observe_script and its stdout parser are deleted; carries #13427's commits

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ap(..)) in the bracket arm did not resolve); arm_rank_observe takes the planned hash, computed once per rank by its caller, so its witnesses supply the digest and one claim pins arm_planned_unit_sha against GNU sha256sum

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…plied host refused the misspelled argv, as the route assertion should); the 241k-step planned-hash pin is withdrawn with its reason stated; the convergence sweep's floor counts its five scripts

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…xtdeps.tools.stat stat_file_type_outcome, one typed outcome: Observed | PathAbsent | Unread), so a path spelling the ENOENT phrase cannot make a refusal absent; the small-file read asks for limit+1 bytes and a longer file is SmallFileTooLarge, never its prefix; reds for both

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…llFileTooLarge is unread, never the record); carries #13427's terminal-reason and whole-file repairs

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…de description keeps main's new modes and this branch's converge sentence; fleet-converge.yml regenerated next

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rants_observe writes nothing, and the workspace pack, checkpoint-measure and checkpoint-restore modes run on srv1/srv3 and write no Spark arm

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…gate main_wet at 8f3c136)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Base automatically changed from session/cool-carp-342-readiness to main October 6, 2026 10:24
gunbc-ci-auto-heal and others added 7 commits October 6, 2026 10:30
…-way against readiness's tip b7914eb so the squash adds no spurious conflicts; the mode description keeps this branch's converge sentence; fleet-converge.yml regenerated next

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…en steps and no scheduled observe; the committed record carries each rank's scheduled end (expires=<ActiveEnterTimestamp + RuntimeMaxSec>, or none for a persistent unit; an unreadable start refuses the commit), read back as ArmCommittedExpiry, so a converge past it reports the arm ended as scheduled; the failure-mode row records both rulings and its new trigger

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s the result through Present (the EpochSecs/Int if-branches did not unify)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…upersedes the accepted-between-converges reading, and its trigger names the arm-unit exit capability (typed stop record plus ntfy push)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ces in a .dag string are interpolation: the module did not parse)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…stat, oom_kill, host PSI) before and after each step: the stop rules judged these readings but the receipt showed only the verdict; group_rank_pressure_wire in the shared load module, with a witness

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

superseded: Group A decommissioned, arm path has no remaining consumer

— sent from cool-carp-342

@gunbai-bot gunbai-bot Bot closed this Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants