fix(harvest): #651 resolve reproducible Playwright deadlock (console-flood on event thread) - #651
Conversation
…ole-flood on event thread Root cause (grounded in code): GenerateHarvestImages subscribed a page.Console handler that logged EVERY browser console message as 5 verbatim Log lines, executed synchronously on Playwright's single event-dispatch thread. Each Log call does a File.AppendAllText (open/write/close under lock) plus AnsiConsole writes — and Spectre.Console's AnsiConsole is NOT thread-safe, so the harvest thread and the Playwright event thread accessed it concurrently. Under a full clean-slate harvest, CardPen floods the console during cardpen.write.generate. The per-message slow synchronous I/O + concurrent AnsiConsole access starved/stalled the Playwright transport, so subsequent Goto/Evaluate awaits never received their responses → indefinite 0-CPU freeze with NO timeout (the timeout mechanism itself needs the stalled dispatch loop). po-2023 reproduced this 3/3 at the Rules harvest (~2-3 min in). It surfaced now because this was the first full fresh harvest since the clobber (max console volume) post-#640/#645. Fix: the handler now captures ERROR-level console messages only, into a lock-free ConcurrentQueue (zero file I/O, zero AnsiConsole on the event thread). Captured errors are drained on the main thread in the finally block. Normal-path console spam — pure debug instrumentation ("--- CONSOLE MESSAGE RECEIVED ---") — is dropped. Error diagnostics for genuine CardPen failures are preserved. Surgical: 1 file, +17/-5. Build clean (0 errors). No behavioral change to harvesting other than removing the log flood and its deadlock. Validation: po-2023 to re-run the harvest with their reliable 3/3 repro. Relates #629 (Playwright stability), #645/#134 (bundle v3 tag critical path). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] — LGTM deep (deadlock Playwright, root cause byte-vérifié contre Logger, fix correct)
fix(harvest): #651 resolve reproducible Playwright deadlock (console-flood on event thread). HarvestManager.cs (+17/-5, 1 fichier). Deadlock reproductible (po-2023 3/3 au harvest Rules) : freeze 0-CPU indéfini, Goto/Evaluate jamais retournés.
Root cause byte-vérifié (pas juste trusté le commentaire) :
L'ancien handler Page_Console loggait CHAQUE message console en 5 lignes Log sur le thread event-dispatch de Playwright. Or HarvestManager.Log (L839) délègue à Logger.Log, et j'ai confirmé dans Logger.cs que :
- L83-86 :
lock (fileLock) { File.AppendAllText(LogFile, ...) }→ I/O fichier synchrone sous lock ✅ - L104-130 : multiples
AnsiConsole.MarkupLine/WriteLinepar message (MessageType.Problem = 4 calls AnsiConsole L115-117) → Spectre.Console non thread-safe, accès concurrent du thread harvest + thread event ✅
Sous flood console CardPen (cardpen.write.generate), chaque message déclenchait I/O sync + AnsiConsole concurrent sur le thread event → starvation du transport Playwright → Goto/Evaluate bloqués → freeze indéfini sans timeout. Le commentaire de root cause est accurate.
Fix correct (pattern textbook producer/consumer) :
- Filtre error-only (
msg.Type == "error") → élimine le flood verbose (le bruit debug n'est plus capturé). Réduit drastiquement la charge. ✅ - Queue lock-free sur le hot path :
ConcurrentQueue<string>.Enqueue— non-bloquant, 0 I/O, 0 AnsiConsole sur le thread event → travail quasi-nul. ✅ - Drain sur le main thread dans le
finally(L475+) :while (consoleErrors.TryDequeue(out var err)) { Logger.Log(err, MessageType.Problem); }— l'I/O + AnsiConsole redeviennent safe (main thread, pas de contention avec l'event thread). ✅ - Unsubscribe préservé (
page.Console -= Page_Consoleavant le drain → stoppe les nouveaux messages, puis drain le backlog). ✅ - MessageType.Problem réel (enum L9/L13 dans Logger.cs, déjà utilisé L105/L115). ✅
Diagnostic préservé : les erreurs browser ne sont pas perdues — elles sont capturées et drainées vers Logger.Log(..., MessageType.Problem) (bold red) sur thread safe. Seul le spam non-error est droppé (justifié : pure debug noise pour un pipeline de harvest).
RAS bloquant. △ mineurs (non-bloquants) :
- Pas de timeout Goto/Evaluate ajouté en defense-in-depth. Le fix adresse la cause (blocking), ce qui est le bon fix primaire ; un timeout separé pourrait être un hardening follow-up (le commentaire note « no timeout » = ce qui rendait le freeze indéfini).
lake build/.NET non rejoués ici (pas de toolchain C#) — mais le fix est petit (17L), structurellement sound, et le root cause est vérifié contre l'implémentation réelle du Logger.
Le commentaire de PR est exemplaire (root cause précis, why-did-it-deadlock, why-this-fix). Décision Emerjesse — de mon côté rien ne bloque.
…EN+FR) (#659) Consolidated release-notes draft for the v0.9.0 GitHub Release, building on the po-2023 draft (docs/RELEASE-NOTES-v0.9.0.md) and reflecting the post-bundle-v3 work not yet captured there: Highlights: - 8 languages x 10 documents, print-ready (bundle v3, 80 PDFs, PNG-300-lossless) - Print & Play free + complete: Standard (full game) + Light (sample + Virtues families overview, depth<=2, #645/#648-650) - Print-ready CMYK + SWOP OutputIntent via --pdf-cmyk Ghostscript post-pass (#632/#652) - Rules i18n cleanup (#633->#640), 0 HIGH residual + committed anti-FP scanner Notable fixes since dossier #591: harvest deadlock (#651), Logger hardening (#630/#655), Scenarii 6.1.3 title (#653), GSheet Rules sync (#642). Known limitations: SVG mind maps potentially stale (#636), OWL round-trip #133, DNN CVEs (separate ops milestone). EN + FR (impersonal). NOT published - draft docs only, jsboige validates and pastes into the GitHub Release. Test count flagged as dashboard-baseline (reported, not empirically re-verified) per [[test-counter-empirical-dotnet-test]]. Base ca5db81. Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude-Code <noreply@anthropic.com>
…tability fixes) (#134) (#689) The v0.9.0 entry predating bundle v3 missed the print-production and late-cycle stability work. This refresh brings it in line with master `21e2c666`, cross-checked against the release-validation dossier v4 (docs/RELEASE-VALIDATION-v0.9.0.md §3.3) and dashboard decisions #26-69. Added: - "Print Production (CMYK & Print&Play)" section: Ghostscript post-process (#632), `--pdf-cmyk` entry-point (#652), bundle v3 = 80 PDFs DeviceCMYK + SWOP OutputIntent (6.18 GB RGB -> 5.30 GB CMYK), P&P Standard/Light (#645/#648-650, 64 -> 80 PDFs), GS timeout 180->900s (#670) - "OWL Ontology (Bilingual EN/FR)" section: Fallacies OWL 5.07 MB (#634 regen), Virtues OWL (#592/#499 Phase 2), honest scope note (EN+FR only, not 8-lang) - "Fixed - Pipeline Stability (Jun-Jul 2026)" section: harvest deadlock #651, serial retry #613/#676, logger Spectre #630/#655, CMYK oxymore, Johnny 6.1.3 #653, Rules i18n refonte #640, CSV hygiene #579/#581/#584, OWL staleness #634 Corrected (code=truth): - Magick.NET 14.13.1 -> 14.14.0 (verified in .csproj) - Test count 548 -> 578 pass / 1 known-fail #133 / 5 skip / 584 total (empirical `dotnet test` on Argumentum.AssetConverter.Tests, 2026-07-04, .NET 9). Previous counts in docs (548/549/570) were all stale No existing content regressed; all prior sections preserved. Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude-Code <noreply@anthropic.com>
…hecklist) (#782) Dispatch 28xdu9 (ai-01, primaire "Readiness regen release"). Base master c1ed77d. A freshness refresh of the 07-04 regen-readiness checklist (the operational harness stays; this corrects drift + adds post-07-04 lessons). No regen launched (HOLD until jsboige GO visuel sem. 13/07). Corrections to the 07-04 checklist (staleness fixed): - Mindmap 8-lang: STALE "ar/fa/zh still need RDP" -> 41 SVGs 8/8 SHIPPED on master (git ls-tree: fr=6, en/ru/pt/es/ar/fa/zh=5 each). Coverage gap CLOSED. - Test baseline: 578 -> 595 pass / 1 fail (#133) / 5 skip / 600 total. - AIF #498: 6 new columns in Fallacies Taxonomy.csv (AIF_attackType etc., 121 fully-modeled) = the real regen input delta (cards+OWL now AIF-enriched). Post-07-04 operational lessons added to the fire-time gate: - dotnet run build-server deadlock (build-server shutdown + build + run --no-build). - Playwright deadlock RESOLVED #651 (stall-watcher measures chromium child CPU). - CardPen local validated by 07-01 Release regen (Pages /Cards/ 404 = structural #629). - AssetConverter Console.ReadKey headless crash (pre-existing config -> prompt skipped). - Harvest clobber still MANDATORY (stale .harvest.json would hide AIF-CSV changes). Readiness verdict: master c1ed77d regen-ready, no code blocker; remaining items are fire-time conditions (RDP foreground, CardPen local up, harvest clobber, GO visuel). Doc-only. No Cards/ edit (po-2024 lane). No regen launched. No visual verdict (ai-01). Co-authored-by: Claude-Code <noreply@anthropic.com>
… full-IIS) (#792) The 3 #131/#132 runbooks were written Jun 15-25 as PRE-execution prep, before the migration was delivered. They now describe steps that have already happened (or carry an undocumented lesson that would bite a future executor). This refresh adds POST-EXECUTION REALITY banners + one targeted correctness addition — without rewriting the historical procedure (a valid record of the prep/reasoning). Changes (docs-only, 3 files, +85/-1): - 131-step1-sandbox-upgrade-runbook.md: POST-EXEC banner — marks the sandbox upgrade DELIVERED (10.3.2+2sxc21 live full-IIS, ACME bypass active), points to phase2-exec-rollback + MEMORY + UPGRADE-ASSESSMENT as current truth, flags 2 staleness items resolved (§2 "2sxc 15.02" was stale — already 21.07; §5 "templates untouched" moot — cliff crossed). - 131-regen-staging-runbook.md: REFRESH banner — scope DEFERRED 4-vs-8 lang RESOLVED (8-lang shipped #565, 41 SVGs), foreground-lock mitigated (#569 tscon recipe), Playwright deadlock resolved (#651), build-server deadlock lesson; points to regen-readiness-refresh-c1ed77d2 (#782) as the live harness. - 132-deployment-runbook.md: POST-EXEC banner (sandbox+full-IIS delivered, prod VPS go-live remains the gated frontier) + the critical B1-inversion lesson added to §5.5(a): 2sxc-21 REQUIRES .NET 8/9 BCL stack (Json 9.0.0.0/SCI 9.0.0.0/Bcl 8.0.0.0), NOT the 6.0.0.0 reversion the early repair-bin-net48.ps1 encoded. This was a real gap — the runbook predates the B1 incident and would have led a future executor to break 2sxc 21 with JsonOptions MissingMethodException. po-2023 lane (docs/dnn-localization/). Non-gated. No Cards/, no .cs, no DB. Gated ai-01 review before merge. Co-authored-by: Claude-Code <noreply@anthropic.com>
fix(harvest): résoudre le deadlock Playwright reproductible (#651) — chemin critique bundle v3 / tag
Répond à l'ASK de po-2023 (dashboard 2026-07-02 15:25) : bundle v3 impossible à produire, le harvest deadlock Chromium de façon reproductible 3/3 après ~2-3 min, toujours au même point (harvest Rules, après plusieurs
Loaded 15 items). Zombie CPU-flat, pas de crash (doncContinueOnHarvestSetFailure#614 ne le catch pas), pas de timeout, freeze indéfini.Root cause (ancrée dans le code, pas une supposition)
GenerateHarvestImagess'abonnait àpage.Consoleavec un handler qui logguait CHAQUE message console du navigateur en 5 lignesLogverbatim, exécutées synchronement sur le thread mono d'événements de Playwright :Chaque
Logfait unFile.AppendAllText(open/write/close souslock(fileLock),Logger.cs:83-86) + desAnsiConsole.WriteLine— etAnsiConsole(Spectre.Console) n'est PAS thread-safe, donc le thread harvest ET le thread d'événements y accèdent concurremment.En full harvest clean-slate, CardPen inonde la console pendant
cardpen.write.generate. L'I/O synchrone lente par message + l'accès AnsiConsole concurrent saturent/stallent le transport Playwright → lesawaitGotoAsync/EvaluateAsyncsuivants ne reçoivent jamais leur réponse → freeze 0-CPU indéfini sans timeout (le mécanisme de timeout lui-même dépend de la dispatch-loop stallée).Pourquoi maintenant ? C'était le 1er full harvest frais depuis le clobber (volume console maximal) post-#640 (Rules refonte) / #645 (P&P). Les runs antérieurs avaient des harvests cachés (moins de navigations fraîches → moins de flood). D'où l'apparition à Rules (tôt, verbeux, 8 langues).
Fix (delete-first, ciblé, non-pendulaire)
Le handler capture désormais uniquement les messages de niveau
error, dans uneConcurrentQueuelock-free (zéro I/O, zéro AnsiConsole sur le thread d'événements). Les erreurs capturées sont drainées sur le main thread dans lefinally. Le flood normal-path (pure instrumentation debug--- CONSOLE MESSAGE RECEIVED ---) est supprimé ; la visibilité des vraies erreurs CardPen est préservée.Validation
Si insuffisant (plan B documenté)
Si le deadlock récidive malgré le fix, la cause secondaire candidate est l'accumulation d'état sur l'
IPageréutilisée (page-poolConcurrentStack<IPage>, une seule page sur des centaines de navigations en mode serial) → prochain incrément = fresh context/page par CardSet.Relates #629 (stabilité Playwright), #645 / #134 (bundle v3, chemin critique tag).
🤖 ai-01