Landing blocker for #61 and #63: the deployment checkout is 12 commits behind and `merge --ff-only` refuses while tracked paths are dirty. Discard is not available here (workspace policy: no `checkout`/`stash` on shared state), so the dirt is committed. - `agent/APPEND_SYSTEM.md`: the point-of-use / one-work-log / PR-link doctrine, already byte-identical to `origin/main` (`cmp` against the trunk blob: no difference). No new prose — the commit exists so the trunk merge can proceed. - `agent/config.yml` and `agent/managed-skills/**`: the machine-live tier the heartbeat owns (AGENTS.md § Live-sync tiers). `ALLOW_SECRETS=1` was used, deliberately and after auditing the staged diff by hand: the only credential-shaped strings in it are two copies of `AKIA0000000000000EXAMPLE` — the inert all-zero AWS example id — and they appear inside the two skill pages that teach that exact placeholder shape. This checkout is still running the pre-#61 hook; the merged gate (value-scoped placeholder filter, `71601f7`) exempts this value, so the refusal dies with this landing. No `--no-verify` was used: the gate ran and its verdict is recorded here. Refs #61
3.1 KiB
name, description
| name | description |
|---|---|
| react-prod-state-mismatch-bisection | Diagnose React SPA prod-build state mismatches (state-derived props update but sibling output stays stale) by driving a real browser and bisecting per-render state via throwaway source instrumentation. |
Prod React state-mismatch bisection
Use when a built SPA shows an internally inconsistent DOM — e.g. aria-current flips
while a sibling conditional slot stays stale, a click handler runs with zero network
activity, jsdom tests pass but the prod journey fails. Reasoning from source fails
here; only per-render state truth resolves it.
Steps
-
Drive the real browser, not tests. Throwaway
@playwright/testchromium script beside the package that installs it (e.g.tests/), seeded via direct API calls + cookie injection:import { chromium } from "@playwright/test"; // resolve from tests/ node_modules const ctx = await browser.newContext(); await ctx.addCookies([{ name, value, domain: "127.0.0.1", path: "/" }]); page.on("console", ...); page.on("pageerror", ...); page.on("request", ...);Run it from that directory; a script at repo root or /tmp hits
ERR_MODULE_NOT_FOUNDfor workspace deps. -
Fiber prop probe — call the handler outside React events to split "delegation dead" from "handler wrong", and read the props the fiber holds:
await page.evaluate(() => { const el = document.querySelector('[data-testid^="thing-"]'); const key = Object.keys(el).find((k) => k.startsWith("__reactProps$")); el[key].onClick(); // or .onChange({target:{value:"x"}}) }); -
If props updated but output is inconsistent, suspect falsy-equality, not React.
undefined === undefinedyields"true"forel.id === selectedIdhighlights — a fake "state updated" signal. Dump the slot's rawinnerHTML, don't trust the derived attribute. -
Instruments beats deduction: insert temporary
console.logs in the component source (one in each state-mutating callback, one at render logging the state), rebuild, restart the server, re-run the script. Compare per-render values — the render where the value isundefinedinstead of the id names the root cause (usually a server payload keyed differently than the client type, e.g. DB row keyorgIdcrossing HTTP where the contract saysid). -
Also grep every suite teardown for broad
DELETE ... LIKE/TRUNCATEon the shared DB when a test flakes only under uncached concurrent runs (turbo run test --force) and passes isolated: parallel vitest files on one DB — the suite that finishes first FK-cascades away the other's live sessions; the symptom is a spurious 401 (or missing row) in the still-running file. Fix by namespacing emails/ids per suite and scoping teardowns to rows the suite created.
Cleanup
Remove instrumentation, move the throwaway script to .AGENTTRASH/, never commit the
debug script. Verified on franksign: GET /api/orgs leaked orgId where the client
contract was id (fake aria-current true, silent auto-select failure).