Repository navigation
feat(frontend): admin UI for outbound mutation reconciliation - #786
Merged
Merged
Conversation
Adds a screen-driven flow so an operator no longer needs raw curl and a manually-extracted admin JWT (docs/RUNBOOK.md §0.1) to resolve a stuck outbound mutation: - protocol.js: list/get/register-evidence/resolve/approve wrappers for the existing AdminOutboundMutationEndpoints API. - state.js: outboundMutations / outboundMutationDetail slices. - adminUi.js + index.html: new "Outbound mutation reconciliation" panel — filterable list, expandable detail, evidence-registration form, propose-resolution form. - Genuinely separate maker/checker actions: "Propose resolution" and "Approve" are two different UI actions, not a same-user double-click. The Approve button is hidden client-side (UX hint only) when the pending proposal's makerRef matches the current session's username; the server independently rejects self-approval regardless. - app.js: wiring + refreshOutboundMutations hooked into refreshAdminData(), 401/422/409/503 feedback mapping. No backend, auth, or automated-decision changes — purely a UI for the already-implemented, already-tested admin API. Closes #785 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pproval identity Address code-review findings on PR #786: - state filter options (conflicting/resolved) didn't exist in the backend OutboundMutationState enum; replaced with the actual wire-format values (ambiguous, recorded_pending_approval, venue_acknowledged, operator_resolved). - the self-approval hint compared proposal.makerRef against session.username, but local-login overwrites username with the raw login-form input (possibly different casing than the JWT sub the server actually uses as makerRef). Now compares against claimsFromToken(token).sub directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pedrosakuma
marked this pull request as ready for review
August 7, 2026 22:34
…ce gate) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
3 of 4 tasks
pedrosakuma
added a commit
that referenced
this pull request
Aug 11, 2026
…und mutations (#787) * feat(frontend): guided one-click resolution for venue-confirmed outbound mutations Refs #785, follow-up to #786. The reconciliation panel shipped in #786 exposed the full admin API surface (evidence registration, resolution decision, evidence type, raw evidence reference, reason code) as a single generic form. For the common case — mutations already in `venue_acknowledged` state with an eligible terminal Execution Report already recorded by the platform — this made operators manually locate and copy a 64-hex evidenceId out of a raw JSON blob for every mutation, one at a time. Add a guided batch action: - The mutation list now surfaces a "Resolve N confirmed mutations" banner whenever any listed mutation is `venue_acknowledged`, requires reconciliation, and has no pending proposal. - Clicking it loads each candidate's detail, selects its eligible terminal ER evidence automatically (`findTerminalExecutionReportEvidence`), and submits `venue_acknowledged` / `terminal_er` / `terminal_er_verified` for all of them behind a single confirmation dialog. - Mutations without eligible evidence are left untouched and reported back to the operator for advanced review. - The full evidence-registration/propose-resolution/approve forms move behind an "Advanced reconciliation" `<details>` disclosure — still available unchanged for `venue_absent`/`leave_ambiguous`/ registered-evidence flows, which genuinely need the raw fields. No backend/API changes. Purely a frontend UX layer on top of the existing `/api/admin/outbound-mutations` endpoints from #785/#786. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: re-validate mutation state from fresh detail before guided batch resolve Code review (gpt-5.5) flagged that handleResolveVenueConfirmedBatch only checked for eligible terminal-ER evidence after fetching each mutations detail, but never re-checked state/requiresReconciliation/pendingApproval against the freshly-fetched detail payload. A mutation that flipped out of venue_acknowledged (e.g. reopened ambiguous by a late contradictory ER) between the list render and this call could still be auto-resolved by the guided one-click path using a stale terminal ER. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: pedrotravi <pedrotravi@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an admin frontend screen for outbound mutation reconciliation, so an operator no longer needs raw
curl+ a manually-extracted admin JWT (docs/RUNBOOK.md §0.1) to resolve a stuck outbound mutation.Closes #785
What changed
protocol.js: 5 new wrappers mirroringAdminOutboundMutationEndpoints.csexactly —listOutboundMutations,getOutboundMutation,registerOutboundMutationEvidence,resolveOutboundMutation,approveOutboundMutationResolution.state.js:outboundMutations(list) andoutboundMutationDetail(expanded row) slices, following the existingset*/notifyconvention.adminUi.js+index.html: new "Outbound mutation reconciliation" admin panel — filterable list (state / requiresReconciliation), expandable per-mutation detail (raw JSON of attempts/evidence/proposals/resolution), an evidence-registration form, and a propose-resolution form.app.js: handlers wired viasetAdminHandlers,refreshOutboundMutationshooked intorefreshAdminData(), status-code-specific feedback (401 → logout, 422 → invalid evidence, 409 → conflict, 503 → unavailable).Maker/checker design (per discussion on #785)
Only the
venue_absentdecision is capacity-releasing and requires a genuinely different admin to approve (OutboundReconciliationService.Approve, server-side self-approval rejection). Unlike the existing killswitch/halt panels (same-userconfirm()double-click), this UI implements "Propose resolution" and "Approve" as two separate actions:makerRefequals the current session's username (session.usernameis populated from the same JWTsubclaim the server uses asmakerRef— confirmed viaJwtIssuer.cs).Non-goals (unchanged from #785)
Testing
frontend/test/outbound-mutations-ui.test.mjs(+ fixturefrontend/test/fixtures/outbound-mutations.json):Authorizationheader) for all 5 new functions.operations-ui-states.test.mjs): list rendering incl. pending-approval badge, row-expand → detail load, and — the key design requirement — the Approve button is verified hidden when the current session is the proposer and shown (and wired to the right mutationId/proposalId) when a different admin proposed it.npm test(428/428) passes, including all pre-existing suites, infrontend/.Not in this PR
Actually resolving FIRM01's 7 legacy stuck mutations (from the July 25 incident, root-caused in #773/fixed by #778) — that's an operational action for a real Entra-authenticated admin to perform via this new screen once merged/deployed, not something this PR does.
Protocol evidence gate
Does this PR add or change a dependency on B3MatchingPlatform / B3MarketDataPlatform wire behavior?
Checklist: