test(asyncify): red-green race harness + ablation flags, unwind-catch shim, spec tightening, decisions docs

The asyncify single-slot work, executed red-green (full ledger:
docs/features/asyncify-arbiter/redgreen.md; decisions record:
docs/features/async/07-decisions-and-outcome.md):

- tests/apps/standalone/asyncify-races/ + tests/asyncify/ + dedicated
  playwright config: 8 scenarios reproducing the KiCad asyncify failure
  family with the kicad-faithful startup topology (pre-park fiber swap →
  park throw through the live trampoline). Built in 3 variants; the
  SHIM_DISABLE_TRAMPOLINE_HEAL / SHIM_DISABLE_HANDLESLEEP ablation builds
  keep the historical hang and index-out-of-bounds crash reproducible
  forever (mutation-style pins for the existing shims).
- scripts/common/shims/handlesleep.js: catch the "unwind" park sentinel
  in the wakeUp path — when main's last pre-park suspension was a sleep,
  the main-loop park throw escaped through that sleep's promise reaction
  as an uncaught rejection (the calculator/gerbview console errors).
- scripts/common/inject-dyncall-shims.sh: SHIM_DISABLE_* ablation knobs.
- Spec tightening (the acceptance bar): 'uncaught exception: unwind'
  tolerance DELETED from pcbnew/eeschema specs; load-pcb gained a hard
  clean-console gate over 5 asyncify corruption signatures.
- wxwidgets pointer bump: modal LIFO resolvers, pump resolve-on-error,
  sync clipboard IsSupported (014f67e6c1).

Final state: asyncify suite 7/7, wx e2e 291/292 (1 skip), KiCad e2e 40
passed / 2 skipped with ZERO corruption signatures in any log across all
six apps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2026-06-12 16:59:07 +02:00
commit 14ca16cbd3
16 changed files with 1593 additions and 13 deletions

View file

@ -493,7 +493,7 @@ test.describe('Eeschema WASM', () => {
const realErrors = testLogger.errors
.slice(baselineErrorCount)
.filter((error) => !error.includes('favicon') && !error.includes('uncaught exception: unwind'));
.filter((error) => !error.includes('favicon'));
expect(realErrors).toEqual([]);
});
});

View file

@ -190,11 +190,6 @@ function runLoadPcbTest(demo: DemoCfg): void {
scale: 'device',
});
// ── The two things this spike actually asserts: no rtree assert,
// no WASM Aborted during the load. The clipboard-polling
// asyncify RuntimeErrors that fire AFTER the board is rendered
// are a separate, pre-existing wasm-port limitation that we
// do not regress on here. ────────────────────────────────────
const allLines = [...testLogger.consoleLogs, ...testLogger.errors];
const rtreeDiag = allLines.filter((l) => l.includes('[RTREE-DIAG]'));
expect(
@ -206,6 +201,26 @@ function runLoadPcbTest(demo: DemoCfg): void {
aborts,
`WASM aborted during ${demo.name} load:\n${aborts.join('\n\n')}`,
).toEqual([]);
// ── Clean-console gate: NO asyncify corruption may surface anywhere in
// the load — not before, not after the board renders. The formerly
// tolerated post-load clipboard/unwind RuntimeErrors are fixed
// (sync clipboard IsSupported in wx; "unwind" sentinel handling in
// scripts/common/shims/handlesleep.js; see docs/features/asyncify-arbiter/).
const asyncifySignatures = [
'index out of bounds',
'indirect call to null',
'uncaught exception: unwind',
'invalid state',
'is not a function',
];
const asyncifyErrors = allLines.filter((l) =>
asyncifySignatures.some((sig) => l.toLowerCase().includes(sig)),
);
expect(
asyncifyErrors,
`Asyncify corruption surfaced during ${demo.name} load:\n${asyncifyErrors.join('\n\n')}`,
).toEqual([]);
});
}

View file

@ -509,7 +509,7 @@ test.describe('PCBnew WASM', () => {
const realErrors = testLogger.errors
.slice(baselineErrorCount)
.filter((error) => !error.includes('favicon') && !error.includes('uncaught exception: unwind'));
.filter((error) => !error.includes('favicon'));
expect(realErrors).toEqual([]);
});
});