Skip to content

feat: browser case import and replay with real browser workflow tests - #34

Merged
EauDoon merged 2 commits into
mainfrom
feat/gui-import-replay
Sep 6, 2026
Merged

feat: browser case import and replay with real browser workflow tests#34
EauDoon merged 2 commits into
mainfrom
feat/gui-import-replay

Conversation

@EauDoon

@EauDoon EauDoon commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Capability (approved milestones 1 and 2)

Import an exported case in the local GUI and replay it without a
terminal and without executing the original action: file selection,
replay control, per-check results, and explicit unavailable /
conflicting / unsupported / failed states. Verification reuses the
existing replayBundle (rail bundle verify + MandateBound review);
no verifier is reimplemented in the browser.

Real browser workflow tests (Playwright, Chromium) drive actual clicks,
file selection, and async responses against the pinned components.

Trust and safety properties (tested)

  • Size cap enforced server-side from the byte stream, not from
    Content-Length; strict content-type; non-object/empty bodies rejected;
    no attacker-controlled paths and no URL fetch from imported evidence;
    embedded keys are never trusted for integrity.
  • Import/replay spawns exactly two verification commands — asserted by
    recording argv. No action execution or remediation is reachable.
  • All imported values are escaped; the imported case is labelled
    separately from any live run and states synthetic keys, no
    provenance, source truth unknown, no legal effect.
  • One replay at a time; rejected uploads drained to a ceiling so the
    caller still gets its error (verified: 1.5 MiB and 3 MiB both return
    413, server stays healthy).

Fixes found during review

  • record(false, "review-request", …) swapped arguments made a failing
    check render as pass.
  • Flagship browser assertion matched "not verified" as a substring of
    "verified"; now asserts the headline exactly.

Verification

  • npm test: 91/91 (endpoint tests for real exported case, conflicting,
    unavailable, malformed, oversized, non-object, wrong content-type,
    repeated imports, no-execution argv, status mapping, escaping).
  • npm run test:browser: 7/7 real browser journeys.
  • npm run gui:smoke passes. CI adds a bounded browser job (Chromium,
    Node 22.12.0, Python 3.13) alongside the existing integration jobs.

Devin Review

An operator receiving an exported case can now inspect and replay it in
the local GUI: file selection, a replay control, per-check results, and
explicit unavailable/conflicting/unsupported/failed states.

- Server: POST /api/replay accepts an exported case as JSON, bounded to
  1 MiB enforced from the byte stream (not from any client claim), with
  strict content-type and object checks. It calls the existing
  replayBundle, so verification (rail bundle verify, MandateBound
  review) is reused rather than reimplemented in the browser. Only one
  replay runs at a time, and rejected uploads are drained to a hard
  ceiling so the caller still receives its error.
- Import/replay never executes an action or remedy: the path spawns
  exactly two verification commands, asserted by a test that records
  their argv.
- Imported identity is rendered as untrusted, escaped text and labelled
  separately from any live run; results state synthetic keys, no
  provenance, source truth unknown, and no legal effect. Imported
  results clear when a new run starts.
- Fixes a swapped record() argument that reported a failing
  review-request check as passing.
- Playwright (Chromium) workflow tests drive real clicks, file
  selection, and async responses against pinned components: full
  run/export/import/replay journey, refusal, repeated runs, stale
  clearing, malformed, unavailable, oversized, and tampered imports.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

Devin Review

Comment thread bin/aas-gui.mjs
const replayButton=document.getElementById('replay');
let latestToken=0;
let importToken=0;
function clearImported(){ importToken++; importResult.innerHTML=''; importStatus.textContent=''; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 New runs permanently disable replay

When a run starts during an import, clearImported invalidates it without restoring the replay button. The replay handler exits on the stale token, leaving replay disabled until reload.

Suggested change
function clearImported(){ importToken++; importResult.innerHTML=''; importStatus.textContent=''; }
function clearImported(){ importToken++; importResult.innerHTML=''; importStatus.textContent=''; replayButton.disabled=false; }
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread bin/aas-gui.mjs
Comment on lines +340 to +342
// Replay runs child verifiers synchronously; allow only one at a time so
// repeated clicks cannot queue unbounded blocking work.
let replaysInFlight = 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Replay requests queue without limit

replaysInFlight starts at zero for every request, so concurrent replays never observe another replay. Each request queues two synchronous verifiers instead of receiving the intended 503 response.

Prompt for agents
The replay concurrency state in bin/aas-gui.mjs is scoped to each request callback, so every POST /api/replay sees zero. Move replay admission state to the createGuiServer closure and reserve a replay before body processing or verifier work begins. Ensure every validation, parsing, verifier, and response path releases the reservation. Because replayBundle uses synchronous child processes that block the event loop, account for requests already waiting while a verifier runs so they are rejected rather than processed sequentially. Add a test that overlaps two replay requests and verifies one receives 503 without invoking its verifier commands.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@EauDoon
EauDoon merged commit eac0d26 into main Sep 6, 2026
10 checks passed
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.

1 participant