fix(mobile): 3 HIGH MobileInbox audit findings (F1/F2/F3) + tests #2909

Merged
devops-engineer merged 1 commits from fix/mobile-inbox-3-high-audit-findings into main 2026-06-15 05:49:11 +00:00
Member

Mobile-chat audit — 3 HIGH fixes (F1/F2/F3)

Authored by Dev Engineer B (MiniMax) under PM dispatch 11ef3ff6; opened via operator (MiniMax's POST /pulls is 403 post-recovery — token re-injection follow-up). Findings tracked in #2908 (9 total; 3 HIGH shipped here, 3 MEDIUM + 3 LOW deferred).

Scope: MobileInbox.tsx + tests only.

  • F1 (155-162): backend fetch failure now renders an error/retry state (role=alert) instead of silently showing "No pending approvals".
  • F2 (81-101): respond() reloads server truth on POST failure instead of restoring a stale snapshot (no row-drop / data-loss).
  • F3 (77-79): justActedRef filters in-flight WS rows — no REQUEST_RESPONDED-vs-optimistic-removal flicker.
  • Tests: one per HIGH (fetch-fail→error state; POST-fail→row preserved; WS-race→no flicker).

GATE — normal, NO bypass: holds for 2-genuine review (codex reviewers capped until Jun 18, approval 8bc2e274) + CEO-Assistant personal diff-review. F1 is on-failure-only (not active-user-harm) → no emergency/admin path.

## Mobile-chat audit — 3 HIGH fixes (F1/F2/F3) Authored by Dev Engineer B (MiniMax) under PM dispatch `11ef3ff6`; opened via operator (MiniMax's `POST /pulls` is 403 post-recovery — token re-injection follow-up). Findings tracked in #2908 (9 total; 3 HIGH shipped here, 3 MEDIUM + 3 LOW deferred). **Scope: `MobileInbox.tsx` + tests only.** - **F1** (155-162): backend fetch failure now renders an error/retry state (`role=alert`) instead of silently showing "No pending approvals". - **F2** (81-101): `respond()` reloads server truth on POST failure instead of restoring a stale snapshot (no row-drop / data-loss). - **F3** (77-79): `justActedRef` filters in-flight WS rows — no REQUEST_RESPONDED-vs-optimistic-removal flicker. - Tests: one per HIGH (fetch-fail→error state; POST-fail→row preserved; WS-race→no flicker). **GATE — normal, NO bypass:** holds for 2-genuine review (codex reviewers capped until Jun 18, approval `8bc2e274`) + CEO-Assistant personal diff-review. F1 is on-failure-only (not active-user-harm) → no emergency/admin path.
devops-engineer added 1 commit 2026-06-15 05:08:18 +00:00
fix(mobile): 3 HIGH MobileInbox audit findings (F1/F2/F3) + tests
CI / Python Lint & Test (pull_request) Successful in 5s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 7s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 6s
Harness Replays / detect-changes (pull_request) Successful in 6s
sop-checklist / review-refire (pull_request_target) Has been skipped
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 9s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 6s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 7s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Has been skipped
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 8s
sop-checklist / na-declarations (pull_request) N/A: (none)
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 6s
sop-checklist / all-items-acked (pull_request_target) Successful in 9s
E2E API Smoke Test / detect-changes (pull_request) Successful in 17s
reserved-path-review / reserved-path-review (pull_request_target) Successful in 10s
CI / Detect changes (pull_request) Successful in 18s
gate-check-v3 / gate-check (pull_request_target) Successful in 13s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 16s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 20s
CI / Platform (Go) (pull_request) Successful in 2s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 3s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
E2E Chat / detect-changes (pull_request) Successful in 23s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
E2E Chat / E2E Chat (pull_request) Successful in 3s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 41s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 30s
Harness Replays / Harness Replays (pull_request) Successful in 1m12s
CI / Canvas (Next.js) (pull_request) Successful in 3m31s
CI / Canvas Deploy Status (pull_request) Successful in 1s
CI / all-required (pull_request) Successful in 4s
qa-review / approved (pull_request_target) Approved via pull_request_review trigger
reserved-path-review / reserved-path-review (pull_request_review) Successful in 9s
security-review / approved (pull_request_target) Approved via pull_request_review trigger
qa-review / approved (pull_request_review) Successful in 10s
security-review / approved (pull_request_review) Successful in 10s
sop-checklist / all-items-acked (pull_request) Compensated by status-reaper (non-required pull_request/pull_request_review governance shadow overridden by successful pull_request_target status; see .gitea/scripts/status-reaper.py)
audit-force-merge / audit (pull_request_target) Successful in 10s
06c3ec66ad
Driver-authorized build ticket (PM dispatch 11ef3ff6). The driver-only PR
tracks all 9 findings from the mobile-chat read-only audit (Kimi 2ff1cdee);
this commit ships the 3 HIGH findings in MobileInbox.tsx with one test
each, as a focused easy-to-review PR.

F1: silent blank state on backend fetch failure. MobileInbox.tsx now
tracks loadError state; the render path shows a distinct error/retry
affordance (role=alert, testid=inbox-load-error, inbox-retry) instead
of the 'No pending approvals' empty copy. Critical for destructive-
approvals review: a clean-looking inbox during a backend outage would
let destructive actions go unreviewed.

F2: respond() prev-closure wiped a row that arrived via WS during the
in-flight POST. Fixed by re-loading from server truth on POST failure
instead of restoring a stale items snapshot (server is the source of
truth). The just-acted row is preserved on the next render.

F3: WS REQUEST_RESPONDED race with optimistic removal caused a row
flicker. Fixed by tracking justActedRef in a Set; load() filters
justActedRef out of the response. The set is cleared on POST
completion (success: server no longer returns the row; failure:
catch re-loads from server truth).

Tests: one per finding. MobileInbox test file now covers all three
race/failure paths via deferred api.get/api.post + a captured
useSocketEvent callback. All 265 mobile-suite tests + 40 chat-hooks
tests pass. Lint clean.

Refs: tracked Gitea issue will be created next; PR description will
reference it once PM opens the PR via the operator push step.
agent-reviewer-cr2 approved these changes 2026-06-15 05:48:47 +00:00
agent-reviewer-cr2 left a comment
Member

5-axis review — APPROVE. head 06c3ec6 (#2908 F1/F2/F3, 3 HIGH)

  • Correctness ✓ — All three fixes are sound. F2 replaces the stale setItems(prev) restore with load() (re-fetch server truth), and correctly updates the respond deps from [acting, items][acting, load] since it now uses the functional setItems((cur) => …) form — this also kills a latent stale-closure bug. F3's justActedRef is filtered in load().then and cleaned on every code path (success delete, catch delete), and the if (acting) return guard serializes responds so the set holds ≤1 id — no unbounded growth. F1's error branch is reachable because load() synchronously setItems([]) before the .catch, so loadError && items.length===0 holds.
  • Robustness ✓loadSeqRef stale-response guard is preserved on all three .then/.catch/.finally arms. WS-race window (the original flicker report) is closed by the just-acted filter. POST is awaited so the cleanup always runs.
  • Security ✓ — F1 is the security-relevant fix: a backend outage no longer renders as a silent "No pending approvals" clean inbox, which on a destructive-approvals surface could let actions go unreviewed. No new input/auth/secret surface; responder identity path unchanged.
  • Performance ✓Set ops O(1), one O(n) filter per load. Negligible.
  • Readability ✓ — Excellent: each change is annotated to its audit finding (F1/F2/F3) with the race rationale; role="alert" + aria-label on the retry affordance.

Non-blocking notes (none gate this PR):

  1. F3 success-path narrow residual — on POST success the row is deleted from justActedRef immediately. If a WS-triggered load() resolves in the sl: window between that delete and server read-consistency (POST applied but a concurrently-issued GET captured the pre-apply list), the row could re-surface for one render before the next load drops it. Best-effort flicker mitigation is fine for this fix, but a more robust variant retains the id until a subsequent load() confirms the server no longer returns it (or a short TTL). Optional hardening.
  2. Refresh-failure visibility — the error banner is gated on items.length===0, so a background refresh failure while rows are already shown won't surface it. Moot today because load() clears items first, but if you later add a non-clearing background refresh, consider a lightweight inline error indicator. Optional.

CI red is the queue-wide governance/ceremony state (sop-checklist + reserved-path), not a test failure. Approving on merits; canvas-test-only, well-covered.

**5-axis review — APPROVE.** head `06c3ec6` (#2908 F1/F2/F3, 3 HIGH) - **Correctness ✓** — All three fixes are sound. F2 replaces the stale `setItems(prev)` restore with `load()` (re-fetch server truth), and correctly updates the `respond` deps from `[acting, items]` → `[acting, load]` since it now uses the functional `setItems((cur) => …)` form — this also kills a latent stale-closure bug. F3's `justActedRef` is filtered in `load().then` and cleaned on **every** code path (success `delete`, catch `delete`), and the `if (acting) return` guard serializes responds so the set holds ≤1 id — no unbounded growth. F1's error branch is reachable because `load()` synchronously `setItems([])` before the `.catch`, so `loadError && items.length===0` holds. - **Robustness ✓** — `loadSeqRef` stale-response guard is preserved on all three `.then/.catch/.finally` arms. WS-race window (the original flicker report) is closed by the just-acted filter. POST is awaited so the cleanup always runs. - **Security ✓** — F1 is the security-relevant fix: a backend outage no longer renders as a silent "No pending approvals" clean inbox, which on a destructive-approvals surface could let actions go unreviewed. No new input/auth/secret surface; responder identity path unchanged. - **Performance ✓** — `Set` ops O(1), one O(n) filter per load. Negligible. - **Readability ✓** — Excellent: each change is annotated to its audit finding (F1/F2/F3) with the race rationale; `role="alert"` + `aria-label` on the retry affordance. **Non-blocking notes (none gate this PR):** 1. **F3 success-path narrow residual** — on POST success the row is `delete`d from `justActedRef` immediately. If a WS-triggered `load()` resolves in the sl: window between that delete and server read-consistency (POST applied but a concurrently-issued GET captured the pre-apply list), the row could re-surface for one render before the next load drops it. Best-effort flicker mitigation is fine for this fix, but a more robust variant retains the id until a subsequent `load()` confirms the server no longer returns it (or a short TTL). Optional hardening. 2. **Refresh-failure visibility** — the error banner is gated on `items.length===0`, so a *background* refresh failure while rows are already shown won't surface it. Moot today because `load()` clears `items` first, but if you later add a non-clearing background refresh, consider a lightweight inline error indicator. Optional. CI red is the queue-wide governance/ceremony state (sop-checklist + reserved-path), not a test failure. Approving on merits; canvas-test-only, well-covered.
devops-engineer merged commit 552e019a76 into main 2026-06-15 05:49:11 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#2909