Skip to content

fix(macos): preserve the Talk overlay through quick toggles - #135731

Merged
steipete merged 4 commits into
mainfrom
codex/voice-overlay-dismissal-20260901
Sep 2, 2026
Merged

steipete merged 4 commits into
mainfrom
codex/voice-overlay-dismissal-20260901

Conversation

@steipete

@steipete steipete commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Related: #132565 (macOS Talk overlay dismissal race). Closes #127645 (Talk panel disappears after rapid toggling).

What Problem This Solves

The macOS Talk panel can disappear when Talk is re-enabled during its previous fade-out: the old dismissal completion hides the newly presented panel.

Why This Change Was Made

The overlay controller now records desired visibility immediately and gives each presentation or dismissal a new token. A dismissal completion hides the panel only while it still owns that token. The existing animation factory remains unchanged, and the redundant nested task is removed. No new helper, production test hook, dependency, configuration, protocol, or storage surface is added.

This is a maintainer rewrite of Huzaifa Iftikhar's (@HuzaifaChaudary) work in #132565 and the selected canonical replacement for that overlapping proposal. The original will be closed only after this repair is verified on main. The controller repair is production +10/-9 (net +1), compared with the original PR's +35 production lines. A 27-line unit regression is supplemented by a 73-line native fixture and 40-line opt-in runner.

User Impact

Quick off/on transitions leave the Talk panel visible. Ordinary dismissal still hides it.

Evidence

A Developer ID-signed isolated AppKit application ran the actual controller and panel factory with an inert synthetic view. Present → dismiss → present failed on the baseline at 0, 40, and 120 ms, then passed at every timing with this repair. Normal dismissal also passed. These actual animation checks remain separate from the checked-in synchronous regression.

The final Swift Testing regression checks immediate desired visibility through present → dismiss → present → dismiss. Against the original controller it fails both immediate-dismiss assertions; against the repaired controller it passes. The local proof uses the native SwiftPM backend, Swift 6 language mode, and the actual overlay and waveform views, with inert application-state/control dependencies. It ran on Swift 6.4/Testing 2077; a local Swift 6.3.3 build could not import the missing Testing module, so no local Swift 6.3 runtime proof is claimed. Scoped SwiftFormat 0.63.0, SwiftLint 0.65.1, and whitespace checks pass.

CI history matters here: the first full run failed the original 400 ms test on post-animation alpha/visibility assertions. The next completion-wait version reached the job's 20-minute timeout; TalkOverlayTests and three unrelated suites remained unfinished. The exact hang cause was not reproduced locally, including with the native backend and actual views. The final test does not await an AppKit animation completion or claim to prove final rendered state; the separate signed AppKit proof covers that behavior. CI run 33582520467 passed on the controller-and-unit-test head 27ae3f1a016d73b8882cdc6df0768e1aff6ddf4b, including macOS Swift tests, the release build, all three macOS Node jobs, and the final CI gate.

This is not a full operator OpenClaw session or microphone test. Both complete before/after captures were inspected and contain only synthetic data. Both attached images are publicly accessible: downloaded PNG bytes match the inspected originals exactly. No alternate image host was used.

Adjacent Notify and Voice Wake overlay completions need separate reproduction; this PR does not claim to repair them.

Rerunnable native regression

The late review correctly identified that the synchronous unit test does not execute the stale completion. The opt-in fixture in apps/macos/Tests/Fixtures/TalkOverlay/ now compiles the actual controller and factory into a separately signed synthetic AppKit app and runs the normal application event loop. It verifies its Developer ID Application signature before launch, reports source and binary hashes, and removes its temporary app. It does not load operator app state, a Gateway, or microphone services.

bash apps/macos/Tests/Fixtures/TalkOverlay/run.sh '<Developer ID Application identity or certificate SHA>'

Removing only the dismissal-token guard makes the same fixture fail the real panel-visibility assertion at all three reopen offsets (0, 40, and 120 ms), while ordinary dismissal still passes. The current controller passes all three reopen and all three ordinary-dismissal checks, including visible alpha for each reopened panel. This is timed end-to-end proof using 240/450/250 ms observation intervals, not deterministic completion control or a formal callback acknowledgment. The fixture is opt-in and separate from automatic Swift Testing. CI run 33593368734 passed on final head 58b669e60f91ada1955aa3a328c68a3b3605febf, including macOS Swift tests and the release build. CI does not automatically execute the opt-in fixture.

For the review's rank-up request, the real event-loop fixture supplies durable coverage of the asynchronous outcome. Deterministic unit completion control is intentionally not added: tracing disproved the proposed animation-disable/task-barrier ordering, and exposing a new production hook solely for this test would weaken the implementation. No workaround, swizzle, timeout increase, or production change is included.

The retained signed run occurred before committing 58b669e60f91ada1955aa3a328c68a3b3605febf; all four input hashes were rechecked after the commit and match its files exactly. It is not a claim that the process was launched after that commit. The runtime log below omits only the temporary app path and signing-status line:

e0cfa5da037ee0194e4dd537a19fcda95e6a00b107f662624342a2a048fc8398  apps/macos/Sources/OpenClaw/TalkOverlay.swift
5c8bc4d76fdfadb469cd8a66164217df7b6f73e0c93c894289732a98b25ac85e  apps/macos/Sources/OpenClaw/OverlayPanelFactory.swift
36569c41a9ce519f195ef2653046ffe5b05bb8d33f13155942e2eba06468f06b  apps/macos/Tests/Fixtures/TalkOverlay/Fixture.swift
3c2c8637738503d26ff409f83d3c136e62192042be1475ca6fd1c9a9d05e17f1  apps/macos/Tests/Fixtures/TalkOverlay/run.sh
fixture_binary_sha256=85965bfb1fca0dd7a73e33f6cbccfc04a8289582d5589e42354210ae52e2cf1d
reopen delay_ms=0 model=true panel=true alpha=1.0 pass=true
dismiss model=false panel=false pass=true
reopen delay_ms=40 model=true panel=true alpha=1.0 pass=true
dismiss model=false panel=false pass=true
reopen delay_ms=120 model=true panel=true alpha=1.0 pass=true
dismiss model=false panel=false pass=true
RESULT failures=0

The parent runner exited 0. The preceding negative control, with only the dismissal-token guard removed, exited 1 and reported RESULT failures=3: all reopened panels were hidden, while all ordinary dismissals passed. That control's controller hash was 8b469e80fe828f59bb7e4047e01002ec754e2541d1e771470f09dabb9e956728 and its signed binary hash was 4aec244aaa9bda7b9c35792b74a7cff1ba875120a5412122291d69a2dc0cca89; the other three source hashes were unchanged.

Before/after pictures

Retained captures of the actual Talk overlay controller and unchanged animation factory in an isolated AppKit fixture with a synthetic view. After present → dismiss → present, the original owner hides the reopened panel; the repaired owner leaves it visible.

Before After
Before: the reopened synthetic Talk overlay is hidden After: the reopened synthetic Talk overlay remains visible

These pictures cover the owner and animation behavior. They do not establish full-app, microphone, current PR-head, or merged-main execution; immutable identities of the captured binaries were not retained.

Record desired visibility at the overlay owner and fence animation completion
with the current presentation token. Remove the redundant nested task.

Rewrites #132565; fixes #127645. Actual signed AppKit before/after proof and
Swift Testing regression cover re-presentation and ordinary dismissal.

Co-authored-by: Huzaifa Iftikhar <[email protected]>
@clawsweeper

clawsweeper Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

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

@openclaw-barnacle openclaw-barnacle Bot added app: macos App: macos size: XS maintainer Maintainer-authored PR labels Sep 2, 2026
@steipete
steipete marked this pull request as ready for review September 2, 2026 00:49
@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 2, 2026
@clawsweeper

clawsweeper Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed September 2, 2026, 2:28 AM ET / 06:28 UTC.

ClawSweeper review

What this changes

This PR prevents an older Talk-overlay dismissal animation from hiding a newly presented panel and adds synchronous plus opt-in AppKit regression coverage.

Regression provenance

Possible regression — probable (reproduction; reviewed change). No predecessor PR is attributed.

Merge readiness

⚠️ Ready for maintainer review - 3 items remain

Keep open for the explicit macOS maintainer decision: current main still contains the stale completion path, while this member-authored, maintainer-labeled PR supplies the owner-side repair with no blocking code finding.

