Skip to content

fix(harvest): #651 resolve reproducible Playwright deadlock (console-flood on event thread) - #651

Merged
jsboige merged 1 commit into
masterfrom
fix/harvest-playwright-deadlock-console-flood
Jul 2, 2026
Merged

fix(harvest): #651 resolve reproducible Playwright deadlock (console-flood on event thread)#651
jsboige merged 1 commit into
masterfrom
fix/harvest-playwright-deadlock-console-flood

Conversation

@jsboige

@jsboige jsboige commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

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 (donc ContinueOnHarvestSetFailure #614 ne le catch pas), pas de timeout, freeze indéfini.

Root cause (ancrée dans le code, pas une supposition)

GenerateHarvestImages s'abonnait à page.Console avec un handler qui logguait CHAQUE message console du navigateur en 5 lignes Log verbatim, exécutées synchronement sur le thread mono d'événements de Playwright :

void Page_Console(object sender, IConsoleMessage msg) {
    Log("--- CONSOLE MESSAGE RECEIVED ---");
    Log($"Type: {msg.Type}"); Log($"Text: {msg.Text}");
    Log($"Location: {msg.Location}"); Log("--- END CONSOLE MESSAGE ---");
}

Chaque Log fait un File.AppendAllText (open/write/close sous lock(fileLock), Logger.cs:83-86) + des AnsiConsole.WriteLine — et AnsiConsole (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 → les await GotoAsync/EvaluateAsync suivants 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 une ConcurrentQueue lock-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 le finally. Le flood normal-path (pure instrumentation debug --- CONSOLE MESSAGE RECEIVED ---) est supprimé ; la visibilité des vraies erreurs CardPen est préservée.

  • Surgical : 1 fichier, +17/-5.
  • Build : 0 erreur (warnings SGEN/WebClient pré-existants).
  • Aucun changement de comportement du harvest hormis la suppression du flood de logs et de son deadlock.

Validation

  • Autoritative : po-2023 re-run le harvest avec son repro fiable 3/3. Signature de succès = passer le point Rules qui gelait à ~2-3 min, harvest complet 8 langues.
  • Le fix cible exactement le mécanisme observé (freeze 0-CPU sans timeout = dispatch-loop stallée, pas un timeout de navigation).

Si insuffisant (plan B documenté)

Si le deadlock récidive malgré le fix, la cause secondaire candidate est l'accumulation d'état sur l'IPage réutilisée (page-pool ConcurrentStack<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

…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 clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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/WriteLine par 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) :

  1. Filtre error-only (msg.Type == "error") → élimine le flood verbose (le bruit debug n'est plus capturé). Réduit drastiquement la charge. ✅
  2. Queue lock-free sur le hot path : ConcurrentQueue<string>.Enqueue — non-bloquant, 0 I/O, 0 AnsiConsole sur le thread event → travail quasi-nul. ✅
  3. 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). ✅
  4. Unsubscribe préservé (page.Console -= Page_Console avant le drain → stoppe les nouveaux messages, puis drain le backlog). ✅
  5. 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.

@jsboige
jsboige merged commit aa926ff into master Jul 2, 2026
3 checks passed
@jsboige
jsboige deleted the fix/harvest-playwright-deadlock-console-flood branch July 2, 2026 16:54
jsboige added a commit that referenced this pull request Jul 3, 2026
…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>
jsboige added a commit that referenced this pull request Jul 4, 2026
…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>
jsboige added a commit that referenced this pull request Jul 11, 2026
…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>
jsboige added a commit that referenced this pull request Jul 12, 2026
… 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>
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