Skip to content

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.61.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

  1. Playwright :has-text matching 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.
  2. Off-viewport visibility — Playwright's default state: 'visible' doesn't require in-viewport, just non-empty bbox + not display: none. Hard to fail.
  3. Layout-shift loading state — the page might briefly render with the h2 inside a parent with display: none while data fetches. Unlikely given Threats.tsx structure but I can't disprove without runtime logging.
  4. Image-source mismatch — operator's panel runs argos-prod-argos:1.3.35. Threats.tsx last touched at commit 04fb22a ("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, bumps waitForTimeout 500 → 1000 ms; comment block documents the v1.3.36.6 unexplained-failure history.
  • scripts/capture/package.json1.3.36.61.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.61.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.