Priority: P2
Reviewed head: 58b669e60f91ada1955aa3a328c68a3b3605febf
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused owner-boundary fix with strong native evidence, green exact-head CI, and no blocking correctness or security finding.
Proof confidence 🦞 diamond lobster (5/6) Not applicable: The contributor-proof gate does not apply to this member-authored PR; independently, the retained native AppKit trace maps the changed controller and real panel factory to an event-loop exercise with passing reopened-panel and ordinary-dismiss results.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: The contributor-proof gate does not apply to this member-authored PR; independently, the retained native AppKit trace maps the changed controller and real panel factory to an event-loop exercise with passing reopened-panel and ordinary-dismiss results.
Evidence reviewed 6 items Current main retains the reported race: Fetched main still derives presentation state from model.isVisible and leaves it true until the unguarded dismissal completion orders the panel out and clears visibility.
The introduced repair owns the async boundary: The PR rotates transition ownership on every present and dismiss, sets desired visibility before the fade, and only orders out when the completing dismissal still owns the token.
Entry point and animation contract: The Talk setting asynchronously calls this controller’s present/dismiss methods, while the shared factory completes the 160 ms dismissal later on the main actor; this makes the reported off/on ordering reachable.
Findings None None.
Security None None.

How this fits together

The macOS Talk controller presents and dismisses a floating AppKit panel when Talk mode changes. Its animation completion determines whether a quick re-enable leaves the operator-visible panel on screen or orders it out.

flowchart LR
  A[Talk mode change] --> B[Talk overlay controller]
  B --> C[Presentation or dismissal token]
  C --> D[AppKit panel animation]
  D --> E{Token still current?}
  E -->|yes| F[Hide panel]
  E -->|no| G[Keep newer panel visible]
Loading

Decision needed

Question Recommendation
Should a macOS owner accept and merge this focused member-authored repair now that the exact head has green CI and targeted native evidence? Accept the repair: Merge the owner-side transition fence, which fixes the current-main stale completion without changing the shared animation factory.

Why: The maintainer label and member authorship require explicit maintainer handling even though the source review found no mechanical blocker.

Before merge

  • Resolve merge risk (P1) - Production code is net +1 line: the transition token is justified as the minimal owner-boundary state for fencing a stale AppKit completion, but it should remain limited to that responsibility.
  • Complete next step (P2) - A macOS owner should make the final maintainer merge decision.
Agent review details

Security

None.

PR surface

Other +141. Total +141 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 4 150 9 +141
Total 4 150 9 +141

Review metrics

Metric Value Why it matters
Production versus test delta production +1 net; tests/test support +140 The only production growth is the token that fences stale asynchronous ownership; the remaining additions are isolated regression coverage.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #127645
Summary: This PR is the evidenced candidate repair for the canonical Talk-overlay dismissal race; the older overlapping PR is superseded by this narrower maintainer rewrite.

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
    Adopt the Talk-controller transition fence and retain its AppKit fixture; leave Voice Wake unchanged because its session-token lifecycle has different side effects.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Adopt the Talk-controller transition fence and retain its AppKit fixture; leave Voice Wake unchanged because its session-token lifecycle has different side effects.

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

Yes—current source exposes the off/on ordering, and the submitted AppKit event-loop fixture records the stale-completion scenario at three timing offsets. This read-only Linux review did not rerun the macOS fixture.

Is this the best way to solve the issue?

Yes. The controller owns both transitions, so invalidating an obsolete dismissal completion there is narrower than altering the shared animation factory; Quick Chat uses the same ownership principle, while Voice Wake has distinct session cleanup.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 728c9c579fe8.

Labels

Label changes:

  • remove proof: sufficient: Current real behavior proof status is not_applicable, not sufficient.

Label justifications:

  • P2: This fixes a bounded macOS operator-visible overlay regression without evidence of wider availability or data impact.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster 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 contributor-proof gate does not apply to this member-authored PR; independently, the retained native AppKit trace maps the changed controller and real panel factory to an event-loop exercise with passing reopened-panel and ordinary-dismiss results.

Evidence

