Skip to content

test(frontend): cover the overseer decision -> run refresh flow - #40

Merged
vbabenko97 merged 2 commits into
mainfrom
babenko/frontend-decision-tests
Sep 25, 2026
Merged

vbabenko97 merged 2 commits into
mainfrom
babenko/frontend-decision-tests

Conversation

@vbabenko97

@vbabenko97 vbabenko97 commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #39, which is now merged; this PR targets main. CI ran while it was stacked on #39's branch, whose tree matches main after that merge.

Summary

#39 made the UI reload the displayed run after an overseer decision, but no automated test covered that flow. This PR adds tests for approve/reject → result refresh and fixes three user-visible problems found along the way.

Changes

Decision flow, now tested (frontend/app/decision.js, frontend/test/decision.test.js)

  • The approve/reject handler moved out of page.jsx into decideAndRefresh(api, request, { setPending, setNotice, setRun }). page.jsx calls it with its own state setters, so the tests exercise the same state updates the page makes.
  • The tests use a fake API modelled on server/api.py (overseer_decide, _decision_update, the RunStatus shape):
    • Approve or reject on the displayed run: posts the decision, removes the queue row, reloads the run, and ResultView shows the released result or the rejection.
    • Decision on a different spec, or with no run id: nothing is reloaded.
    • Failed decision: the error is shown and nothing else changes.
    • Failed reload: the recorded decision is still reported.

Fixes found on the way

  • A rejection announced "Result rejectd."; it now reads "Result rejected."
  • A rejected run said "Output withheld pending release."; it now says "Output rejected by the overseer."
  • heldReason() expected k_anon:-style prefixes, but the API sends bare codes (PublicReason, _public_reasons). Every real flagged or rejected run therefore showed the generic "Disclosure review required." It now matches the code before any :, so both forms work. A new test uses the bare codes.
  • A failed reload after a successful decision replaced the success notice with the bare error. The notice now keeps the decision and appends the error.
  • AGENTS.md: CI runs on pushes to main and on every pull request, not "on every push".

Verification

  • CI on this PR is the first time these tests run. A local policy hook blocks node and npm in the environment where this was written.
  • Before pushing, a static review traced every assertion in both test files against result-view.jsx, decision.js and the installed react-dom static renderer. It found no failing assertion. The gaps it raised (untested state wiring, the runId guard, the POST body, fixture shapes) are addressed in this PR.
  • git diff --check: clean.

Not covered

  • The onClick → decide() binding in page.jsx. A component-level test needs a DOM environment (jsdom and Testing Library), which would be a new dev dependency.
  • A stale closure predating this PR: if a new run is submitted while a decision request is in flight, the reload can replace the new run on screen with the old one. This is unlikely in practice: deciding happens on the overseer view and submitting on the analysis view.

decide() in page.jsx posted the overseer decision and then re-read the
displayed run, but no test exercised it: the frontend tests only
rendered ResultView. The handler now lives in app/decision.js
(decideAndRefresh), which page.jsx calls with its state setters, and
test/decision.test.js drives it against a fake API modelled on
server/api.py and checks the resulting page state:

- approve / reject of the displayed run posts the decision, drops the
  queue row, re-reads the run, and ResultView then shows the released
  result / the rejection
- a decision on another spec, or without a run id, re-reads nothing
- a failed decision is shown and changes nothing else
- a failed re-read still reports the recorded decision

User-visible fixes found on the way:
- a rejection announced "Result rejectd."; it now reads "Result
  rejected."
- a rejected run still said "Output withheld pending release."; it now
  says "Output rejected by the overseer."
- a failed re-read replaced the success notice with the bare error; the
  notice now keeps the decision and appends the error

Only the button -> decide() binding in page.jsx stays untested; a
component-level test would need a DOM test environment, a new dev
dependency.
RunStatus.reasons carries bare codes (server/schemas.py PublicReason;
server/analysis_service.py _public_reasons keeps only the part before
':'), but heldReason() matched "k_anon:"-style prefixes, so every real
flagged or rejected run showed the generic "Disclosure review
required." It now matches on the code before any ':', which covers
both forms; the existing prefixed-reason test still holds and a new
test uses the codes the API actually sends.

AGENTS.md: CI runs on pushes to main and on every pull request, not on
every push.
@vbabenko97
vbabenko97 changed the base branch from babenko/disclosure-governance to main September 25, 2026 09:31
@vbabenko97
vbabenko97 merged commit 81672cb into main Sep 25, 2026
4 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