Skip to content

Fix wrong assert - #1028

Merged
hnyls2002 merged 1 commit into
mainfrom
fix-chunked-cache
Aug 11, 2024
Merged

hnyls2002 merged 1 commit into
mainfrom
fix-chunked-cache

Conversation

@hnyls2002

Copy link
Copy Markdown
Collaborator

Thank you for your contribution, we really appreciate it. The following instructions will help improve your pull request and make it easier to receive feedback. If there are any items you don't understand, don't worry. Just submit the pull request and ask the maintainers for help.

Motivation

The assert req.rid in self.entries is wrong when the first token is eos after prefill.

Modification

Remove the assert and delete the element if it is in.

Checklist

  1. Ensure pre-commit pre-commit run --all-files or other linting tools are used to fix potential lint issues.
  2. Confirm that modifications are covered by complete unit tests. If not, please add more unit tests for correctness.
  3. Modify documentation as needed, such as docstrings or example tutorials.

@hnyls2002
hnyls2002 enabled auto-merge (squash) August 11, 2024 09:20
@hnyls2002
hnyls2002 merged commit fcc0f5e into main Aug 11, 2024
@hnyls2002
hnyls2002 deleted the fix-chunked-cache branch August 11, 2024 09:22
timethink pushed a commit to timethink/sglang that referenced this pull request Mar 9, 2025
Hide-on-bushsh pushed a commit to Hide-on-bushsh/sgl-project that referenced this pull request Jul 20, 2026
* revert coverage

* revert

* revert
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…fixt -- Hold-Text ehrlich gemacht; Abnahme-Menge komplett

== WAS ICH ZUERST EINRAEUME ==
Der LEBENDE Zustand war weg, bevor die Freigabe kam: ich hatte den
gestrandeten Prozessbaum per SIGKILL geraeumt, um Serving zurueckzuholen. Die
py-spy-Stacks sind gesichert, Queue-/Seat-/Chunk-Zustand nicht. Geerntet wurde
daher aus dem BOOT-LOG (das die Felder traegt), nicht am lebenden Prozess.

== BEFUND, aus dem logindex-Ingest des Strandungs-Boots ==
boot_855_fix1027_0840f82601_0830_112900.log, 708.146 Zeilen, voll ingested.
Ein sauberer Schnitt: ALLES Request-bezogene endet 11:57:55-11:58:07
(admission, admission_suppressed, prefill_batch/rank, output_fill,
extend_invariant, hicache_io/round, flip_cutover/reconciled). NUR policy_hold,
flip_event, collective_census und cohort_read laufen bis 12:04:5x weiter.
Der Scheduler lebt; die Pipeline ist ab Admission tot.

Im Strandungsfenster (ts > 11:58:10) gibt es GENAU EINE Hold-Begruendung:
  "decoding in tp"  n=29, 11:58:17 .. 12:04:51
Gegenprobe: letzter Decode-Batch 11:57:45, NULL Decode-Batches nach 11:58:10.
Die Policy hielt also 6,5 Minuten auf einer Begruendung, die einen Decode
behauptet, den es nicht gab.

== ABER: DIE POLICY IST SYMPTOM, NICHT URSACHE -- und das ist der Grund,
warum hier nichts "gefixt" heisst ==
`phase_policy.py:3696` ist der FALL-THROUGH des tp-Zweigs. Er wird erreicht,
wenn `work_exists()` False ist -- und `work_exists()` fragt AUSSCHLIESSLICH
nach Prefill (`pending_prefill_tokens` / `admissible_prefill_tokens`). An
dieser Stelle weiss der Code also: es ist kein Prefill geschuldet. Ueber
Decode weiss er NICHTS. Der Hold ist damit die korrekte Antwort auf "mir wurde
nichts angetragen" -- und PP0 stand passend dazu in `is_fully_idle`, der
Detokenizer wartete gesund auf seinem Socket, der Client wartete auf RM-13.
DER REQUEST IST OBERHALB DER PHASE-POLICY VERLORENGEGANGEN. Die Wurzel liegt
im Request-Intake, ist NICHT lokalisiert, und ist damit >S/M und ein
Kandidat der sgl-project#968-PP0-Familie -> MELDEN STATT BAUEN, wie beauftragt.

== WAS GEBAUT WURDE: nur die Ehrlichkeit des Instruments ==
`_no("decoding in tp")` -> nennt jetzt den Term, auf dem der Zweig steht, und
unterscheidet die zwei Faelle, die im Log identisch aussahen:
  running_bs>0 : "decode is in flight, this is a normal hold"
  running_bs=0 : "NOTHING IS DECODING EITHER; if a client is waiting, its
                  request never reached this scheduler (sgl-project#1028)"
VIERTE instrument-text-luegt-Instanz des Tages, und die einzige, die mich
Zeit gekostet hat: die Zeile schickte die Suche nach einem haengenden Decode,
den es nie gab. Detektion richtig, Erklaerung erfunden.
Prior-Art-Gate gelaufen (rrf): naechste Verwandte sind sgl-project#962 (batch_is_full-
Latch nach tp_to_pp), #888b D1 (Sitz von einem Residenten gehalten, den die
Phase nicht fahren darf) und die Stranded-Decode-Surcharge-Commits vom
2026-08-15. KEIN Neubau abgeleitet -- die Wurzel ist nicht dieselbe.

== ABNAHME-MENGE KOMPLETT, alle fuenf Packs auf oder ueber Referenz ==
  toolcall-15        13/15   Ref 12
  instructfollow-15  15/15   Ref 14
  structoutput-15    14/15   Ref 14
  dataextract-15     13/15   Ref 11
  reasonmath-15      14/15   Ref  7
  SUMME              69/75   Ref 58/75
RM-13, die Frage der Strandung, lief diesmal DURCH (96,7 s -- auffaellig
langsam gegen die sonst ~20 s, aber bestanden). Deadman still.

ZWEI SCHRANKEN, die das Ergebnis NICHT ueberschreiten darf:
1. Es ist KEIN Ein-Build-Durchlauf. Packs 1-3 liefen auf dem Stand VOR sgl-project#1027,
   Pack 4 auf dem sgl-project#1027-Stand, Pack 5 auf diesem. Keiner der beiden Fixes
   aendert Generierungsinhalt (greedy, gleiche Token), aber gemessen ist es
   ueber drei Staende, nicht einen.
2. DASS RM-13 durchlief, ist KEIN Beleg, dass sgl-project#1028 behoben ist. Ich habe
   einen LOG-TEXT geaendert, sonst nichts. Der Defekt ist intermittent und
   hat diesmal nicht gefeuert. sgl-project#1028 bleibt OFFEN.

BELEG-STUFE: Lokalisierung BOOT-BEWIESEN (Ingest des Strandungs-Logs, Zeit-
schnitt und Hold-Zensus). Wurzel NICHT bestimmt. Label-Fix DESK-BEWIESEN
plus boot-gelaufen. Abnahme-Zahlen BOOT-GEMESSEN ueber drei Staende.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…inert/unerreichbar -- die Suche zieht auf S1. Und die Inventur haelt am Pin NICHT.

Fokussierter Durchgang auf den zwei einzigen als erreichbar+UNKLAR gefuehrten
S0-Intake-Gates. Kein Schnitt gebaut: es gab nichts zu schneiden.

== G-06 `drain_recovery_request` -- INERT, NICHT die Wurzel ==
Zeile haelt am Pin (scheduler.py:2860 in `process_input_requests`, ein
resolved Caller). Der Code liest `RECOVERY_CHANNEL_ATTR` und gibt bei None
sofort zurueck; den Kanal legt AUSSCHLIESSLICH der Watchdog-Post-Pfad an. Ohne
erkannten Wedge ist das ein getattr und ein None -- die `waiting_queue` wird
nicht angefasst.
Empirisch am Strandungs-Boot (boot_855_fix1027_..._112900.log): Kanal NIE
angelegt. WEDGE-RECOVERY genuine 0, RecoveryOutcome 0, Attribut 0.
NEBENBEFUND, Lehrstueck fuer die sgl-project#995-Regel: 'recovery channel' zaehlt bare
31 / genuine 0 -- reine Prosa-Erwaehnungen. Ein blanker grep -c haette hier
31 "Treffer" gemeldet und das Gate faelschlich als aktiv ausgewiesen.
VERDIKT: nicht die sgl-project#1028-Ursache. Und KEIN Loesch-Kandidat auf Kanal-Ebene:
der Kanal ist die Thread-Grenze (Watchdog -> Scheduler-Thread) eines
fork-eigenen Features, kein zweiter Datensatz der waiting_queue. Die
Inventur-Beschreibung "Recovery-Request vor den Input-Strom injiziert"
ueberzeichnet den gesunden Pfad.
DESIGN-FORK, GEMELDET STATT ENTSCHIEDEN: das FEATURE Wedge-Recovery ist
seinerseits eine Kompensationsschicht fuer fork-eigene Wedges, und das ist
per Upstream-Minimal-Gesetz ein Loesch-Kandidat auf FEATURE-Ebene. Loeschen
aendert aber nutzer-sichtbares Verhalten (ein gewedgtes Rig erholt sich dann
nicht mehr selbst). Das ist ein echter Wert-Trade -> Operator-Entscheid.

== G-04 Prefetch-Gruppenvotum -- UNERREICHBAR auf der Ship-Config ==
Die Inventur nennt scheduler.py:5085,5139. Am Pin steht dort etwas anderes
(`routed_experts_start_len` bzw. ein Prefetch-Docstring). Ueber den EMITTIERTEN
Literal statt ueber die Zeile relokalisiert: der Code lebt in
`disaggregation/decode_hicache_mixin.py:152-197`, also im PD-Disagg-Pfad.
Unser Boot faehrt `disaggregation_mode='null'`; der Mixin wird nur von
`disaggregation/decode.py` verwendet. Marker `anchor_no_vote` im
Strandungs-Boot: bare 0 / genuine 0.
VERDIKT: auf dieser Form nicht erreichbar, also nicht die sgl-project#1028-Ursache. Die
"decisive check"-Frage der Inventur (ueberlebt ein Konsument des Votums G-32?)
ist auf der Ship-Config gegenstandslos; sie bleibt fuer die Disagg-Form offen.

