v1.3.36.7 -- Capture: drop threats h2 wait¶
A bugfix on top of v1.3.36.6. The h2:has-text("Active decisions") wait introduced in v1.3.36.6 timed out at 5 s in the operator's prod despite the h2 being unconditional in Threats.tsx source. Cause unconfirmed (Playwright text-match glitch / momentary visibility blip / unknown). Fix: drop the h2 dependency entirely and rely on the proven-reliable h1 anchor + waitForSettled + a 1 s render-settle.
argosVersion and frontend/package.json deliberately stay at 1.3.35.4 (tooling-only). scripts/capture/package.json bumps 1.3.36.6 → 1.3.36.7.
Why¶
Re-investigation of Threats.tsx:
return (
<div className="p-6 max-w-[1400px] mx-auto space-y-4">
<div className="flex items-center justify-between">
<h1>Threats</h1> {/* always rendered */}
<button>refresh</button>
</div>
{err && <div>{err}</div>}
{status?.state === 'not_configured' && <SetupBanner />}
<div className="grid ...">stats cards</div>
<section>
<h2>Active decisions</h2> {/* always rendered (in source) */}
{!decisions ? <Loading />
: decisions.length === 0 ? <Empty />
: <DecisionsTable />}
</section>
<AddDecisionForm />
<section><h2>Collections</h2>...</section>
</div>
);
Static reading: the main Threats() component has a single return (line 66), no early returns, no Suspense, no lazy rendering. The <h2>Active decisions</h2> at line 117 is inside an unconditional <section> at line 115 — not wrapped in any {condition && ...}. It should render on every page load.
Static reading and operator runtime disagree. The 5 s waitForSelector('h2:has-text("Active decisions")', ...) times out in the operator's prod. v1.3.36.6's premise (both anchors always render) was wrong in practice for unknown reasons.
Hypotheses I can't disprove without runtime instrumentation¶
- Playwright
:has-textmatching glitch — should match<h2>Active decisions</h2>via case-insensitive substring. Hard to fail. Possible if React re-renders mid-wait and the element loses its identity for a moment. - Off-viewport visibility — Playwright's default
state: 'visible'doesn't require in-viewport, just non-empty bbox + notdisplay: none. Hard to fail. - Layout-shift loading state — the page might briefly render with the h2 inside a parent with
display: nonewhile data fetches. Unlikely given Threats.tsx structure but I can't disprove without runtime logging. - Image-source mismatch — operator's panel runs
argos-prod-argos:1.3.35.Threats.tsxlast touched at commit04fb22a("cap content max-width at 1400px") — pre-v1.3.35 — so the deployed UI matches the source I read. Mismatch unlikely but not zero.
I can't get more evidence without running Playwright against the operator's prod with verbose logging. Per operator spec, Option B drops the h2 dependency and relies on the resilient h1 + waitForSettled + render-settle chain.
Fix¶
test('20. threats-decisions.png', async ({ page }) => {
await page.goto('/threats');
await page.waitForSelector('h1:has-text("Threats")', { timeout: 10_000 });
await waitForSettled(page);
await page.waitForTimeout(1000); // render-settle for conditional sections
await shotFullScroll(page, 'threats-decisions');
});
h1:has-text("Threats")— proven reliable in the operator's prod (v1.3.36.6 reached this point before failing on the h2 wait).waitForSettled— gates "data fetched" via networkidle with 3 s fallback.waitForTimeout(1000)— bumped from 500 to 1000 ms to give any conditional sections (SetupBanner, error banner, Loading → Empty / DecisionsTable transition) a frame to paint.
Whatever DOM state exists at screenshot time is reality — the docs portal can legitimately illustrate Loading / Empty / DecisionsTable / SetupBanner / error states. The screenshot doesn't need to fake decision data or wait for a specific section header.
This trades one validity-gate (h2 must render) for resilience against unknown render-state edge cases. Acceptable since the screenshot is the source of truth, not the intermediate selector wait.
Smoke phase 14 update¶
Three asserts (down from four):
14. threats-decisions selector fix:
- Active code uses h1:has-text("Threats")
- Old broken 'table, [role="tabpanel"]' selector gone
from active code (comment lines documenting the
failure mode are allowed)
- v1.3.36.6's h2:has-text("Active decisions") wait
gone from active code (Option B drops it)
- Synthetic verify against Threats.tsx: file still has
<h1> AND mentions 'Threats' (page-rename
regression-guard)
The "Active decisions" anchor check is dropped (Option B no longer needs it). The new check rejects v1.3.36.6's h2 wait from active code so the regression can't sneak back.
Smoke result post-fix¶
phase 1: run.sh refuses without .env... PASS
phase 2: .env is git check-ignore'd... PASS
phase 3: .env.example placeholders only... PASS
phase 4: safeClick synthetic test... PASS (13/13)
phase 5: working tree unchanged by smoke... PASS
phase 6: storageState wiring (v1.3.36.1)... PASS (5/5)
phase 7: banner output uses fs.readFileSync... PASS
phase 8: viewport 1440x1080 + shotFullScroll... PASS
phase 9: waitForSettled helper (timing fix)... PASS (5/5)
phase 10: openModal modal-visibility wait + TG selector... PASS (6/6)
phase 11: host-row triggers click button[aria-label=edit]... PASS (2/2)
phase 12: safeClickTab helper + tab-click migrations... PASS (7/7)
phase 13: DNS-01 selector fix... PASS (3/3)
phase 14: threats-decisions selector fix... PASS (4/4)
scripts/check-no-personal-data.sh clean. mkdocs build --strict clean.
Files changed¶
scripts/capture/capture.spec.js— test 20 drops h2 wait, bumpswaitForTimeout500 → 1000 ms; comment block documents the v1.3.36.6 unexplained-failure history.scripts/capture/package.json—1.3.36.6→1.3.36.7.scripts/smoke/capture-automation.sh— phase 14 updated (h2 check dropped; v1.3.36.6 h2 wait rejected from active code).docs/release-notes/v1.3.36.7.md— this file.CHANGELOG.md,mkdocs.yml.
NOT changed: argosVersion stays at 1.3.35.4, frontend/package.json version stays at 1.3.35.4. No Go code; no frontend code; no panel binary change.
Operator workflow post-fix¶
cd ~/argos-edge && git pull
scripts/capture/run.sh
# Verify post-fix:
# 1. threats-decisions.png shows the page (header + stats
# cards + section bodies in whatever state the data
# dictates -- DecisionsTable / Empty / Loading /
# SetupBanner are all valid).
# 2. Test 20 runs in ~3-4s instead of timing out at 5s on
# the h2 wait or 38.8s on the prior table/tabpanel
# wait.
# 3. No regression on other surfaces.
Versioning¶
scripts/capture/package.json 1.3.36.6 → 1.3.36.7. Tag-without-rebuild precedent for tooling-only patches: v1.3.27.1, v1.3.34, v1.3.35.1, v1.3.35.5.