What I checked:

  • Current main retains the reported race: Fetched main still derives presentation state from model.isVisible and leaves it true until the unguarded dismissal completion orders the panel out and clears visibility. (apps/macos/Sources/OpenClaw/TalkOverlay.swift:50, 728c9c579fe8)
  • The introduced repair owns the async boundary: The PR rotates transition ownership on every present and dismiss, sets desired visibility before the fade, and only orders out when the completing dismissal still owns the token. (apps/macos/Sources/OpenClaw/TalkOverlay.swift:47, 58b669e60f91)
  • Entry point and animation contract: The Talk setting asynchronously calls this controller’s present/dismiss methods, while the shared factory completes the 160 ms dismissal later on the main actor; this makes the reported off/on ordering reachable. (apps/macos/Sources/OpenClaw/TalkModeController.swift:25, 58b669e60f91)
  • Regression coverage reaches the stale completion: The opt-in fixture runs the real controller and panel factory under an AppKit event loop, checks reopened panel visibility and alpha at 0/40/120 ms offsets, then verifies ordinary dismissal; the checked-in synchronous test separately protects immediate visibility intent. (apps/macos/Tests/Fixtures/TalkOverlay/Fixture.swift:22, 58b669e60f91)
  • Submitted native trace matches final inputs: The four SHA-256 values printed in the PR’s retained AppKit run exactly match the final controller, factory, fixture, and runner bytes; the trace records all reopen and ordinary-dismiss checks passing. (apps/macos/Tests/Fixtures/TalkOverlay/run.sh:25, 58b669e60f91)
  • Relevant history: The functional repair was introduced on this branch by the first PR commit; older current-main file history also shows sustained macOS maintenance by the same contributor, though the partial clone could not verify a pre-PR line-introduction coordinate. (apps/macos/Sources/OpenClaw/TalkOverlay.swift:27, ff8b5691d714)

Likely related people:

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

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.

History

Review history (8 earlier review cycles)
  • reviewed 2026-09-02T00:56:35.829Z sha ff8b569 :: needs maintainer review before merge. :: none
  • reviewed 2026-09-02T01:39:42.847Z sha 0676ab9 :: needs maintainer review before merge. :: none
  • reviewed 2026-09-02T02:23:08.180Z sha 27ae3f1 :: needs maintainer review before merge. :: none
  • reviewed 2026-09-02T03:54:48.874Z sha 27ae3f1 :: needs maintainer review before merge. :: none
  • reviewed 2026-09-02T04:23:37.384Z sha 27ae3f1 :: found issues before merge. :: [P2] Exercise the stale dismissal completion
  • reviewed 2026-09-02T04:39:12.991Z sha 27ae3f1 :: found issues before merge. :: [P2] Exercise the stale dismissal completion
  • reviewed 2026-09-02T05:14:11.329Z sha 58b669e :: needs maintainer review before merge. :: none
  • reviewed 2026-09-02T05:28:35.184Z sha 58b669e :: needs maintainer review before merge. :: none

Keep immediate visibility intent in the native test suite and retain separate signed AppKit evidence for animation completion. Avoid an unbounded animation wait in the CLI test runner.
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Sep 2, 2026
@clawsweeper clawsweeper Bot removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Sep 2, 2026
@clawsweeper clawsweeper Bot added 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. proof: sufficient ClawSweeper judged the real behavior proof convincing. and removed merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. proof: sufficient ClawSweeper judged the real behavior proof convincing. labels Sep 2, 2026
@steipete
steipete merged commit 1bf5fb6 into main Sep 2, 2026
91 checks passed
@steipete
steipete deleted the codex/voice-overlay-dismissal-20260901 branch September 2, 2026 06:35
@steipete

steipete commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Sep 3, 2026
…#135731)

* fix(macos): preserve Talk panel after interrupted dismissal

Record desired visibility at the overlay owner and fence animation completion
with the current presentation token. Remove the redundant nested task.

Rewrites openclaw#132565; fixes openclaw#127645. Actual signed AppKit before/after proof and
Swift Testing regression cover re-presentation and ordinary dismissal.

Co-authored-by: Huzaifa Iftikhar <[email protected]>

* test(macos): await Talk overlay animation completion

* test(macos): check Talk visibility intent synchronously

Keep immediate visibility intent in the native test suite and retain separate signed AppKit evidence for animation completion. Avoid an unbounded animation wait in the CLI test runner.

* test(macos): retain native Talk overlay race proof

---------

Co-authored-by: Huzaifa Iftikhar <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: macos App: macos 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: S 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.

A stale macOS Talk dismissal animation can hide a newer presentation

1 participant