Skip to content

Runner debug - #887

Closed
hnyls2002 wants to merge 4 commits into
mainfrom
runner-debug
Closed

hnyls2002 wants to merge 4 commits into
mainfrom
runner-debug

Conversation

@hnyls2002

Copy link
Copy Markdown
Collaborator

No description provided.

@hnyls2002 hnyls2002 closed this Aug 2, 2024
@Ying1123
Ying1123 deleted the runner-debug branch August 2, 2024 08:47
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 26, 2026
…-chunk exception carried onto the line, and the permission withdrawn at the execution) into the flip train
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 26, 2026
…own merges moved

The merge-regression run over the day's ticket suites turned up exactly one
failure outside the recorded standing set:
test_rejected_evidence_pins.py::PpWithSpecEvidenceTest::
test_evidence_cites_land_on_BOTH_halves_of_the_guard.

Not a defect in any merged fix. sgl-project#898 re-pinned this row against
be56c4e, and the same day's server_args.py additions -- sgl-project#889, sgl-project#893,
sgl-project#894, sgl-project#897 and sgl-project#887/sgl-project#890 -- then pushed the guard down 44 lines. The
verdict did not change; its coordinates did. :19436/:19451 -> :19480/:19495,
both re-read in the merged tree rather than taken from the failure text.

This is the guard doing its job: a line pin is a coordinate into a file
other tickets edit, so it goes stale at MERGE time, not at write time, and
only a train run can see it. #704b re-pinned the same row for the same
reason, so the note now says so instead of reading as a one-off.

CLASS. test_rejected_evidence_pins.py is the only line-pin validator in the
tree (grep for _LINE_REF / "cite has drifted"), and its other five cases
pass, so no sibling pin drifted unseen behind a missing guard. 6 passed.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
… die Policy vergleicht

== EIN FEHLENDES ARGUMENT, GEMESSEN STATT VERMUTET ==
`SUBCHUNK-SERVED-IN-TP` feuerte auf boot_855_1011idle NULL Mal, waehrend das
sgl-project#887-Gate 11x gewaehrte und die Policy 10x tp_to_pp auf "pending prefill 51
tok > 0" armte. Alle statischen Terme lasen erfuellbar, also war Weiterlesen
nicht die Antwort: ein Direktaufruf von `decide()` reproduzierte die LIVE-Zeile
byte-genau mit grant=0 und lieferte bei grant=51 die Unterdrueckung -- die
Policy-Haelfte ist korrekt, der Grant kam als 0 an.

WURZEL: `_pending_prefill_tokens(inflight=None)` -- der eigene Docstring sagt
"``inflight`` ... Passing it is what the flip policy MUST do". Die sgl-project#942-Sonde
liegt auf dem Flip-Policy-Pfad und uebergab es NICHT. `recv_requests` wertet
die Policy AUS, BEVOR die Requests in `waiting_queue` stehen (genau deshalb
existiert der Parameter), also mass die Sonde bei einer FRISCHEN ANKUNFT 0,
waehrend die Policy den echten Prompt sah. `0 < 0 < chunk` = False -> Grant 0
-> Unterdrueckung unmoeglich, und zwar fuer genau den Fall, fuer den sgl-project#887
gebaut wurde. Ein bs1-Strom macht ausschliesslich frische Ankuenfte.

== DER SCHNITT: EIN OPTIONALES DURCHREICHEN, EINE REGEL ==
`tp_compute_fits_in_one_chunk(scheduler, inflight=None)`. Default None haelt
JEDEN anderen Aufrufer byte-identisch -- insbesondere das ECHTE GATE
(phase_purity.py:1093) reicht bewusst nichts durch: ein Batch-Builder kann nur
bauen, was wirklich in der Queue steht. Die Fit-Regel bleibt EINE Regel (nicht
dupliziert), sie beantwortet nur je nach Aufrufer die passende Frage.
LECK-FAMILIE UNBERUEHRT (sgl-project#890/sgl-project#906): beide Sonden sind PURE READS,
`_spend_tp_compute_chunk` wird ausschliesslich aus `prefill_blocked_here`
gerufen. Zu sehen ist nicht zu verbrauchen.

== FEHLERKLASSEN-PASSENDE CHECKS ==
* Gefaehrlichste Klasse: `inflight_reqs` nicht im Scope -> NameError, vom
  umgebenden `except Exception` verschluckt -> stiller No-op. AST-geprueft:
  es ist PARAMETER von `maybe_arm_phase_policy` (13020-13664). In Scope.
* Verhalten je Aufrufer, Stand-in-Scheduler:
    frische Ankunft (Queue 0, inflight 51): Gate=False (UNVERAENDERT),
                                            Policy-Sonde=True   <- der Fix
    bereits gequeued (51):                  beide True  (unveraendert)
    ueber einem Chunk (9000):               beide False (Refusal erhalten)
    Legacy-Accessor ohne Argument:          True (TypeError-Fallback haelt)
* Diagnose-Zeile rate-limitiert (unlimitiert: 1457 Zeilen in EINER Zelle);
  Zaehler-Vorlauf per Direktaufruf belegt (3 nach 3 decide()).
* sgl-project#1011-Nichtregression per Direktaufruf: CASE1 refuses, CASE3 flips.

== ZELLEN (gleiche Harness, greedy, 600 tok, fuehrendes Unique-Token) ==
  Zelle            vorher (bc43f0d)      nachher (dieser Commit)
  bs1              25,09  n=5  Spr. 4,1%    72,0   n=7  Spr. 69,0%
  conc=4           88,64  n=3  Spr. 3,4%    134,93 n=3  Spr. 100,8%
  Flips/bs1-Zelle  12 auf 6 Requests        3
  SUBCHUNK-SERVED  0                        10
KEIN DOPPEL-PREFILL: `cached_tokens` ist in JEDEM Lauf 0, in schnellen wie
langsamen -- der Grant-Pfad fuehrt keinen Re-Prefill ein (sgl-project#939 unberuehrt).
EHRLICHE SCHRANKE: die Verteilung ist BIMODAL (bs1-Laeufe 67-80 tok/s, mit
einzelnen bei ~23), daher die grosse Spreizung. Der Median bewegt sich stark,
die Streuung ist NICHT Rauschen sondern zwei Regime -- welcher Anteil der
Requests noch ins alte Regime faellt, ist offen und nicht von diesem Commit
beantwortet.
BELEG-STUFE: BOOT-BEWIESEN (boot_855_942c, boot_855_942cfinal).
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 31, 2026
…omote, and the silently-wrong pair

ONE build closing the publish -> cached_tokens chain, plus the health-probe
confound that made every measurement of it ambiguous.

THE TWO BINDING TERMS (both desk-proven at the tree, both root, not symptom):

1. The promote `pending -> effective` existed at exactly ONE site, inside
   `_event_loop_pp_body`. The warm prefill on this rig runs boot-measured in
   the TP phase (the instance flags it itself: `sgl-project#838 work_in_wrong_layout
   class=prefill phase=tp`, 9 genuine violations), where that body never runs.
   PP0 published into `pending` and nobody ever promoted it: `sgl-project#968 LOAD-BACK
   PROMOTED` = 0/0/0 over three whole boots, and that is a REAL zero -- its
   cadence counts promotions, so the first would have printed.
   Fix: `promote_pp_load_back_pending()`, idempotent, called from both loop
   bodies AND from `_get_new_batch_prefill_raw` (the phase-neutral consumer),
   above every early return.

2. `init_pp_loop_state` nulled `_pp_load_back_pending` UNCONDITIONALLY while
   the line directly below guarded `_pp_load_back_effective` with `hasattr`.
   The unconditional null was deliberate ("only pending is a per-pass
   quantity") and is only fatal BECAUSE the promote was phase-bound. Fix 1 is
   the root; this is the guard that carries the fact across the cutover.

REGRESSION IN e0bc960, FOUND AND FIXED: that commit moved the offer write
to the load-back site but kept only the `_hit > 0` half, deleting the only
deleter -- the old `= _hit if _hit > 0 else None`. The comment it removed
states the lost property verbatim ("goes to None on its own"). The offer then
outlived its own truth: stale-HIGH re-publish -> a peer told an extent its
host tier no longer covers -> `EXTENT UNHONOURABLE` -> PPScheduleRefused,
which voids the pass for the WHOLE GROUP. Deleter restored at the spend.

STAGE A: the stamp population and the entry population were two different
sets -- the stamp sits behind eight `continue` branches (`already_in_batch`
is the sgl-project#946 shape: `add_chunked_req` appends the continuation to
`can_run_list` BEFORE the loop, which then skips it), while the decision is
built from `can_run_list`. Boot-measured in the gap: `loop_skips(
prefetch_pending=1(first=bce57cf8...))`, 3x, the same 4618-token rid. The
key is now stamped over `can_run_list` itself, so the two populations are
identical by construction. This also closes the NEXT link, which was already
visible: the publish filter needs `load_back_len AND load_back_key`, so an
entry with a live offer and no key would still have published 0.

SILENTLY-WRONG PAIR, mandatory in this build because fixes 1+2 are what arm
the path -- once live it returns wrong tokens rather than crashing:
* The adopted GDN state is the node's END state, at `_applied`; the S1 clamp
  cuts KV to `told` and cannot cut a transfer that already ran. The scan
  would resume at `told` from a state that has consumed `told.._applied-1`.
  No assert can fire; the KV geometry is self-consistent at `told`. And it is
  the EXPECTED relation, not a corner (a4c9a7a: "stale-low by
  construction", measured 1215 -> 1216 -> 2114). The whole load-back now
  falls instead; the state is NEVER trimmed, per the hazard the code already
  spells out. The slot is returned before the raise, because the exception
  bypasses the sgl-project#991 revert site.
* Mamba slot starvation used to skip the restore while the KV prefix grew to
  `told` -- warm-booked prefix over a state never loaded. It now fails the
  whole load-back. KV extent and GDN state stand or fall together, one rule.

HEALTH ISOLATION (sgl-project#942, standing user order): a /health_generate probe is a
liveness instrument, not work. Upstream only dispatches one when the box is
`is_fully_idle`, so at idle the probe IS the entire backlog, and under purity
`tp_threshold` is 0 -- its single token decided the layout of the whole
instance. Excluded from all FOUR backlog terms via
`_pending_prefill_tokens(include_health=False)` and
`_admissible_prefill_tokens(include_health=False)`, at the economy readers
only.

THE SPLIT IS AT THE CALL SITE, AND THAT IS THE WHOLE POINT. A blanket
exclusion is the obvious form and is wrong in the silent direction:
`tp_compute_fits_in_one_chunk` grants only while `0 < pending < chunk`, so
subtracting the probe there too collapses the sgl-project#887 grant and the probe is
served by nothing at all. Service readers keep the default; economy readers
exclude. No new tag: upstream already stamps the rid at the
/health_generate source, and its own predicate reads `.rid` via getattr, so
it applies unchanged to Req, TokenizedGenerateReqInput and PPAdmissionEntry.

MEASURABILITY (without these the next boot cannot read its own result):
* `cached_tokens` was structurally blind for exactly the population the warm
  acceptance lands on: the booking sits under `if not req.retracted_stain`
  and the stain is an ever-flag, while the cutover design retracts and
  re-admits everything. Now booked per admission by assignment (never `+=`,
  so the double count the stain guards stays impossible) and the stain is
  kept, because two other consumers read it.
* The publish probe counted PASSES: with fewer than 64 publishes the `%64`
  arm never fired and the whole budget went to the boot's cold head, leaving
  every warm opportunity unobserved. Now event-cadenced, and health is its
  own column rather than mixed into the denominator.
* `sgl-project#783` named a term that was not the one firing (it printed two identical
  numbers); each of the three terms now names itself.
* The `sgl-project#998` sample printed `items()[:3]` of a never-pruned dict, i.e. the
  OLDEST entries forever -- one genuine event echoed 35k+ times, which reads
  as a request that stayed resident all boot. Now `[-3:]`.

Ordering guard at `truncate_prefix_to`: it zeroes `host_hit_length`, which
the offer is captured from. Dormant only because sgl-project#1015 deleted the receiving
end; whoever restores that wire must re-check this order first.

Tests: test/srt/test_health_isolation_942.py, 7 tests against the production
functions, all green; can-fail proven by mutation (exclusion -> no-op gives 3
failures, incl. "a health probe armed the tp-ward flip"). Cross-module
exception identity for the new refusal verified as identical. AST checks on
signature/call-site agreement (9 sites) and on control-flow ordering (35/35,
24 failures against pristine HEAD). ruff 108 = 108 vs HEAD, no new findings.
Not proven on metal at the symptom -- that is the next boot.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 31, 2026
…t waited for a chunk the gate refuses to build

BOOT-PROVEN EDGE (boot_855_1050dev_0840f82601_0831_115825, the boot 20
livelock). The open question of the handover was WHY the broken chunk never
completes before the 30 s abandon. It is not a race: inside the armed window
12:22:19-12:22:49 PP0 logged ZERO `Prefill batch` lines and 15 `Decode batch`
lines. The chunk was never attempted, and the re-arm is in the SAME SECOND as
the abandon (12:22:49), so no unarmed window existed in which it could be.
N9's remaining candidate (abandon deadline vs chunk completion time under
growing backlog) is therefore FALSIFIED.

THE BINDING TERM, with file:line. `prefill_blocked_here`
(phase_purity.py:1087-1095) grants the sgl-project#887 one-chunk exception only when BOTH
`prefill_allowed_in_tp_now(spent)` AND `tp_compute_fits_in_one_chunk` hold --
deliberately, so the gate and the sgl-project#838 detector permit the same batches
(REGISTER_OPEN_876 on sgl-project#887). `tp_compute_fits_in_one_chunk` is `0 < pending <
chunked_prefill_size`. The last grant was 12:22:11 with new_tokens=3392 against
a 4096 cap; the backlog then crossed the cap and never came back (12347 ->
48866 -> 101075 tok), so the gate refused on every later round, SILENTLY, at
its final `return not _relaxed(...)`.

The grant is spent AT the grant (`_spend_tp_compute_chunk`), so a refusal on
the `fits` term leaves the budget untouched. The hold
(`prefill_runnable_in_current_layout`, phase_flip_runtime.py:156) read ONLY
that budget, kept answering "TP can still make progress", and
`chunk_blocks_quiescence` kept the rank un-quiescent for ever: PP0 armed 29 /
committed 4 / abandoned 48, `this rank ready=0` in all 72 abandon lines,
backlog 4618 -> 101075 tok.

CLASS: a hold predicate that predicts another gate's decision in a different
currency. SIBLING SWEEP over the three consumers of the sgl-project#887 grant: the gate
itself reads both terms; `maybe_arm_phase_policy` (sgl-project#942, scheduler.py:13611-
13632) reads both terms and says so in its own comment; the flip hold read
one. The fix makes the third consistent with the two that were already right,
by passing the gate's OWN side-effect-free probe rather than a second copy of
it. FUTURE CHECK: a new reader of `tp_compute_budget_remaining` that makes a
runnability claim must also read `tp_compute_fits_in_one_chunk`.

AND THE OPERATOR'S CONDITION, which turns this from a deletion into a
replacement. Once the hold correctly reports "this layout cannot finish the
chunk", the cutover commits over the broken chunk. That is licensed by sgl-project#988
riding the prefix across (loss <= 1 chunk = the sgl-project#939 grant) ONLY while the
COMPLETED pages are settled in the store. The old strict arm blocked on
"incomplete"; it now blocks on the real hazard -- an unacked prefix -- read
from the EXISTING sgl-project#792/sgl-project#703 fence (`ongoing_write_through`, the same counter
sgl-project#1050's `reclaim_rows_for_drop` and `on_idle` gate on), never a second ledger.
Unknown is refusal, never permission. The hold is bounded by the ack
(milliseconds), with the 30 s park deadline still the outer backstop; no
second clock.

UNTOUCHED AND PINNED BY TEST: mid-admission (no pool row -> still blocks),
non-strict (residual, still filed not solved), and pp_to_tp drain-and-flip
(runnable HERE -> still blocks, which is what keeps all prefill in PP).
The PP-microbatch-in-flight half has its own predicate and is not read here.

DETECTION: `_why_not` returned one string for three different holds. It now
names which term is holding, including the new unacked-prefix case.

DESK CHECK (matched to the error class of this edit -- a control-flow
inversion in a restructured predicate, which py_compile and import smoke are
structurally blind to): devtools/check_1033_quiescence_edge.py, 24 cases,
hermetic CUDA_VISIBLE_DEVICES="", all pass. Includes a RED-FIRST arm that
evaluates the PRE-FIX expressions on the measured boot-20 state (budget 2,
pending 48866, cap 4096) and asserts they produce the wedge, and the POST-FIX
expressions on the same state and asserts they commit only against an acked
prefix. Plus compile + import + an order proof that both probes are assigned
before either read inside get_next_batch_to_run.

NOT YET BOOT-PROVEN. This is DESK-PROVEN at the predicate; the metal proof is
boot 21.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 31, 2026
… while the peers spin

BOOT-PROVEN (boot 22, boot_855_1033b_0840f82601_0831_131955, 13:24-13:26).
The first TP forward after a pp_to_tp cutover touches a shape specialisation
never loaded in this process. Rank 0's crash-time stack (log line 53248 ff.):

  _dcp_write_scatter (flashinfer_backend.py:2574)
    run (triton/runtime/jit.py:743) -> _init_handles (compiler.py:466)
      loadBinary -> cuModuleLoadData (libcuda.so) + 11 libcuda frames

NOT the compiler -- cuModuleLoadData. The module is built; LOADING it into the
CUDA context blocks, because the load needs the device and the device is
saturated by the peers' barlink BAR1 spin kernels, which are waiting in
all_gather for this very rank. The cycle closes and the spin deadline fires:
Bar1CollectiveAborted (ranks 1/3 and 2/3, group flip_dcp:0) -> SIGQUIT.

WHY THE EXISTING MECHANISM MISSED IT, measured: 352 build-window lines in that
boot, ZERO in 13:24-13:26, all 352 carrying 'full cuda-graph capture warmup'.
cold_build_window had exactly three production callers (barlink BAR1 build,
sampling warmup #603b, capture warmup) and none is on the path a cutover
re-dispatches into. sgl-project#640 on a path sgl-project#615 never saw. The fix makes that path a
caller rather than building a second mechanism beside it.

A STALE COMMENT NEARLY REFUTED THIS FIX, and the correction ships with it.
sampler_warmup.py said wrapping a lazy build in cold_build_window "does NOT
work ... the window is PROCESS-LOCAL". True when written (8bddb93,
2026-08-06); falsified ONE DAY LATER by sgl-project#615 (38ec4fb, 2026-08-07), which
hooked publication into cold_build_window so "every existing call site
therefore becomes group-visible without moving". Never revised, both ancestors
of this pin, verified with git log -S rather than assumed. The paragraph is
annotated in place rather than deleted, because it is still right about ITS
module (warm-at-boot + barrier REMOVES the race; a window only EXTENDS it).

WHY A WINDOW OVER THE REAL FORWARDS, NOT AN ENUMERATED WARM SET. The warm set
is not knowable by inspection -- it varies with direction, spec-decode, the
sgl-project#887 one-chunk grant and any backend swap -- and missing one member reproduces
the wedge exactly, at the next first-loader. Whatever loads, loads under the
window. Residual stated in the code: this EXTENDS deadlines (900 s cap) rather
than removing the race; boot 22's stall was ~150 s, so the cap is not binding.

FIRST-LOADER CENSUS, as a standing table: devtools/CENSUS_1033c_first_loaders.md
-- every first-loader site x triggering state change x covering window x
collective proximity. Rows 1-6 verified at file:line (row 5 on metal); rows 7
(runtime recapture / drafter switch) and 8 (rung change sgl-project#704, resume-restore
sgl-project#89) are written UNVERIFIED, so the table is a lower bound on the covered set
and never a proof that nothing else is exposed. It also names the inverse
column (armed windows that may cover paths that no longer first-load -- the
352 are a count of WARMUPS, not of BUILDS, and the window carries no
modules-loaded counter, so live and dead coverage are indistinguishable from
the log) and answers the upstream-minimal question: there are not four window
mechanisms but two plus a front door, with one site (barlink_device.py:865)
bypassing the front door. Named, not rebuilt.

#1033d, same boot, INDEPENDENT DEFECT, minimal hardening only: the prefetch IO
aux thread caught only Empty, so a page whose geometry did not match the
incoming binding (mha.py:556 reshape, '[2,16,1,4,256]'=32768 against a 16384
page) ended the thread three seconds before the cutover -- killing no process,
setting no exit code, appearing in no health probe, and leaving storage
prefetch dead for the rest of the boot. It now fails the OPERATION (host slots
released, so the requester gets a refusal instead of an unreachable
completion), logs loudly with a counter, and stays alive. The broad except is
defensible only because the alternative is silent thread death with no
supervisor above the loop; the underlying two-geometry host pool across a flip
(sgl-project#718/sgl-project#719/sgl-project#875 family, and the fork's own 'sgl-project#939 RE-HOME VIA RE-READ ... source
page 16384 elems vs destination page 32768 elems' line names the same mismatch
two lines earlier while handling it correctly) is NOT fixed here and is its own
posten.

DESK CHECKS, matched to each edit's failure class:
* devtools/check_1033c_cutover_window.py -- 19 cases, hermetic, driving the real
  run_batch wrapper: RED-FIRST arm reproducing the boot-22 condition (counter
  unarmed -> no window -> nothing published), first forward covered AND
  published, window closes (the sgl-project#431 open-without-close shape), budget finite
  and steady state byte-identical, window closed and budget spent even when the
  forward raises, and an AST check that the arming sits under no `if` (the naive
  grep form of that assertion failed on a COMMENT -- fixed to ask the AST).
* AST check on the aux thread: broad handler present, logs at ERROR, releases
  the failed operation's host slots, and continues.
* Method-split check: wrapper delegates, window only on the armed path, lazy
  import off the fast path, inner body intact.
ruff F401/F821/UP037 clean.

DESK-PROVEN. Metal proof is boot 23.
efschu pushed a commit to efschu/htsglang that referenced this pull request Sep 1, 2026
…d a pp_to_tp/tp_to_pp livelock, 0 decode

Bisected against a51e5e8 (last decode-green boot, 297 decode batches).
git bisect narrowed to an ambiguous parent/child pair
(96bdb4a / 67b08b8) after several sgl-project#631-style crash-only commits
were skipped; direct-parent verification (67b08b8^ == fdb7b21, the
last confirmed-good commit) resolved it definitively in favor of
67b08b8 ("[sgl-project#1033] The flip hold asked half the one-chunk grant").

That commit added two new terms to the pp_to_tp/tp_to_pp quiescence
predicates: `fits_in_one_chunk` in `prefill_runnable_in_current_layout`
and a new `completed_chunk_pages_acked` ack-gate consumed by
`chunk_blocks_quiescence`. Root-caused via a first, WRONG partial revert
(kept `fits_in_one_chunk`, reverted only the ack-gate) that still failed
on the metal with a NEW symptom: 51 flips alternating every 27-40s (a
flip-flop livelock, 0 decode) plus a separate sgl-project#928-anchor retraction loop
on one request. Reading the full original diff showed the pre-sgl-project#1033 code
never had an ack-gate either (unconditional `return False`), so that half
of my hand revert was a no-op; `fits_in_one_chunk` was the actual driver.

This reverts 67b08b8's entire diff (both functions plus the
scheduler.py wiring in `build_flip_quiescence_fn`), applied at the
current branch tip via `git revert --no-commit` rather than a blanket
file-copy from the old commit: none of the 12 commits that touched these
two files between 67b08b8 and this tip touch these functions (only
one, [#1054d], and it edits an unrelated region ~11000 lines away), so
the revert applied with zero conflicts and preserves every later commit's
work in both files intact.

Per upstream-minimal: the sgl-project#1033 mechanism was itself a fork-own
compensation layer over the sgl-project#887 one-chunk grant; its own defect (an
unreachable-in-practice "both halves must agree" premise that instead
produced a livelock) makes it a deletion candidate, not a repair target.
Deleted the now-stale devtools/check_1033_quiescence_edge.py alongside it
(it exercised `fits_in_one_chunk`/`completed_chunk_pages_acked`, both
gone).

Test results (BOOT-PROVEN, two independent boots, driver load
disc631_driver.py --duration 480 --words 24000 --concurrency 2):
  - boot_855_fullrevert1033_67b08b87b6_0901_044806.log (smoke, at the
    bisect commit): VERDICT=GOOD, 15 decode batches, single clean
    pp_to_tp flip (no flip-flop), 0 crashes.
  - boot_855_tiprevert1033_2b23c255fb_0901_050011.log (this exact commit
    content, at branch tip): VERDICT=GOOD, 12 decode batches (accept
    rate 0.66), single clean pp_to_tp flip (3 reconciled rank-flips, no
    flip-flop), 0 Tracebacks, 0 sgl-project#631 crash markers, 0 sgl-project#928-retraction
    markers.

Not yet run: the >=30 min real-router-agent-load acceptance test
(Phase 3). This commit closes Phase 1/2 (bisection + root-cause fix,
boot-proven) only.
efschu pushed a commit to efschu/htsglang that referenced this pull request Sep 1, 2026
…flip hold and the stale re-admission order

sgl-project#1065 (livelock boot_855_tiprevert1033 05:04:53-05:23Z): chunk_blocks_quiescence
loses its strict clause and the #858b/sgl-project#887 runnability oracle
(prefill_runnable_in_current_layout) with both call sites. The clause held
tp_to_pp on an incomplete chunk whose continuation the builder's own gate
(fits_in_one_chunk + seam grant) refused to build: budget>0 said runnable,
grant=0 said never -- 37 abandons/rank over 1114 s, 11 queued / 0 running,
abort of the hanging rid deferred until after a cutover that never came.
A flip commits at any settled chunk boundary now; re-admission serves the
committed prefix by read-through (cutover-full-reset design).

sgl-project#1066 (cached=0 on 90/90 prefills, same boot): re-admission ran BEFORE the
pool rebind, so every intake prefetch opened on the outgoing binding and
completed into a sgl-project#937 stale refusal; the #943b one-rid-per-round vote (a CPU
all_reduce on the admission path) re-fetched one request per round, too late,
and the recompute raced the fetch into an sgl-project#841 decline. Deleted: the vote
(take_agreed_reissue), the owed ledger (_reissue_pending), the #1025b shim
(reissue_seam_prefetch). Replaced by construction: the requeue is deferred to
after _cutover_fn (_post_cutover_readmit), which also sweeps the queue's
stale prefetch records through the ordinary _prefetch_kvcache
(retire-then-register); PP0 waits for its own drained prefetch verdict
(#969Z revisited: row authority delivers membership same-pass, followers
still never decide).

Desk-proven: py_compile, predicate truth table, _post_cutover_readmit smoke,
10/10 deletion falsifiers (test_968_deletion_falsifiers.py).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant