Skip to content

fix(android): retire resolved approval cards with their result - #140111

Merged
steipete merged 1 commit into
mainfrom
codex/android-approval-inbox-snapshot
Sep 6, 2026
Merged

steipete merged 1 commit into
mainfrom
codex/android-approval-inbox-snapshot

Conversation

@steipete

@steipete steipete commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Closes #140109. Related: #134939 and #139789.

What Problem This Solves

Fixes an issue where users reviewing Android command approvals could see “already denied” while the same request still showed enabled Allow/Deny controls and counted as pending. A controlled real-Compose/loopback reproduction captures this contradictory frame on unchanged production.

Why This Change Was Made

NodeRuntime previously published rows, refresh/error state, and terminal notices through four independent StateFlows. MainViewModel and Compose collected them separately, so a writer lock could not provide an atomic UI snapshot.

One internal immutable approval inbox now owns those facts. All six terminal-producing paths retire the matching card and publish its result together. Settings, Home, and Wear read that coherent state; the old parallel flows are removed. Conditional dismissal preserves replacement notices, and existing refresh generations, Gateway lifecycle/method checks, ambiguous-write recovery, expiry, and decision authorization remain intact.

User Impact

A resolved request disappears from the pending list when its outcome appears. Other pending requests remain actionable and the result stays visible until dismissed. No configuration, dependency, protocol, storage/schema, or permission change is introduced. This repairs the existing documented approval behavior rather than adding a new approval policy.

Evidence

  • The final old-production control fails the intended rendered-coherence assertion. The corresponding repaired test passes using real NodeRuntime/MainViewModel, Compose, and a synthetic loopback Gateway. Both release/join the paused publisher, verify settled retirement and Dismiss, and make no approval-resolution/execution/chat request.
  • 152 focused and sibling tests passed on the repaired source, including canonical/legacy outcomes, unknown-write recovery, stale dismissal, runtime/model lifecycle, Home counts, and Wear projection. After the small nullable-error rendering cleanup, the two affected UI suites passed again: 30 tests.
  • The complete 14-check changed-file gate passed on exact efdd37fed1a9dcca63b377ac54d456641b55ee0c. Its rebase onto 0a753eb92f6f687476654f68c12143b68ea804fa preserves all eight reviewed/tested afterimages byte-for-byte; current-main Android drift is four unrelated generated protocol method names.
  • Independent ownership review and Codex autoreview found no actionable P0–P2 issues. Production is +112/−100 (net +12); tests are +389/−93. The small production increase replaces parallel state surfaces with one owner boundary; most runtime-test churn is migration to the shared snapshot.

Before and after

These are synthetic Compose/Robolectric captures under controlled scheduling, inspected in full by the maintainer agent. They are not installed-device screenshots. Refreshing legitimately remains visible while the publisher is paused.

Before: denied notice beside its enabled approval card

After: denied notice with Pending 0 and no approval card

Validation and limits

Exact-head hosted CI 34034559425 passed, including Play/third-party/Wear tests, Play/Wear builds, ktlint, and the required CI gate. The installed API 36 Play/debug flow also passed: fresh install and manual TLS onboarding, Pending 1 with enabled controls, remote denial followed by canonical approval.get, Pending 0 with the matching notice and no approval card, Dismiss, and ordinary Disconnect. Installed APK bytes match reviewed efdd37fed1a9dcca63b377ac54d456641b55ee0c (SHA-256 8ce416b74867fd592efb874aaf01a16f7ab19e51a7f5fbdc3c6871f3a5a3a16a). No approval resolution/request or chat-send RPC occurred. Display and locale remained unchanged. Complete evidence was downloaded; native processes/ports closed and the Testbox was stopped. The regression measures controlled rendered inconsistency, not natural recurrence frequency, duplicate execution, or an authorization bypass. The first candidate witness stopped on Refresh readiness; that failed attempt is retained, and the final old/new controls use the same explicit rendered-ready condition without increasing its timeout.

Installed-app sequence

These unmodified full-frame captures are from the installed app using an isolated synthetic Gateway fixture, personally inspected by the maintainer agent. They show the ordinary fixed-app flow, not a controlled scheduling reproduction or a production Gateway authorization test.

Installed: pending approval with enabled controls

Installed: remote denial retires the card and shows its notice

Installed: Dismiss preserves the empty inbox

Review disposition

The latest ClawSweeper review reported no source/security findings and requested completion of the declared installed flow. That flow is now complete; its image-fetch limitation is addressed by direct full-frame inspection of both the existing controlled Compose captures and the installed sequence above. This is the maintainer agent’s evidence review, not a claim of separate human review. The reviewed owner-boundary design is unchanged. Two earlier large-input sync attempts failed before native execution; both leases were stopped. The successful run used ordinary remote builds and local-on-Testbox archive assembly without changing providers, timeout guards, or transport behavior.

Post-merge integration audit

The landed tree preserves the reviewed runtime changes and the unrelated provider-refresh work on main. The audit also caught two test-only references which moving main had added to the removed getters; #140247 repairs that clean-merge composition. Its compiler reproduction and 30-test validation are recorded there.

@steipete steipete self-assigned this Sep 6, 2026
@clawsweeper

clawsweeper Bot commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@openclaw-barnacle openclaw-barnacle Bot added app: android App: android size: L maintainer Maintainer-authored PR labels Sep 6, 2026
@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Sep 6, 2026
@clawsweeper

clawsweeper Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: blocked before merge. Reviewed September 6, 2026, 8:59 AM ET / 12:59 UTC.

ClawSweeper review

What this changes

Android approval results, pending cards, and counts now share one observable snapshot so resolved requests disappear when their outcome appears.

Merge readiness

⛔ Blocked before merge - 3 items remain

This remains a useful, focused fix: current main still permits inconsistent approval presentation, and no introduced correctness defect was found. The linked merged Android changes address different problems.

Priority: P2
Reviewed head: efdd37fed1a9dcca63b377ac54d456641b55ee0c

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused owner-level repair with meaningful rendered regression coverage and no actionable code finding.
Proof confidence 🌊 off-meta tidepool Not applicable: The maintainer-authored PR is exempt from the external-contributor proof gate, and this patch does not materially change authority. Supplied Compose/loopback evidence exercises the real runtime and ViewModel publication path; installed-app verification remains an explicit author-declared hold.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: The maintainer-authored PR is exempt from the external-contributor proof gate, and this patch does not materially change authority. Supplied Compose/loopback evidence exercises the real runtime and ViewModel publication path; installed-app verification remains an explicit author-declared hold.
Evidence reviewed 10 items Verified review boundary: The checkout is the pinned PR head; its raw parent matches the supplied introduction baseline. The introduced diff was inspected locally rather than treating endpoint drift as contributor changes.
Current main still needs the repair: Current main publishes the terminal notice before independently publishing the filtered approval rows. The writer lock does not make separate consumer StateFlows atomic.
Atomic publication at the runtime owner: Terminal retirement updates rows and notice together while retaining resolved-ID tracking, pending-write retirement, refresh invalidation, and conditional notice dismissal. The batch-refresh path uses the same snapshot contract.
Findings None None.
Security None None.

How this fits together

Android receives pending command approvals and terminal results from the Gateway. Its runtime supplies the approval screen, Home counts, and Wear summary while the existing decision path submits authorized responses.

flowchart TD
  A[Gateway approval events and responses] --> B[Lifecycle and refresh checks]
  B --> C[Android approval inbox snapshot]
  C --> D[Pending cards and result banner]
  C --> E[Home and Wear counts]
  D --> F[User approval decision]
  F --> G[Existing Gateway decision checks]
Loading

Before merge

  • Resolve merge risk (P1) - The PR explicitly leaves the ordinary installed-app pending → remote-denied → notice/Dismiss flow unverified.
  • Resolve merge risk (P1) - Linked screenshot pixels could not be independently inspected in this review environment.
  • Complete next step (P2) - Complete the installed-app pending → remote-denied → notice/Dismiss validation declared pending in the PR body and record the redacted result.
Agent review details

Security

None.

PR surface

Tests +296, Other +12. Total +308 across 8 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 3 389 93 +296
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 5 112 100 +12
Total 8 501 193 +308

Review metrics

Metric Value Why it matters
Approval publication boundary 4 independent flows replaced by 1 snapshot Consumers can no longer combine a terminal result with an older pending-card list.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #140109
Summary: This PR is the explicit candidate repair for the open approval-presentation issue; the other linked Android PRs address distinct behavior.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge-risk options

Maintainer options:

  1. Decide the mitigation before merge
    Keep approval rows and terminal outcomes under one runtime-owned snapshot while preserving the existing lifecycle, recovery, and decision checks.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Keep approval rows and terminal outcomes under one runtime-owned snapshot while preserving the existing lifecycle, recovery, and decision checks.

Do we have a high-confidence way to reproduce the issue?

Yes: current main exposes the separate notice/row publication sequence, and the supplied controlled Compose reproduction targets that exact boundary. This review did not execute it.

Is this the best way to solve the issue?

Yes: consolidating publication at the runtime owner removes the inconsistency across consumers without adding UI timing workarounds or changing approval policy.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 4418cdcc5875.

Labels

Label changes:

  • add P2: This repairs contradictory Android approval presentation with a limited blast radius; no execution or authorization bypass is established.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The maintainer-authored PR is exempt from the external-contributor proof gate, and this patch does not materially change authority. Supplied Compose/loopback evidence exercises the real runtime and ViewModel publication path; installed-app verification remains an explicit author-declared hold.

Label justifications:

  • P2: This repairs contradictory Android approval presentation with a limited blast radius; no execution or authorization bypass is established.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The maintainer-authored PR is exempt from the external-contributor proof gate, and this patch does not materially change authority. Supplied Compose/loopback evidence exercises the real runtime and ViewModel publication path; installed-app verification remains an explicit author-declared hold.

Evidence

What I checked:

  • Verified review boundary: The checkout is the pinned PR head; its raw parent matches the supplied introduction baseline. The introduced diff was inspected locally rather than treating endpoint drift as contributor changes. (efdd37fed1a9)
  • Current main still needs the repair: Current main publishes the terminal notice before independently publishing the filtered approval rows. The writer lock does not make separate consumer StateFlows atomic. (apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt:8522, 4418cdcc5875)
  • Atomic publication at the runtime owner: Terminal retirement updates rows and notice together while retaining resolved-ID tracking, pending-write retirement, refresh invalidation, and conditional notice dismissal. The batch-refresh path uses the same snapshot contract. (apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt:8502, efdd37fed1a9)
  • Consumers retain a coherent snapshot: Settings collects one inbox value and derives its pending count, banner, and cards from it. MainViewModel adapts that single flow; Home and Wear also read the consolidated state. (apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsScreens.kt:595, efdd37fed1a9)
  • Existing approval contract: The Android documentation defines shared approval state, canonical first-answer results, and locked actions during uncertain outcomes. This patch repairs presentation of that existing contract without adding an approval policy. Public docs: docs/platforms/android.md. (docs/platforms/android.md:389, efdd37fed1a9)
  • Rendered regression and supplied proof: The new test exercises real NodeRuntime/MainViewModel/Compose through a loopback WebSocket fixture, pauses publication through a subscriber, checks that a terminal notice does not coexist with its actionable card, releases and joins the observer, and verifies Dismiss. The captured PR body reports the corresponding old-production failure and repaired pass, while explicitly leaving installed-app verification pending. No tests were executed during this review. (apps/android/app/src/test/java/ai/openclaw/app/ui/SettingsScreensContrastTest.kt:267, efdd37fed1a9)

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Vincent Koc: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Complete the installed-app approval retirement and Dismiss validation already identified as pending in the PR body.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@steipete
steipete merged commit 149433b into main Sep 6, 2026
83 checks passed
@steipete
steipete deleted the codex/android-approval-inbox-snapshot branch September 6, 2026 15:37
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android maintainer Maintainer-authored PR P2 Normal backlog priority with limited blast radius. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: L status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Android approvals can show a denied result beside an actionable card

1 participant