Fix wrong assert - #1028
Merged
Merged
Fix wrong assert#1028
Conversation
merrymercy
approved these changes
Aug 11, 2024
hnyls2002
enabled auto-merge (squash)
August 11, 2024 09:20
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.entriesis wrong when the first token is eos after prefill.Modification
Remove the assert and delete the element if it is in.
Checklist
pre-commit run --all-filesor other linting tools are used to fix potential lint issues.