== META-BEFUND, der die sgl-project#968-Arbeitsweise betrifft ==
BEIDE von mir geprueften S0-Zeilen hielten am Pin NICHT: G-06 ueberzeichnet
die Wirkung, G-04 nennt die falsche DATEI und eine falsche
Erreichbarkeits-Spalte. INVENTAR-1003 steht auf `feat/969-deletion-cut @
90c5ada`, einem fremden Branch, und ist einen Tag alt.
Die Inventur bleibt wertvoll als Karte, WO zu suchen ist -- ihre
Erreichbarkeits-Spalte traegt am Pin aber KEINEN Schnitt ohne Gegenpruefung.
Fuer den restlichen sgl-project#968-Umbau gilt damit: jede Zeile wird vor einem Schnitt
per emittiertem Literal am Pin relokalisiert und per Marker-Zaehlung am
Boot-Log auf Erreichbarkeit geprueft, nie per Zeilennummer uebernommen.
(Das ist die Determinations-Schnappschuss-Regel, jetzt mit zwei Belegen.)

== FOLGE FUER sgl-project#1028 ==
Station S0 ist als Verlustort AUSGESCHLOSSEN: von sieben Fork-Gates sind drei
strukturell unerreichbar (G-01/02/03), zwei lassen den Request per Verdikt
ohnehin durch (G-05/G-07), und die beiden UNKLAR-Zeilen sind inert bzw.
unerreichbar. Die Suche zieht auf S1 (ROUND ENTRY / Admission) weiter -- die
Station, an der auch das Admission-Verdikt sitzt, das nach der sgl-project#968-Order zu
PP0 gehoert. Damit faellt der sgl-project#1028-Einstieg mit dem Umbau-Kern zusammen,
statt daneben zu liegen.

BELEG-STUFE: DESK-BEWIESEN (Code am Pin) + BOOT-BEWIESEN (Marker-Zaehlung
trapsafe am Strandungs-Log). Kein Boot ausgegeben, kein Code geaendert.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…ONGRUENT ZUGELASSEN -- meine "nie angekommen"-Erklaerung ist FALSCH

S1-Durchgang, Schritt 1. Die rid-genaue Timeline aus dem Strandungs-Log
kippt meine eigene Diagnose, und zwar in die praezisere Richtung.

== DIE TIMELINE (logindex, boot_855_fix1027_..._112900, voll ingested) ==
Nach 11:57:30 nennt das Log GENAU EINEN rid: 2fe8dab34ab1.
  admission   n=18   11:56:14 .. 11:58:05
  flip_event  n=100  11:56:17 .. 12:02:21
Die 18 Admission-Zeilen sind SECHS Runden zu je drei Raengen, und JEDE ist
  "sgl-project#788 PP-ADMISSION verdict=ADMIT n_reqs=1 rids=2fe8dab34ab14d"
auf Rang 0 UND 1 UND 2. Die Raenge waren sich EINIG. Letzte Zulassung
11:58:05; letzter Prefill 11:58:07; danach nichts mehr.
Und der Flip-Apparat nennt denselben rid noch bis 12:02:21 in
FLIP EXTENT PROBE auf allen drei Raengen -- der Request ist also die ganze
Strandung ueber RESIDENT, nicht verschwunden.

== SELBSTKORREKTUR (fuenfte, und sie stand im CODE) ==
Mein Commit 70d85ed schloss: "DER REQUEST IST OBERHALB DER PHASE-POLICY
VERLORENGEGANGEN", und ich hatte diese Erklaerung in die sgl-project#1028-Log-Zeile
geschrieben ("its request never reached this scheduler"). Das ist
FALSIFIZIERT: er hat den Scheduler erreicht, wurde sechsmal von allen drei
Raengen zugelassen und blieb danach resident. Die Zeile haette den naechsten
Leser genau so fehlgeleitet, wie mich "decoding in tp" fehlgeleitet hat --
also derselbe Fehler, den ich am selben Tag viermal katalogisiert habe, von
mir selbst begangen und in den Baum committet. Text korrigiert auf
"admitted and then stopped progressing somewhere DOWNSTREAM of admission".
Der Grund, dass ich es fand: die rid-Timeline, nicht erneutes Nachdenken.

== FOLGE: DER VERLUSTPUNKT LIEGT NICHT AUF S0 UND NICHT AUF S1 ==
Intake (S0) war bereits ausgeschlossen. Admission (S1) ist hiermit ebenfalls
ausgeschlossen -- sie hat kongruent ADMIT gesprochen. Der Defekt liegt
ZWISCHEN Zulassung/Prefill und dem laufenden Batch, also auf der MERGE-Bahn
(S2). Damit ist die Station benannt, an der weitergesucht wird.

== EIN KANDIDAT, AUSDRUECKLICH NOCH NICHT ALS URSACHE BEHAUPTET ==
Am Strandungspunkt feuert auf allen drei Raengen
  "SELF-MERGE REFUSED: last_batch is running_batch (bs=1)"
(scheduler.py:7427-7457). Formal ist das genau die vom Gesetz verbotene
Form: ein Guard, der einen Zustand erkennt, den sein eigener Kommentar
"the resident set is corrupted" nennt, und dann REFUSAL-UND-WEITER macht
statt die Gruppe zu stoppen -- und der Kommentar sagt selbst "a detector
that only declines to act cannot stop a doubling -- the instance still
died". Nach Upstream-Minimal ist ein Defekt in einer Kompensationsschicht
ein LOESCH-KANDIDATEN-Befund; die eigentliche Frage ist, warum
`last_batch is running_batch` ueberhaupt gilt (Aliasing).
ABER, INDIKATOR-GESETZ: der Marker feuert 166.173-mal auf diesem Boot,
also auch waehrend der ~26 Minuten, in denen alles funktionierte. Er ist
damit NICHT hinreichend fuer die Strandung und wird hier als
KO-OKKURRENZ gefuehrt, nicht als Ursache. Wer ihn ohne diese Zahl zitiert,
berichtet eine Korrelation als Wurzel.

== NAECHSTER SCHRITT (nicht mehr in diesem Zug) ==
S2/Merge-Bahn mit derselben Disziplin: Zeilen per emittiertem Literal am Pin
relokalisieren, per trapsafe zaehlen, und die Frage stellen, die die
sgl-project#1031-MERGE-PATH-PROBE (scheduler.py:7462ff) bereits woertlich formuliert --
"`#running-req: 0` heisst entweder der Prefill hat NIE gemerged, oder er
merged und sgl-project#856-no-carry hat ihn vor der Decode-Runde retracted; nichts
Gemessenes trennt die beiden". Genau diese Trennung ist jetzt die Aufgabe,
und die Probe dafuer liegt schon im Baum.

BELEG-STUFE: BOOT-BEWIESEN (rid-Timeline aus dem voll ingesteten Log).
Kandidat SELF-MERGE: BEOBACHTET, ausdruecklich nicht kausal belegt.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…gl-project#1027 einen Aufruf frueher -- mein eigener Fix war halb. Merge-Frage: NULL.

Reproduktions-Budget eingehalten: EINE Sequenz Pack4->Pack5. Sie strandete
nach ~13 Minuten, bei DE-07 -- derselben Frage wie beim Original.
UND DIESMAL WURDE VOR DEM RAEUMEN GEERNTET (die Regel, die ich letztes Mal
gebrochen habe). Specimen /spinning/gpu-arb/W1028-LIVE-CATCH/.

== DREI STACKS, EIN MOMENT ==
  PP0  active       all_reduce <- _update_uniform_pool_budget (scheduler.py:6186)
  PP2  active       all_reduce <- _update_uniform_pool_budget (scheduler.py:6186)
  PP1  active+gil   memory_stats <- memory_reserved
                    <- _allocator_cache_bytes (corridor_admission.py:715)
                    <- _takeable_cache_bytes:734 <- spendable_bytes:620
                    <- granted_width:635 <- _local_corridor_width_ceiling:8343
                    <- _update_uniform_pool_budget (scheduler.py:6121)
Zwei Raenge warten im Kollektiv, der dritte hat es nicht erreicht. Exakt die
sgl-project#1027-Form, anderer Aufruf. Eine fruehere Probe derselben Episode fand PP0 bei
`available_size (memory_pool.py:383)` <- `get_num_allocatable_reqs`
<- `_local_admit_limit (scheduler.py:6612)` -- die Straggler-ROLLE ROTIERT,
die Region ist fuer mehr als einen Aufrufer langsam.

== SELBSTKORREKTUR (sechste): MEIN sgl-project#1027-FIX WAR HALB ==
sgl-project#1027 legte `torch.cuda.memory_snapshot()` (:748-750) hinter eine Kadenz.
Aber `_takeable_cache_bytes` ruft EINE ZEILE FRUEHER (:734)
`_allocator_cache_bytes()`, und das geht ueber `memory_reserved()` /
`memory_allocated()` ebenfalls durch `torch.cuda.memory_stats()`. Ich habe die
innere teure Haelfte gedeckelt und die aeussere auf dem Pro-Runde-Pfad
gelassen. Der Fix war ECHT (die sgl-project#1027-Signatur ist weg: kein Rang steht mehr
im Snapshot-Zweig) und UNVOLLSTAENDIG. Jetzt ist der GANZE Wert cadenced.

== DIE MERGE-FRAGE IST BEANTWORTET, UND ZWAR MIT NULL ==
Die sgl-project#1031-Probe war erschoepft, nicht stumm: sie verbrauchte ihren 200er-
Deckel in 37 s (11:32:16-11:32:53) und schwieg 25 min vor der Strandung; der
gestrandete rid kam in NULL Probe-Zeilen vor. Nachgeschaerft: Kopf-Stichprobe
BEHALTEN plus ein Arm, der immer feuert, wenn ein Extend-Batch Requests trug
und der Running-Batch danach leer ist (`why=NEVER-MERGED-CANDIDATE`) -- genau
die Diskriminante, die der Proben-Docstring als ungetrennt benennt.
Ergebnis ueber die ganze Episode: head 600, NEVER-MERGED-CANDIDATE **0**.
Also: nichts wurde zugelassen-und-am-Merge-fallengelassen. S2 faellt als
Verlustort aus.

== DAMIT IST sgl-project#1028 VON ANFANG AN FALSCH GERAHMT GEWESEN ==
S0 ausgeschlossen, S1 ausgeschlossen (kongruentes ADMIT), S2 ausgeschlossen
(Merge-Null). Es gibt keinen verlorenen Request: der Request ist da, und die
GRUPPE STEHT, bevor sie ihn bedienen kann. sgl-project#1028 ist kein Intake-Verlust,
sondern ein Liveness-Stall derselben Familie wie sgl-project#1027. Meine eigene
Rahmung "der Request ging oberhalb der Policy verloren" ist damit zum
zweiten Mal korrigiert -- erst zu "downstream of admission", jetzt zu
"gar kein Verlust".

== KLASSE, EINE EBENE HOEHER -- und das ist die sgl-project#968-Aussage ==
Nicht "ein langsamer Aufruf", sondern: `_update_uniform_pool_budget` leistet
UNBESCHRAENKT teure lokale Arbeit in der Region zwischen Rundenbeginn und
einem Kollektiv, das jeder Rang erreichen muss. Jeder Aufruf dort kann die
Gruppe aushungern, und einen zu deckeln befoerdert den naechsten:
sgl-project#1027 = memory_snapshot, dies = memory_stats, und
`available_size`/`get_num_allocatable_reqs` (:6174) ist der naechste
Kandidat. Der dauerhafte Fix ist KEINE dritte Kadenz, sondern die Region
konstruktiv kostenfrei zu machen -- keine Allocator-Introspektion zwischen
Rundenbeginn und Barriere. Das IST die sgl-project#968-Form: das Budget-Verdikt gehoert
zu PP0 und wird verteilt, statt von jedem Rang vor der Barriere lokal neu
gerechnet zu werden. Als Kommentar an der Fix-Stelle hinterlegt, damit der
naechste Leser die Kadenz nicht fuer das Ziel haelt.

== BELEG-STUFE ==
BOOT-BEWIESEN: drei Live-Stacks eines Moments, Straggler-Rotation, Proben-
Zensus ueber die volle Episode, Reproduktion bei derselben Frage.
NICHT BEWIESEN: dass dieser Fix die Strandung verhindert. Er ist gebaut,
kompiliert, gebootet (Instrument feuert: "WHOLE value cadenced at 30s"),
health 200, greedy kohaerent -- eine zweite Reproduktions-Sequenz zum
Gegenbeweis ist NICHT gefahren. Wer "behoben" sagt, ueberschreitet die Daten.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…z derselben Region. Per-Call-Kadenz ist WIDERLEGT.

Gegenbeweis-Sequenz gefahren wie beauftragt (pack4->pack5, Deadman armiert,
Live-Catch-Regel eingehalten). Ergebnis: FAIL, und der Fehlschlag ist der
staerkste Beleg fuer den sgl-project#968-Umbau, den dieser Strang bisher hat.

== WAS DER FIX NACHWEISLICH LEISTET ==
Pack 4 lief sauber durch: 13/15 (Referenz 11), DE-07 in 85,5 s -- also genau
die Frage, an der die Gruppe zweimal starb. Die Sites sgl-project#1027 (memory_snapshot)
und #1028a (memory_stats) tauchen in KEINEM Stack mehr auf. Die Deckel wirken,
je Site.

== WAS ER NICHT LEISTET ==
Bei RM-13 strandete es erneut: health 503, GPU 0% auf allen drei.
Drei Stacks, ein Moment:
  PP0  idle    all_reduce <- _update_uniform_pool_budget (scheduler.py:6186)
  PP1  idle    all_reduce <- _update_uniform_pool_budget (scheduler.py:6186)
  PP2  active  mem_get_info (torch/cuda/memory.py:842)
               free_bytes (corridor_guard.py:1086)
               spendable_bytes (corridor_admission.py:602)
               granted_width:635 <- _local_corridor_width_ceiling:8343
               <- _update_uniform_pool_budget (scheduler.py:6121)
`spendable_bytes:602` ist `free = float(guard.free_bytes())` -- die ERSTE
Zeile der Funktion, VOR dem :620, den ich gedeckelt habe.

== DIE DREI INSTANZEN ==
  1  memory_snapshot()  corridor_admission.py:748  gedeckelt (sgl-project#1027)
  2  memory_stats()     corridor_admission.py:715  gedeckelt (sgl-project#1028)
  3  mem_get_info()     corridor_admission.py:602  UNGEDECKELT
Alle drei in `spendable_bytes` oder dessen Callees, alle drei auf dem
Pro-Runde-Pfad vor der Barriere.

== VERDIKT: PER-CALL-KADENZ IST WIDERLEGT ==
Sie war ein vertretbarer erster Zug und wirkt je Site nachweislich. Aber die
Region hat mehr Allocator-/Treiber-Aufrufe als Deckel, die Straggler-Rolle
wandert auf den jeweils naechsten, und ein vierter Deckel befoerderte
`available_size`/`get_num_allocatable_reqs` (:6174) -- den eine fruehere
Probe bereits einmal als Straggler gefangen hatte.
ICH BAUE KEINEN DRITTEN DECKEL. Das waere die dritte Kompensation fuer
denselben strukturellen Fehler, und das Gesetz nennt genau das einen
Loesch-Kandidaten-Befund statt eines Fix-Auftrags.

== DIE WURZEL, jetzt empirisch belegt statt argumentiert ==
Nicht "ein langsamer Aufruf", sondern: JEDER RANG MUSS UNBESCHRAENKTE LOKALE
ARBEIT ABSCHLIESSEN, BEVOR ER EINE BARRIERE ERREICHT, DIE JEDER RANG
ERREICHEN MUSS. Drei Instanzen, drei verschiedene Aufrufe, dieselbe Region,
dieselbe Signatur.

== DESIGN-SKIZZE LIEGT VOR, BAU NICHT BEGONNEN ==
/spinning/gpu-arb/DESIGN_968_budget_verdict_to_pp0.md
Kernpunkt daraus, gemessen: `_update_uniform_pool_budget` existiert in
upstream `main` NULL mal, und `all_reduce` in upstream `scheduler.py`
ebenfalls NULL mal -- gegen 8 im Fork. Upstreams Scheduler-Schleife macht
GAR KEIN Kollektiv. Der ganze Mechanismus ist damit Fork-Zweitbuchhaltung
ohne Upstream-Aequivalent, und die Beweislast liegt beim BEHALTEN.
Aufwand ehrlich: L, mit einem XL-Risiko (Transport-Phase des sgl-project#791-Lap).
Empfehlung: NICHT mit dem Voll-Schnitt beginnen, sondern mit der
Slot-Klassifikation (Divergenz-Patch vs echtes physisches MIN) -- Desk-
Arbeit, kein Boot, und sie bestimmt die Groesse alles Weiteren.

BELEG-STUFE: BOOT-BEWIESEN ueber zwei volle Sequenzen, je eine Strandung, je
drei Live-Stacks, je ein anderer Straggler-Aufruf. Serving wieder oben.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…e steht, aber der Umbau loest den Stall NICHT -- zwei Korrekturen an meiner eigenen Skizze

Desk-Arbeit, kein Boot. Jeder Slot am Pin relokalisiert.
Volltext: /spinning/gpu-arb/DESIGN_968_budget_verdict_to_pp0.md

== DIE KLASSIFIKATION IST DREI-, NICHT ZWEIWERTIG ==
Die Order fragt "Divergenz-Patch vs echtes physisches MIN". Die acht Slots
zerfallen tatsaechlich in drei Klassen, und die dritte ist der Grund, warum
der Umbau kleiner ausfaellt als gedacht und den Stall nicht behebt:
(a) DIVERGENZ-PATCH -- ein VERDIKT, das jeder Rang rechnet und ueber das man
    sich einigt: head_match (sgl-project#823 W9), admit_limit (:6167), Prefetch-Ballot
    (#791b). DIESE DREI SIND DIE LOESCHLISTE.
(b) PHYSISCHE EINGABE -- eine per-Rang-Tatsache, die PP0 NICHT SELBST WISSEN
    KANN: local_avail (#616g), admission (sgl-project#610), host (sgl-project#639), mamba (#639b),
    corridor (sgl-project#794). Muessen weiter reisen, aber als EINGABE in PP0s Verdikt.
(c) DIVERGENZ-DETEKTOR -- die `x, -x`-Paare, die aus einem MIN zugleich Max
    liefern, also Uneinigkeit ERKENNEN. Nach dem Rang-Gesetz ist die einzig
    legale Reaktion CRASH/STOP; heute speisen sie Kompensation.

== KORREKTUR 1 AN MEINER SKIZZE: die Kadenz-Deckel fallen NICHT ==
§4 Punkt 5 meiner eigenen Skizze behauptete, die sgl-project#1027/sgl-project#1028-Deckel wuerden
mit dem Umbau gegenstandslos. FALSCH. Die drei teuren Calls (memory_snapshot,
memory_stats, mem_get_info) haengen unter Slot 5, und Slot 5 ist eine
PHYSISCHE EINGABE -- PP0 kann PP1s freies VRAM nicht selbst messen. Das
Verdikt zu PP0 zu verschieben nimmt den Followern die ENTSCHEIDUNG ab, nicht
die MESSUNG. Der Stall liegt damit auf einer ANDEREN ACHSE als die
Verdikt-Platzierung: die physische Messung darf nicht auf dem kritischen Pfad
zwischen Rundenbeginn und Barriere liegen (Off-Thread-Sampling; der
`corridor-trace`-Thread existiert bereits). Beides kombinierbar, aber keines
folgt aus dem anderen.

== KORREKTUR 2 / TRANSPORT-VERDIKT: der Ring-Lap traegt es NICHT ==
pp_admission_congruence.py:235 woertlich: eine Entscheidung braucht BIS ZU
`pp_size - 1` Runden (hier 2) ueber den Lap. Und die sgl-project#1027-Sicherheitsanalyse
UEBERTRAEGT SICH NICHT -- die Asymmetrie ist der Kern:
  * `trapped` (sgl-project#1027) wird ABGEZOGEN -> ein zu alter, zu grosser Wert VERENGT
    einen Cut. Sichere Richtung.
  * `corridor_width` (Slot 5) ist eine OBERGRENZE -> ein zu alter, zu grosser
    Wert WEITET den gewaehrten Chunk. UNSICHERE Richtung, und exakt der Fall,
    vor dem der sgl-project#856-F6-Kommentar an spendable_bytes:606-619 warnt.
Ein Ein-bis-Zwei-Lap-altes Budget ist also fuer mindestens einen Slot in der
unsicheren Richtung. Der Lap traegt das Verdikt in seiner heutigen Phase
nicht. XL-Fork, geht zur Meldung statt in einen stillen Umbau.
Alternativen benannt: (1) frueherer Lap-Punkt -- aendert die Rundenphasen,
Risiko bei den Lockstep-Familien; (2) Piggyback auf bestehendem Kollektiv --
kein neuer Draht, aber PP0s Verdikt raeste auf demselben Reduce, den der
Umbau abschaffen soll. Dritte Moeglichkeit (konservative Marge gegen die
unsichere Richtung) NICHT empfohlen, aber benannt, damit sie nicht spaeter
als neu auftaucht -- sie waere eine vierte Kompensationsschicht.

== AUFWAND, PRAEZISIERT ==
  Loeschliste Slots 6-8 zu PP0            M   (Verdikte, kein Transportproblem)
  Slots 1-5 als Eingaben behalten         S   (Payload schrumpft, Semantik bleibt)
  Stall-Behebung (Messung vom Pfad)       M-L (eigene Achse)
  Slot 5 unter PP0-Autoritaet             XL-Risiko, UNGELOEST
EMPFEHLUNG: Loeschliste und Mess-Achse sind unabhaengig und beide OHNE den
XL-Fork machbar. Der volle PP0-Umbau von Slot 5 wartet auf die
Transport-Entscheidung.

BELEG-STUFE: DESK-BEWIESEN (Slots am Pin relokalisiert, Archaeologie aus den
Einfuege-Kommentaren, Lap-Latenz aus pp_admission_congruence.py:235).
Kein Boot, kein Code geaendert.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…en stale-UNSICHEREN Term eingefuehrt. Zurueckgenommen.

Schritt A (reiner Lese-Pass, beide Quellen je Zeile) -- und die Grundlage,
die kippt, ist mein eigener sgl-project#1028-Fix, der im Baum UND im laufenden Serving
lag. Gemeldet und korrigiert im selben Zug.

== DIE STALENESS-RICHTUNGSTABELLE (Ergebnis von Schritt A) ==
  Treiber-Call                Konsument              Operation   stale-groesser
  memory_reserved/allocated   want (:447)            SUBTRAHIERT under-arm  SAFE
  memory_reserved/allocated   spendable_bytes (:620) ADDIERT     weitet     UNSAFE
  mem_get_info (free_bytes)   spendable_bytes (:602) ADDIERT     weitet     UNSAFE
  memory_snapshot (trapped)   takeable (innen)       SUBTRAHIERT verengt    SAFE
Die Asymmetrie steht im Code und ich hatte sie nicht gelesen:
`_takeable_cache_bytes`-Docstring :720-728 sagt woertlich "the two callers
want OPPOSITE errors: sizing `want` is safe when the cache is overstated, and
sizing a spendable BUDGET is not."

== WAS ICH FALSCH GEMACHT HABE ==
sgl-project#1028 cachete den GANZEN `_takeable_cache_bytes`-Wert 30 s lang und begruendete
das so: "der Wert wird vom spendable Budget ABGEZOGEN (:620), ein zu grosser
Altwert verengt einen Cut, er weitet ihn nie."
FALSCH. Bei :620 steht `free + takeable - delta` -- takeable wird ADDIERT, und
`takeable_cache_bytes`' eigener Docstring sagt es auch ("a negative budget that
would then be ADDED to a free column"). Meine Aussage stimmte fuer `trapped`
(innerhalb takeable subtrahiert, sgl-project#1027) und wurde falsch, sobald ich die SUMME
cachete: ein 30 s alter, zu grosser `cache` WEITET die Zuteilung -- der Fall,
den der sgl-project#856-F6-Kommentar :606-619 unsurvivable nennt. Ich habe das Vorzeichen
meines eigenen Konsumenten aus dem Gedaechtnis zitiert statt es zu lesen; genau
die Methoden-Wurzel, die gestern als Regel ins Register kam.

== KORREKTUR ==
Zurueck auf sgl-project#1027-Umfang: nur `trapped` bleibt cadenced (beweisbar sichere
Richtung). `_allocator_cache_bytes` kehrt auf den Pro-Runde-Pfad zurueck, was
den #1028a-Straggler REINSTALLIERT. Das ist ein bewusster Tausch und der vom
Gesetz vorgeschriebene: ein intermittenter Liveness-Stall wird ERKANNT (Deadman,
er korrumpiert nichts still), ein ueber-gewaehrter Korridor ist stilles
Ueber-Commit. Crash vor Korruption.
OPERATIVE FOLGE, ehrlich: die Stall-Anfaelligkeit unter RM-Schwerlast ist damit
wieder auf dem Stand vor sgl-project#1028. Das ist eine Verschlechterung der Liveness zum
Preis einer wiederhergestellten Korrektheits-Invariante.

== FOLGE FUER STUECK 3 (Mess-Achse) -- der Lese-Pass hat es vorab entschieden ==
Off-Thread-Sampling loest das NICHT: eine Off-Thread-Probe ist ebenfalls stale,
und fuer einen ADDIERTEN Term ist Staleness die unsichere Richtung. Fuer
`cache` und `free` bleiben nur drei Wege: (a) synchron billig genug machen,
(b) so umbauen, dass der Term nicht pro Runde gebraucht wird, oder (c) eine
GEMESSENE UNTERE SCHRANKE fuehren (Minimum ueber das Sampling-Fenster), die in
der SICHEREN Richtung stale ist -- das ist keine willkuerliche Marge, sondern
ein Messwert, und damit nicht die abgelehnte vierte Kompensationsschicht.
Schritt B ist deshalb NICHT wie beauftragt gebaut worden: der Lese-Pass hat
seine Praemisse ("off-thread nach corridor-trace-Muster") widerlegt.

BELEG-STUFE: DESK-BEWIESEN (Vorzeichen an beiden Konsumenten am Pin gelesen,
Docstrings zitiert). Korrektur BOOT-GEBOOTET: health 200, greedy kohaerent,
nur noch die sgl-project#1027-Kadenz aktiv (3 Instrument-Zeilen), Deadman armiert.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…us den Stacks -- aber der Stall bleibt, mit ANDERER Wurzel

Weg 3 gebaut nach GO, mit allen drei Schutzgelaendern. Abnahme: Stall FAIL,
Kosten PASS -- und der Fehlermodus hat sich geaendert, was der eigentliche
Ertrag ist.

== WAS GEBAUT WURDE ==
`_bounded_min(name, sample_fn)`: der teure Call laeuft hoechstens einmal je
_BOUND_REFRESH_S (1 s), zurueckgegeben wird das MINIMUM der Lesungen im
_BOUND_WINDOW_S (5 s). Kadenz beantwortet die KOSTEN, das Minimum die
RICHTUNG -- damit ist mein eigener #1028b-Einwand beantwortet (Kadenz allein
war unsicher, weil sie den LETZTEN Wert nahm).
Ehrlich im Docstring: min-ueber-Fenster ist KEINE beweistragende Schranke auf
den aktuellen Wert (er kann unter alle Samples fallen); es ist strikt
konservativer als der Wert, den der Aufrufer sonst benutzt haette, und der
Rest faellt in dieselbe Klasse, die der `delta`-Wasserstand (:597-599) ohnehin
abdeckt. Kein erfundener Konstant -- jeder Wert wurde am Geraet gelesen.

== SCHUTZGELAENDER ==
(a) Instrument druckt je Lesung Fenster, n, bound, instant, delta und sagt
    ausdruecklich, dass dort eine SCHRANKE steht. Feuert ~1/s unter Last.
(b) KOSTEN GEMESSEN, n=139 je Term:
      driver_free  mean 25,3 MiB  max 232,0 MiB
      alloc_cache  mean  6,9 MiB  max  76,0 MiB
    WELT UNBEWEGT: 618.390 -> 617.026 (-0,22%), PP1 byte-identisch.
    BAND: card2 laeuft ueber der Decke (13% der Samples im Band, mean 1619).
    NICHT dieser Aenderung zuzuschreiben: der mittlere Ueberschuss ist 390 MiB
    gegen 32 MiB mittleren Konservatismus -- eine Groessenordnung. Ohne
    Vor-Aenderungs-Baseline zertifiziere ich das Band in KEINE Richtung.
(c) Schranke NUR auf den addierten Konsumenten (:602, :620). Der subtrahierte
    (:447 `want`) und der Test-Hook-Pfad sind unberuehrt -- verifiziert.

== STALL-ABNAHME: FAIL, ABER DIE ALLOCATOR-SITES SIND WEG ==
Erneuter Stall bei DE-07. In KEINEM der drei Stacks steht noch
memory_snapshot, memory_stats oder mem_get_info. Die Schranke hat geleistet,
wofuer sie gebaut wurde. Was stattdessen steht:
  PP1  broadcast <- _broadcast_reqs_across_ranks (request_receiver.py:399)
  PP2  broadcast <- _broadcast_reqs_across_ranks (request_receiver.py:399)
  PP0  zykelt die Schleife -- Probe 1 in phase_flip_output_trace.observe,
       Probe 2 in os.getenv (event_loop_normal:2711). ER BEWEGT SICH.
PP0 ist die WURZEL dieses Broadcasts und betritt ihn nicht. Zwei Raenge warten
auf ein Kollektiv, das der Root nicht betritt.

== DAS IST EINE ANDERE WURZEL ALS sgl-project#1027/sgl-project#1028 ==
Die waren KOSTEN: ein Rang zu langsam fuer eine Barriere, die alle erreichen.
Dies ist DIVERGENZ: Raenge in verschiedenen Schleifen-Regionen. Das ist der
woertliche Fall des Rang-Gesetzes, und dessen Antwort waere CRASH/STOP --
heute ist es ein unbegrenztes Warten.
Naechster Pruefpunkt (NICHT belegt): der Phase-Flip-Loop-Dispatch
(run_phase_flip_event_loops -> dispatch_event_loop) kann PP0 einen anderen
Schleifenkoerper geben als den Followern.

== ZWEI EIGENE LESUNGEN INNERHALB DIESES LAUFS KORRIGIERT ==
1. Ein einzelner py-spy-Treffer in `phase_flip_output_trace.observe` sah aus
   wie "das Instrument ist der Straggler". `observe` ist `pass_no += 1;
   ring.append(...)` -- trivial billig. Die zweite Probe zeigte PP0 bewegt.
   Ein Sample einer billigen Funktion ist kein Stall-Beleg.
2. Eine einzelne NVML-Lesung (card2 1535 MiB, 306 ueber der Decke) sah aus wie
   ein Band-Riss durch meinen Konservatismus. Die 100-Sample-Verteilung zeigte
   ~4 GB Schwankung ueber den Flip-Zyklus und die Groessenordnungs-Differenz
   oben. Beide Male haette ein Einzelsample einen falschen Befund erzeugt.

BELEG-STUFE: Bau DESK+BOOT (Instrument feuert, Guardrails verifiziert).
Kosten BOOT-GEMESSEN. Stall-Abnahme FAIL, neue Wurzel BOOT-BEOBACHTET
(PP1/PP2 je 1 Sample, PP0 2 Samples), Mechanismus NICHT bestimmt.
Serving wieder oben, health 200, kohaerent.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…ypothese WIDERLEGT, Detektor existiert mit DECKUNGSLUECKE -- der Fix ist S

Reiner Lese-Pass, kein Boot, kein Code. Drei Fragen in der vorgegebenen
Ordnung, beide Quellen je Zeile.

== (1) SCHLEIFENKOERPER: HYPOTHESE WIDERLEGT ==
`dispatch_event_loop` schickt auf einer Flip-Form JEDEN Rang in
`run_phase_flip_event_loops` (:14251-14253, unbedingtes return). Dort wird
zwischen `event_loop_normal` (TP-Phase) und `event_loop_pp` (PP-Phase) nach
aktivem Stack gewaehlt -- ein echtes Divergenz-RISIKO, aber NICHT das, was im
Specimen passierte: alle drei Raenge standen in `event_loop_normal`
(PP0 :2681/:2711, PP1+PP2 in recv_requests aus :2668). GLEICHER
Schleifenkoerper.
Die richtige Beschreibung des Zustands ist damit nicht Divergenz, sondern:
PP0 ist eine Iteration VORAUS und arbeitet seinen Durchlauf noch ab, waehrend
die Follower schon im Broadcast der naechsten warten. Also wieder die
STRAGGLER-Form -- nur an einer anderen Barriere und mit PP0 als Langsamem.
SCHRANKE, ausdruecklich: ich habe von PP0 ZWEI Proben, beide in billigem Code
an verschiedenen Stellen. Das schliesst "PP0 haengt in einem Aufruf, den ich
nicht getroffen habe" nicht aus. Um "die ganze Iteration ist langsam" von
"eine ungesehene Stelle haengt" zu trennen, braucht es ein PROFIL
(py-spy record ueber Sekunden), keine Dumps. Nicht behauptet, benannt.

== (2) DETEKTOR: ER EXISTIERT, UND DIE LUECKE IST PRAEZISE ==
`on_blocked_recv` (sgl-project#824 W5(b), request_receiver.py:89-95) ist genau der
Detektor fuer diese Klasse. Sein eigener Kommentar nennt den Fehlermodus
woertlich: "Without it that call is a blocking PP receive that records
nothing, SO THE WATCHDOG CANNOT NAME IT -- the same blind spot sgl-project#821 left on
the chain_receiver path, which is where two of three ranks wedged on
boot_827."
Er wird um den DIREKTEN KETTEN-EMPFANG gestampft (`point_to_point_pyobj`,
:323-326). Er wird NICHT um den TP-BROADCAST gestampft
(`broadcast_pyobj`, :380-404) -- und genau dort standen PP1 und PP2.
Das ist keine fehlende Idee und kein Detektor ohne Aktionsarm, sondern eine
DECKUNGSLUECKE eines vorhandenen Fixes. Und sie erklaert, warum dieser Wedge
nur per py-spy zu finden war: der Watchdog konnte die Stelle nicht benennen,
exakt wie der Kommentar es vorhersagt.
Im ganzen File: kein timeout, keine deadline auf dem Broadcast-Pfad. Das
Warten ist heute unbegrenzt.

== (3) PRIOR-ART-GATE: TREFFER, ALSO LUECKE STATT NEUBAU ==
Dieselbe Stelle hat Wedge-Historie: `kill_window_lines.txt` (2026-08-01)
zeigt einen Traceback ueber `recv_requests -> _broadcast_reqs_across_ranks`,
und BEFUND-pp3solo-ring-wedge-1013.md haelt die Geschwister-Form fest
(geschlossener Ring, alle in SEND). Kein Neubau abgeleitet.

== VORGESCHLAGENER SCHNITT, ZWEIGETEILT ==
(a) DECKUNG, S: `on_blocked_recv` um den Broadcast stampfen, wie es um den
    point-to-point-Empfang schon geschieht. Wirkung: der Watchdog kann die
    Stelle benennen; der naechste Wedge dieser Art braucht kein py-spy.
    Reine Deckungserweiterung eines bestehenden Mechanismus, kein neuer.
(b) AKTIONSARM, getrennt: heute ist `on_blocked_recv` ein AUFZEICHNENDER
    Haken, kein Aktuator. Der Rang-Gesetz-Teil (begrenztes Warten ->
    CRASH/STOP mit benannter Diagnose beider Seiten) braucht zusaetzlich die
    "was tut PP0 gerade"-Haelfte, und die ist ohne das Profil aus (1) nicht
    sauber formulierbar.
EMPFEHLUNG: (a) jetzt, (b) nach dem Profil. (a) ist S und macht (b)
ueberhaupt erst diagnostizierbar.

BELEG-STUFE: DESK-BEWIESEN (beide Quellen je Zeile am Pin gelesen,
Kommentar-Zitate woertlich). Die Stall-Beobachtung aus dem Vorlauf bleibt
BOOT-BEOBACHTET mit der oben benannten Proben-Schranke.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
…, and stop the two instruments that lied about it

NOT AN sgl-project#1028 FIX. This was written as one and the boot refuted the
attribution; it is committed as the standalone correction it actually is.
The sgl-project#1028 recompute has a different root (see the retraction below).

THREE CORRECTIONS, all at the flip's writeback fence:

1. THE BOUND IS ON STALLING, NOT ON DURATION.
   `deadline_s` was a flat wall-clock cut, which cuts a fence making steady
   progress at the same moment as a wedged one. Measured, boot_855_wt1016
   19:22:42 and 19:22:45 -- the same four nodes fenced twice, three seconds
   apart: `acked=0 outstanding=4` then `acked=1 outstanding=3`, both
   `elapsed=2.000s/2.000s`. Acks were landing at ~1 per 3 s; the backups
   were slow, not stuck, and the flat bound discarded that progress.
   `deadline_s` is now the NO-PROGRESS bound (unchanged default 2.0 s, so a
   genuinely stuck backend behaves exactly as before) and a derived hard
   ceiling (12x) stops a slow backend holding the seam open without end.
   The stall clock resets only when the in-flight set SHRINKS, which is
   monotone by construction, so noise cannot reset it.

2. AN EXPIRED FENCE MAY NOT CONVERT INTO A FALSE PROMISE.
   The cutover released residents and logged "Their KV is in the canonical
   store from the fence" unconditionally -- measured on wt1016 one second
   after the fence reported `acked=1 outstanding=3`. The claim now reads the
   report it speaks for, and distinguishes measured-zero from unmeasured.
   An incomplete fence additionally joins the EXISTING unanimous abandon
   (`too_small`), at the point where nothing has been mutated yet, rather
   than becoming a check of its own: same argument the row-bounds and
   staging terms make in that function, and a rank-local abandon would
   half-flip the group. Bounded by _WRITEBACK_DEFER_LIMIT=3 so a
   permanently stuck backend cannot trade a recompute for a wedge -- the
   worse of the two -- and the acceptance is then logged out loud instead
   of happening silently.

3. DWELL-RELEASE SAID "the flip cohort is resident" ON TWO DIFFERENT PATHS.
   The cohort count reaches zero either because the cohort became resident
   or because a re-admission left it with a SPENT one-chunk TP grant while
   still mid-recompute (scheduler.py, `seam_grant_is_open` exclusion). Only
   the first is residency. Measured wt1016 19:23:33: "the flip cohort is
   resident" logged in the same second, same rank, as a 4096-token TP batch
   with `recomputing=True`. `_seam_cohort_pending` now also returns the
   count it was already skipping; no decision changes, the log stops
   asserting the flattering one of two causes.

RETRACTION, recorded because the wrong version was reported first: I
attributed the sgl-project#1028 large-prompt recompute to this fence expiring. My own
boot refutes it. boot_855_1028fence ran 27/27 fences COMPLETE
(outstanding=0), including `eligible=4 acked=4 outstanding=0`, and the
re-admission still reported `#969B READMIT-MATCH prefix_len=0 host_hit=0
storage_hit=0 input_len=13180` and recomputed all 13180 tokens. A fully
acked fence yields host_hit=0 too, so the timeout is not the cause. That
inference was a correlation in a single boot read as causation. The real
candidate is `prefetch_registered=False prefetch_keys=0`, present in every
READMIT-MATCH of both boots -- the never-shipped sgl-project#856 read-through half.
sgl-project#1028 stays OPEN; nothing here is claimed to fix it.

EVIDENCE / BELEG-STUFE: BOOT-PROVEN AS RUNNING AND BEHAVIOURALLY INERT,
not "fixed" at any symptom. boot_855_1028fence: all fence lines now carry
`ceiling=24.000s` (execution proof the code is live), all 27 completed with
`elapsed=0.000s` exactly as before, and all four new markers counted 0 --
the trigger condition (outstanding>0) never occurred, so the defer branch
cannot have influenced that boot. Desk check matched to the error class of
these edits (undefined name / tuple arity, which py_compile is blind to):
ruff F821/F811/F841/E9 clean on the changed regions -- the only 2 hits are
pre-existing duplicate imports at scheduler.py:148/171, far from the edits
at 3812+/13321+ -- plus an AST scan confirming `_seam_cohort_pending`
returns 3-tuples on every path and its single call site unpacks 3.

Sibling census for the warn-then-continue class in phase_flip_runtime.py:
2 other sites (1529, 7933); both warn honestly and make no promise, so
neither is touched. This site was a false negative of that deliberately
narrow rule, which is stated rather than dressed up as a rule hit.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
ROOT, corrected against the handover's framing. The handover read the store
census as "15457 KV pages vs 11 mamba anchors = grid behaviour" and ordered
the anchors published at every write-through. Measured on the same boot
(boot_855_1028fence), that premise is a PAGE-vs-NODE denominator mix and the
ordered fix is already in place:

  * `#969H BACKUP` = 33 lines = n=1..11 on EACH of PP0/PP1/PP2 at identical
    timestamps. The probe logs every call up to n<=40, so 11 lines means
    `write_backup` reached its component loop exactly 11 times per rank in the
    whole run -- and all 11 carried `mamba_value=has_value`.
    => mamba coverage OF THE HOST-BACKUP PATH is 11/11 = 100%, not 11/15457.
  * All 11 `.mamba` hashes in the store are also full-KV page hashes
    (intersection 11, mamba-only 0): one anchor per node at a real page
    boundary, shared key namespace.
  * The 15457 KV pages are the PAGES of those same 11 nodes:
    `write_backup_storage` writes `keys=node.hash_value` (every page of the
    node) while the mamba branch writes `keys=[node.hash_value[-1]]` (one
    trailing page).

The real hole is one link earlier and is not mamba-specific: `_inc_hit_count`
returns before any backup when `chunked=True` (upstream's "skip the hit count
update for chunked requests"). Under chunked prefill NOTHING is published per
chunk -- so the 11 backups are the 11 FINISHED requests, and a chunked prefill
that never finishes publishes nothing at all.

That is why the 13179-token prompt found its deepest anchor at 3072 and
recomputed the remaining 10107 tokens: the anchors are as dense as finished
requests, not as dense as chunks.

FIX: allow a chunked-prefill node to reach the host tier, gated structurally
on (storage tier present AND a MAMBA component present). The per-chunk node
already carries a donated state on the device
(`MambaComponent.prepare_for_caching_req`, is_finished=False branch); this
early return was the only reason it never reached host or storage.

UPSTREAM-MINIMAL: the chunked skip IS upstream (`hiradix_cache.py`), so this
is a DEVIATION and carries its burden of proof. Named: (1) drain-and-flip --
a chunked prefill interrupted by a flip never reaches `cache_finished_req`
(sgl-project#856 removed the carry, the flip DISCARDS it), so upstream's publish-at-finish
never fires; (2) GDN hybrid -- a recurrent state is valid at exactly one token
position, so the per-chunk anchor has no pure-attention analogue. Gate off =
byte-identical to upstream (verified: branch truth table, 0 mismatches over
all 8 chunked/write_back/force combinations).

LAWS: `mamba-per-knoten-nicht-gitter` in its own words ("states per radix
node/chunk like KV pages"), and it waives write volume explicitly.
`kein-doppel-prefill` (sgl-project#939): loss bound becomes ONE chunk.

sgl-project#968 PP0 DEBT -- WHERE THIS PATH STAYS RANK-LOCAL: the publish decision is
taken at the scheduler's chunk boundary, which every rank runs for the same
request at the same split, so it is unanimous BY CONSTRUCTION, not by
agreement; this path holds no reduce (the `check_prefetch_progress` MIN is
TP-scoped and the boot runs tp_size=1/pp_size=3, so it is structurally
skipped). `raenge-nie-uneins` is met by construction and `#1028P
CHUNK-PUBLISH` is how the claim gets CHECKED: identical n at identical
timestamps across ranks, the evidence shape `#969H` gave for the 11.
The one rank-local input reachable from here is the sgl-project#581/sgl-project#773 write-through
pin budget, which fired ZERO times in that boot (trap-safe: bare 0, genuine 0)
because 11 backups never approached it. Per-chunk publishing makes it
reachable for the first time; `pin_skipped` rides on the same line so a
nonzero count next to a divergent n is the divergence, named in advance.

COST, QUANTIFIED NOT BUILT: one `.mamba` blob is 78446592 B = 74.8 MiB
(measured, all 11 identical); a KV page is 32768 B. A published 4096-token
chunk therefore adds 74.8 MiB of anchor on top of 128 MiB of KV (+58% L3).
Host RAM is UNCHANGED -- the host mamba pool is pre-sized at boot from
`hicache_ratio` (1.5) x device slots, so anchors roll through a fixed tier and
land on disk. The int8-anchor idea (sgl-project#1013) would cut the 74.8 MiB and is
deliberately NOT built here.

ALSO: #1028B FETCH CAP instrument at the `min` in `hicache_storage.py`, the
cap that decides how much of an existing KV prefix a prefetch may claim. It
printed nothing: `final_pages`, `kv_pages`, `boundary=`, `hit_pages` each
occur 0 times in the whole 5.87 MB log, so "anchors too sparse" and "KV prefix
too short" produced the same number and were NOT separable from that boot.
Now both terms print on one line.

Desk checks (error-class matched): ruff F821/F811/F841/E9 clean on both
changed files (new names + new attribute reads); branch truth table executed,
gate-off equivalence exact. The gate is deliberately NOT memoised --
`enable_storage` is False at __init__ and only set in `init_hicache`, and a
memoised early False would leave a wired-but-inert write path, the sgl-project#742/sgl-project#745
class this area has produced before.

NOT YET BOOT-PROVEN. Acceptance is the next boot.
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 30, 2026
… the realised loss

Two instrument repairs, no behaviour change.

1. `#1028P` counted per INSTANCE (`self._chunk_publish_n`) and was therefore
   useless for the one job it was built for. A rank runs several
   UnifiedRadixCache instances (PP stack, TP stack, flip stacks), so the
   counter reset per instance and the log filled with `n=1`. Boot 1 printed
   4/4/4 across ranks and that LOOKED like the rank-unanimity proof; it was a
   line-count coincidence, and boot 2 printed 7/6/6 from the same healthy
   state. Now a class attribute, i.e. per process = per rank, the population
   `raenge-nie-uneins` actually asks about. Same shape as the `#969H` probe,
   which is why that one could carry its claim and this one could not.
   THE EARLIER UNANIMITY CLAIM IS WITHDRAWN: unanimity for the publish path
   is argued constructively (the chunk boundary is a group-synchronous
   scheduler event), NOT measured. This counter is what would measure it.

2. `#1028B` now prints `claimed` and `lost` (= kv - claimed) instead of
   leaving the subtraction to the reader. `lost` is the quantity sgl-project#939 bounds
   at ONE chunk, so it belongs in the log as a number.
   Also documented at the site: an empty `caps` dict does NOT mean "nothing
   capped this claim". `hit_count[name]` is assigned only `if boundary`, so a
   component whose boundary came out 0 -- the total-miss case, exactly the one
   that zeroes the claim -- leaves no entry. Empty caps beside final=0 means
   "a component found no anchor in this span at all", the opposite reading.

PARTIAL-CLAIM IS ALREADY THE SEMANTICS -- recorded so it is not built twice.
A faithful replay of the trailing_pages arm (`hicache_storage.py:1710-1722`)
shows `boundary` is already the DEEPEST prefix whose last page carries an
anchor, and `final = min(kv, boundary) = boundary`:
    no anchor in span   -> boundary=0   final=0
    anchor at page 199  -> boundary=200 final=200
    anchor at page 520  -> boundary=521 final=521
So `final=0` is NOT an all-or-nothing discard of an otherwise claimable
prefix; it means no anchor exists anywhere in that span. A "claim up to the
last covered point" change would be a no-op.

Desk checks (error-class matched): ruff F821/F811/F841/E9 clean on both files;
AST scan of every logging call in both files for format/arg arity = 0
mismatches (the class py_compile is blind to, and the class these two edits
could actually produce).
efschu pushed a commit to efschu/htsglang that referenced this pull request Aug 31, 2026
…ilt 2026-08-24, never shipped

CHERRY-PICK OF 14606c6, unchanged apart from one trivial conflict.

WHY IT IS HERE NOW. The sgl-project#1050 forensics found this commit while running the
prior-art gate on its own fix route, and found it in the state the fork keeps
producing: BUILT, hermetically green (16 tests + 4 subtests, 5 mutants killed),
flag-gated so it is byte-identical when off -- and NOT AN ANCESTOR of the
shipping pin. `git merge-base --is-ancestor 14606c6 38c6f28` says no; it
lived only on fix/811-admission-pin-ack-release and fix/849-census-resident-line.
It has stood in WINDOW-QUEUE.md as TICKET W-811 since 2026-08-30 waiting for a
metal proof it never got, because as a pure capacity optimisation it had no
symptom to be measured against.

It has one now. Boot 17 and boot 18 died with `leaked_mamba_pages` of 4 slots
out of 20 beside the full-pool loss -- {13, 3, 5, 14} and {19, 18, 11, 12} --
i.e. exactly the population whose release this commit moves from "finish" to
"write-through ack". So this boot IS the W-811 metal probe, and
`leaked_mamba_pages = 0` is its acceptance criterion.

CONFLICT: one hunk, both sides adding an attribute in `__init__`
(`_chunk_publish_n` from sgl-project#1028 here, `_anchor_ack_release_armed_cached` from
sgl-project#811). Both kept; no semantic overlap.

CHECK, matched to a merge rather than to a new edit: the commit's OWN existing
suite re-run on the merge result -- 16 passed, 4 subtests passed. Not a new
battery; the point of a merge check is that the shipped proof still holds after
seven days of line movement.

NOTE ON HOW IT WAS RUN, because the first run was a FALSE RED: without
PYTHONPATH=<worktree>/python, pytest imported sglang from the serving tree
(/spinning/htsglang-gpu) instead of this worktree and died in collection. A
worktree test that does not set PYTHONPATH is measuring another tree.

The flag stays as designed (`--mamba-anchor-ack-release`, off by default,
planner-emitted, no environment form). Arming it is the launcher's business,
declared per boot.
efschu pushed a commit to efschu/htsglang that referenced this pull request Sep 4, 2026
…(matched+loaded), not loaded alone

BOOT weg1b6 @ 06a605f, log boot_855_weg1b6_06a605fc20_0903_160236.log:134985-134996.
rid 1e95e023, stamp 6008, allowance 4096, three ranks re-admitting one prefix
after a cutover:

  PP1/PP2  completed_local=6008  matched=5966  loaded=42
  PP0      REAPED at 7.87 s (policy=timeout, queue-lagged behind its siblings
           in the cutover burst -- the mirror of sgl-project#1175):
           completed_local=3456  matched=3456  loaded=0
  sibling ce45cd48 seconds earlier: matched=40 loaded=5971  (path B WORKS)

matched + loaded == completed_local on every line. PP0 raised
StoreWitnessContradiction (stamped=6008 probed_hit=6008 loaded=0 shortfall=0)
and STOPped the group, although 3456 tokens WERE present: shortfall 2552 is
inside the one-chunk allowance = a sanctioned sgl-project#939 bounded re-prefill.

ROOT: phase_purity.py:1388-1425 `_witness_from_outcome` read `loaded` alone and
ignored `matched` (the already-device-resident prefix,
unified_radix_cache.py:4140-4145 `insert_result.prefix_len`). CLASS: the
instrument measured the TRANSFER instead of the PRESENCE -- the same class the
sgl-project#841 comment names for `loaded` at unified_radix_cache.py:4136-4139.

FIX
- hicache_storage.py PrefetchOutcome gains positional-with-default `matched`
  plus the read-only `materialized` = matched + loaded; __reduce__/__repr__
  carry it (review-B1 lesson kept: positional ctor, survives pickle.loads).
  int(outcome) stays the loaded count, so every existing reader is unchanged.
- unified_radix_cache.py:4148 passes matched=insert_result.prefix_len (the
  complement of loaded against completed_synced, host_span_unclaimed branch
  included). The revoke drain near :4617 passes matched=0 with the reason
  named: a revoke ran no insert, so no measured resident prefix exists there.
- phase_purity.py judges presence = outcome.materialized; shortfall =
  stamp - presence; "hit" iff presence > 0 and shortfall <= allowance. The
  raise now prints matched= and materialized= beside loaded= and says
  "shows nothing materialized" vs "fell short ... by more than one chunk".
  A bare-int record (hiradix_cache.py:1937, hi_mamba_radix_cache.py:2364)
  keeps its old reading unchanged. probed/unprobed/cold untouched.

EVIDENCE
- red-first, final test file on parent 06a605f (scratch worktree,
  new file only): 12 failed / 1 passed. Green after the fix:
  test_1176_witness_materialized.py + test_1157_seam_witness.py 36 passed.
- mutants: m1 `materialized` ignores matched -> 7 failed (case (a) red);
  m2 allowance comparison removed -> 5 failed (case (c) red, plus two sgl-project#1157
  shortfall pins). Both reverted.
- tool check: import smoke of the three modules OK; ruff parity per file
  0/0/0 against the parent, new test file clean.

RANK UNIFORMITY (reported, NOT built)
Boot 6 raised on PP0 only (1 occurrence, PP0-tagged): PP1/PP2 held loaded=42
and read "hit", PP0 read "contradiction" -- the ranks DID diverge on this
verdict. Under --tp-size 1 --pp-size 3 the N1 packed MIN all_reduce is not
taken (`if self.tp_world_size > 1`, unified_radix_cache.py:3879-3907;
attn_reduce_world=1 synced=no on every sgl-project#1028 line of this boot), so the record
is rank-LOCAL and every rank evaluates seam_transport_premise_holds
independently (phase_purity.py:1009 <- scheduler.py:8925, which runs on all PP
ranks). The sgl-project#1064/sgl-project#1175 carrier runs the WRONG WAY for this: it is
follower->PP0 by construction ("PP0 is the only consumer",
scheduler_pp_mixin.py:2174-2191). There is no PP0->follower channel for the
seam premise, so no one-line "skip verdict, take PP0's told prefix" exists;
building one would be a second mechanism. Filed as a raenge-nie-uneins hazard
for the sgl-project#968 PP0-authority rebuild instead.
efschu pushed a commit to efschu/htsglang that referenced this pull request Sep 4, 2026
…was asked for, and only the rank that owns the verdict may STOP the group

Three blocking findings from the adversarial review of 4b277ff, each
re-verified at file:line on that tree before the edit.

(b) PRESENCE UNDER-COUNTS BY THE REGISTRATION-TIME MATCH -- the same class
    as sgl-project#1176 itself, not closed by it. `matched` is
    `insert_result.prefix_len` (unified_radix_cache.py:4140-4148): the prefix
    of the FETCHED SPAN the tree already held, NOT this request's whole
    device-resident prefix. The span excludes what was matched when the
    prefetch was registered -- scheduler.py:5322
    `_matched_len = len(req.prefix_indices) + req.host_hit_length`, :5350
    `_new_input_tokens = full_untruncated_fill_ids[_matched_len:_match_end]`,
    handed to unified_radix_cache.py:3271 -- while the stamp is the WHOLE
    prompt prefix (schedule_batch.py:2593). So
    `matched + loaded <= stamp - _matched_len` BY CONSTRUCTION.
    Demonstrated on 4b277ff: stamp=20000, matched 10000 at readmit, a
    9999-token span completed IN FULL -> raised with shortfall=10016 on a
    prefix that was 19984/20000 present. Boot weg1b6 hid it because the tree
    was reset at the cutover (_matched_len 0, span == stamp); it becomes MORE
    reachable exactly as the store starts serving, because `host_hit_length`
    feeds `_matched_len`.
    FIX: `demand = min(stamp, span)` where the span is the rank-local stamp
    scheduler.py:5354 already writes (`req._prefetch_span_tokens`, one writer,
    written in the same call that registers this prefetch) -- no new plumbing.
    `min` keeps the stamped prefix as the ceiling for an output-bearing
    request whose `_match_end` runs past its prompt. A record whose request
    carries no span stamp keeps the stamp as the demand (unchanged reading).
    The raise now prints span= and demand=.

(f) TWO OF THE REVIEW'S OWN MUTANTS SURVIVED on the axis this work is about.
    MUT-B (`shortfall = demand - presence` -> `- hit`) survived because every
    case in both files had hit == presence: nothing separated "the probe
    ANSWERED" from "the prefix is MATERIALIZED". Pinned by class H --
    stamp=6008, hit_tokens=6008 (probe answered the whole span), matched=0,
    loaded=100: correct verdict RAISE (shortfall 5908 > 4096), mutant verdict
    "hit" = admission at P=100 against a 6008-token stamp, the exact
    kein-doppel-prefill violation the witness exists to prevent.
    MUT-C (`presence > 0` -> `>= 0`) survived because case (d) reaches the
    raise through the SHORTFALL arm, so the "nothing materialized" term was
    never load-bearing. Pinned by class I -- stamp=100 (inside the 4096
    allowance), presence 0: the raise is reachable ONLY through that term.

(d) RANK DIVERGENCE ON THE VERDICT, measured on boot weg1b6 and previously
    filed as a hazard instead of fixed. The packed MIN all_reduce over the
    prefetch group is taken only under `tp_world_size > 1`
    (unified_radix_cache.py:3879-3907) and that boot ran --tp-size 1
    --pp-size 3 (`synced=no attn_reduce_world=1` on every sgl-project#1028 line), so
    every rank evaluated its own record: PP0 raised, PP1/PP2 read "hit". Any
    completed-span spread wider than the allowance splits the verdict, and
    the dangerous half is a FOLLOWER dying while its peers admit and enter a
    collective one rank short. Both reachable sites are covered:
    seam_transport_premise_holds (phase_purity.py:1009 <- scheduler.py:8925,
    the site that raised in weg1b6) and both admission arms
    (scheduler.py:11298 / :11351 -- and the PP arm runs on EVERY rank of the
    group, not only PP0).
    FIX uses the authority that already exists rather than a second channel:
    `witness_stop_authority` = PP0, or any world that is not a pipeline group
    (there the record is the only one or MIN-reduced). sgl-project#968 names PP0 as the
    single verdict site and the #969Z note in that same loop already says
    followers "credit only and decide nothing". A follower now REPORTS --
    returns the state "contradiction", deliberately absent from the tuple
    `seam_transport_premise_holds` accepts, so it licenses nothing -- with a
    rate-limited WARNING naming the divergence. This is not compensating a
    recognised disagreement (raenge-nie-uneins): a follower cannot SEE PP0's
    presence, so there is no disagreement to recognise; it declines a verdict
    it was already declared verdict-free for. A stand-in without a pipeline
    identity is its own authority -- absence of `ps` must never silence the
    witness.

EVIDENCE
- red-first on the parent 4b277ff (the new
  cases only; test_1157 was untouched and green there):
  test_1176_witness_materialized.py 7 failed / 20 passed. The 7 are the four
  span cases (b) and the three follower cases (d); H and I are mutant-killers
  and pass on correct code by construction.
- after the fix, both witness files: 50 passed (test_1157 23 + test_1176 27).
- mutants, one per finding, all killed:
  M-b  span never narrows the demand              -> 3 failed
  M-f1 shortfall = demand - hit (MUT-B)           -> 3 failed
  M-f2 presence >= 0 (MUT-C)                      -> 1 failed
  M-d  every rank is the authority again          -> 2 failed
  Each reverted from a pristine copy; `git diff --stat` clean afterwards.
- two existing pins updated because this commit changes their subject, not
  their claim: test_1157's admission-site regex now demands the
  `is_authority=witness_stop_authority(self)` call form on BOTH arms (it
  still pins two arms, the tree, and the int(...) credit), and its message
  pin carries the new span=/demand= fields.
- tool check: import smoke of phase_purity / scheduler / hicache_storage /
  unified_radix_cache OK plus a direct authority-predicate probe
  (pp0=True, follower=False, no-ps=True); ruff clean on phase_purity and both
  test files, scheduler.py 103 = 103 against the parent (pre-existing).

NOT CHANGED, named
- The bare-int record path (hiradix_cache.py:1937,
  hi_mamba_radix_cache.py:2364) keeps its old reading; probed/unprobed/cold
  are untouched; the 40-token chat-template false hit still STOPs.
- `_prefetch_span_tokens` is rank-local and stamped at registration. It is
  paired with the record this witness reads (same call), which is why the
  narrowing is safe; a stale-LARGER span cannot widen the demand past the
  stamp, and a missing one falls back to the stamp.
efschu pushed a commit to efschu/htsglang that referenced this pull request Sep 8, 2026
Boot weg2sb4 (e4f1b9f) ran 1,499.8 HICACHE-ROUND/s on group P and
303.1/s on group D for 34 minutes with an empty queue and a live front.
Measured from that boot's own logs: P 677,214 lines / n=5,643,450 over
3,504 s, D 105,354 lines / n=877,950 over 3,628 s.

ROOT, and it is not missing machinery.  The sgl-project#547 blocking-poll ladder is
fully built and correct (idle_sleeper.py: rungs 0/1/10 ms, 50 ms cap).  It
was never CONSTRUCTED: scheduler.py:1243 init_idle_sleeper arms only under
--sleep-on-idle or SGLANG_IDLE_BLOCKING_POLL, and the weg-2 launcher sets
neither, so `idle_sleeper` was None on all six ranks of both groups and the
loop never blocked.  At the ladder's 50 ms cap an idle origin ticks at
~20 Hz instead of ~1,500 Hz -- a 75x reduction.

The RANK GATE is not the defect and is kept.  Only the request origin owns
the zmq sockets the ladder polls; ranks above it take a blocking chain
receive / broadcast (request_receiver.py: "ranks 1..n-1 from
point_to_point_pyobj") and are driven at the origin's cadence, which is why
all three PP stages logged byte-identical round counts.  Parking the origin
parks the whole group: one wait point, not six, and no rank-local decision
(RAENGE-NIE-UNEINS).  Arming is scoped to the weg-2 form via
SGLANG_WEG2_GROUP -- the launcher's own established discriminator -- so the
stock default is untouched.

Second, smaller allocator on the same path: the sgl-project#1028 ROUND CENSUS line was
written every 25th round, ~60 lines/s/rank of "nothing changed", each one a
format tuple + formatted string + LogRecord on the round path.  The counter
still counts every round; the LINE is now written on a state change, plus a
60 s heartbeat.

Tests hermetic (CUDA_VISIBLE_DEVICES=""), 14 passed.  Red-first proven: 3
of them fail against the unpatched tree (weg-2 arming, quiet rounds, state
change).  test_every_pass_still_does_its_work is the regression guard for
the fix-1c / fix-2 per-pass semantics -- a queued control object still gets
its lap within one pass; only the log line changed, never the work.
Ruff parity exact: scheduler.py 106 -> 106, unified_radix_cache.py 1 -> 1.

NOT BOOTED.  Rounds/s at idle is an arithmetic consequence of the ladder
cap, not a measurement; the next boot's WEG2-IDLE-CENSUS line is what
settles it.
efschu pushed a commit to efschu/htsglang that referenced this pull request Sep 8, 2026
…line

Boot weg2sb4 grew ~19.0 MiB/min of host anon across its six ranks while
completely idle, against an independently observed +21.3 MiB/min.  Measured
per rank over two /proc samples 140 s apart: P PP0 +5.4, PP1 +5.4, PP2 +0.0,
each D rank +2.8 MiB/min; everything else (front, detokenizers, launcher
parents, inductor workers) exactly 0.0.

WHERE IT IS.  64 MiB-aligned [anon] rw-p glibc malloc arenas, extended in
place plus new arenas appearing (PP1 7d51f4000000 63.7->64.0, 7d51e8000000
19.6->20.4, new 7d51ec000000 0->11.43 MiB).  It is NOT the host ring --
dShmem was 0.0 on every process -- NOT CUDA pinned memory (/dev/zero flat)
and NOT the store.

MALLOC_ARENA_MAX=4, chosen from this rig's measurement rather than folklore:
glibc defaults to 8 x ncores arenas and CPU affinity here is 0-31, so the
default ceiling is 256.  The allocating threads on the steady-state path are
the scheduler thread plus the three the reset path names by hand ("#N RESET
JOIN threads=['prefetch', 'backup', 'prefetch_io_aux']"), so 4 gives each one
its own arena without the default's fragmentation surface.  It is a CEILING,
published with setdefault so an operator experiment still wins.

malloc_trim runs at the idle point, BOUNDED: only when this rank actually
grew WEG2_TRIM_MIN_GROWTH_MIB since the last trim, because trim walks the
arena free lists and a flat rank must not pay for it.

WEG2-IDLE-CENSUS, per rank, once a minute while idle:
  rounds_per_s=<n> rss_anon_mib=<n> d_anon_mib_per_min=<n> threads=<n>
  arena_max=<s> trimmed=<n> group=<P|D> rank=<n>
from /proc/self/status only -- no smaps walk (these ranks carry ~3,300
mappings).  It exists because NEITHER fact was in the log: the round rate had
to be reconstructed by counting sgl-project#1028 lines with an external script, and the
growth was only visible because a desk agent read /proc minutes before a host
OOM killed the processes.  The next boot's acceptance can now gate on
rounds_per_s ~= 0 and d_anon_mib_per_min ~= 0 with no external tool.

`threads=` is in the line deliberately: the per-rank thread count was NOT
captured before the OOM, so the arena value is derived from the reset line's
thread names and the next boot confirms or corrects it.

EXPECTED EFFECT, stated as arithmetic and not as a measurement: the sgl-project#1276
ladder cap takes an idle origin from ~1,500 to ~20 rounds/s (75x), and the
growth is per-round allocator churn, so the arena bound plus the reduced
round rate should take +19.0 MiB/min toward ~0.  UNPROVEN until a boot.

WHAT REMAINS UNEXPLAINED: in the same 140 s window cgroup anon rose +815 MiB
while the tracked processes explain only +44.3.  That window was NOT idle --
a desk probe was reading the checkpoint inside it -- so the +815 is
confounded and must not be quoted as an sglang rate; the +19.0 MiB/min
per-process figure is the defensible one and it is the one that matches the
independent idle observation.  The full-system attribution of the remainder
was never taken: the scan was killed by the OOM before its second sample.

Tests hermetic, 11 passed, red-first proven (the launcher publication test
fails against the unpatched tree).  Ruff parity exact: launcher.py 0 -> 0,
scheduler.py 106 -> 106 (the census import is function-local, which also
keeps weg2 from reaching back into managers at module scope).

NOT BOOTED.  No GPU used; cards were free and nothing was booked.
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.

2 participants