Skip to content

Open the usage share preview from desktop widgets - #3678

Open
Chipagosfinest wants to merge 41 commits into
steipete:mainfrom
Chipagosfinest:fix/widget-share-stats
Open

Chipagosfinest wants to merge 41 commits into
steipete:mainfrom
Chipagosfinest:fix/widget-share-stats

Conversation

@Chipagosfinest

@Chipagosfinest Chipagosfinest commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Desktop widgets currently have no visible path to the local Usage & Spend share image. This adds Share overview with the standard Share symbol to Usage, Compact, Switcher, and History widgets. Small widgets use the tile URL and an existing header/chip row; larger widgets use an explicit link. Provider-switch buttons remain available, with single-line compact labels and full accessibility names.

The data-free codexbar://share-stats?version=1 route opens the existing preview for the selected overview scope. Cold-launch delivery queues until the status controller is ready and coalesces duplicate requests. The URL carries no account, provider, spend, usage, callback, or file data. Unsupported parameters and destinations are rejected.

Depends on #3677. This is a separate stacked feature PR because GitHub cannot use a contributor fork branch as an upstream PR base. The prerequisite ancestry is tracked, so the concern-scoped comparison contains only widget changes. Remove the prerequisite diff after #3677 merges.

Validation:

  • Remote macOS proof passed at source e880687f9: three route/handoff tests and one native rendering XCTest, zero failures.
  • All nine Usage/Compact/Switcher/History size captures were visually inspected. The dense small fixture includes two quota rows, code-review usage, and three provider chips. It uses simulated 16-point desktop margins. The wrapping found in the first capture was corrected; ordinary compact truncation retains the full accessibility name.
  • Real package proof passed at source d223726ffd8d9d60c3eab54af55e730b7291669c: debug app contains the Xcode-built WidgetKit extension; bundle IDs, URL scheme, extension point, and strict deep signatures were verified. This run did not launch or install either bundle. Current-head full CI remains the merge gate.
  • Actual packaged warm/cold URL test passed: one named GUI test executed, zero skips. Both app launches exposed exactly one populated preview with synthetic Claude 110K tokens and $0.45 estimated spend. Both previews passed pixel checks before explicit app activation and their PNGs were inspected. A separate diagnostic found the desktop/clock controls for the next gallery step. No actual widget click was performed.
  • Actual installed small Switcher proof passed: the test added the widget through the real gallery, verified its shared synthetic 110K/$0.45 snapshot, and clicked it to open populated warm and cold app previews (one named test, zero skips). No direct URL invocation substituted for those clicks. This covers the new widget and retention across app restart, not an older-version upgrade.
  • Installed provider buttons and sharing passed together on macOS 15.7.9: actual Claude → Codex → Claude clicks produced populated → empty → populated states without opening Share; later body clicks opened the populated preview warm and cold. One named test passed, zero skips/failures. Group selection ended as Claude; root inspected the screenshots and diagnostics. The unchanged product package is d223726ffd8d9d60c3eab54af55e730b7291669c; subsequent PR commits add evidence only.
  • OS boundary: the same provider fixture fails on macOS 26.6.2 for both the candidate and unmodified v0.60.3 baseline. Packaged intent metadata matches. The macOS 15 pass proves compatible interactions on that OS; it does not diagnose the macOS 26 failure or establish an introduced regression.
  • Retained older-widget upgrade passed on macOS 15.7.9: install build146, replace the verified bundle at the same path with build147, retain the sole widget/data, observe the new share affordance, then open populated warm/cold previews through that retained widget. Both phases passed with zero skips. The widget was never re-added. This is an ad-hoc debug-package replacement with one Claude provider, not a Sparkle/signed-release update or customized-selection test.
  • Installed medium explicit-link proof passed on macOS 15.7.9: resize the installed Switcher through its public Medium menu, require medium dimensions, switch Claude → Codex → Claude, then click the explicit Share overview control to open populated warm/cold previews. One named test passed, zero skips/failures; no body click or direct URL substituted for the explicit control. Root inspected screenshots, AX, and logs.
  • macOS26 upgrade boundary: the same replacement protocol retained the old view without adopting the Share affordance within 90 seconds. This remains a compatibility/runner investigation; the macOS15 pass does not certify macOS26 behavior.
  • Remaining boundaries: installed Large/other families, dense unobstructed system layouts, VoiceOver, signed/Sparkle upgrades, and the documented macOS26 behavior. Current-head CI, maintainer review, and prerequisite Expose usage snapshot sharing from the menu overview #3677 remain merge gates. All native checks used disposable runners and synthetic data; the user's app and desktop were untouched.

Dense small Switcher with share affordance

All nine native captures and proof boundaries.

Small Usage/Compact headers use the relative-timestamp space for Share. Medium/large widgets retain labelled links. Burn Down remains outside this aggregate overview entry point.

References: Apple widget links, widget interactivity.

@clawsweeper

clawsweeper Bot commented Sep 16, 2026

Copy link
Copy Markdown

🦞👀
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.

@Chipagosfinest
Chipagosfinest marked this pull request as ready for review September 16, 2026 07:09
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-16T07:13:54.759954Z 45f3f6d Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45f3f6dc25

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if let balance = extraUsageBalanceLine(for: entry) {
balance
}
WidgetShareOverviewFooter(launchesSharePreview: false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve room for the footer in small widgets

When the small Switcher displays a normal Codex entry with two quota rows plus the code-review row, this footer pushes the nested content beyond the tile height after the provider-chip row and WidgetKit content margins are included, so the final usage row or the new label is compressed or clipped. The native proof only checks the outer hosting-view bounds without real WidgetKit margins, so it cannot detect this; omit the redundant footer for small tiles—which already use widgetURL—or reduce the small row budget, then verify the actual desktop widget.

AGENTS.md reference: AGENTS.md:L41-L41

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 16, 2026
@clawsweeper

clawsweeper Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codex review: blocked before merge. Reviewed September 17, 2026, 1:25 AM ET / 05:25 UTC (Revision 19).

ClawSweeper review

What this changes

Adds desktop-widget links to the local usage-share preview, queued cold-launch delivery, and the prerequisite menu-sharing changes with tests and native screenshots.

Merge readiness

Blocked before merge - 3 items remain

Keep open: current main lacks this widget entry point, and substantial native evidence supports retaining the contribution. No blocking code defect was found.

Priority: P3
Reviewed head: 805bf31a4d45a2c9ad38506ad9747c27e0eb1d2a
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused implementation with strong native interaction evidence and no supported blocking code finding.
Proof confidence 🦞 diamond lobster (5/6) ✨ media proof bonus Sufficient (linked_artifact): Installed WidgetKit small-body and medium-link clicks exercised the packaged app's URL handler and shared preview, producing populated warm/cold results; macOS 15 also preserved a retained widget across replacement. Inspected screenshots and documented native runs support this behavior, while macOS 26 adoption remains a separate compatibility decision.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (linked_artifact): Installed WidgetKit small-body and medium-link clicks exercised the packaged app's URL handler and shared preview, producing populated warm/cold results; macOS 15 also preserved a retained widget across replacement. Inspected screenshots and documented native runs support this behavior, while macOS 26 adoption remains a separate compatibility decision.
Evidence reviewed 9 items Applicable repository policy: Read the complete root AGENTS.md and found no nested AGENTS.md or maintainer-notes directory. Applied guidance on provider isolation, native widget evidence, scoped changes, and avoiding credential prompts. Builds and tests were not executed under the read-only review contract.
Current main and release still lack widget sharing: Current-main widget source has no Share link or widgetURL, while Settings already uses the existing share presenter. Inspection of v0.60.4 also found no widget share entry point. Reusing that presenter adds access without creating a competing export implementation.
Route and export boundary: The parser accepts only the overview destination and version query, rejecting credentials, ports, paths, fragments, and additional data. App delivery invokes the existing preview; pasteboard writes and file saving remain explicit preview actions.
Findings None None.
Security None None.

How this fits together

CodexBar widgets display usage snapshots published by the app. The new link asks the app to rebuild its selected usage overview and present a local preview; copying or saving still requires a separate action.

flowchart LR
  A[Desktop widget click] --> B[Validate data-free URL]
  B --> C[Queue until app is ready]
  C --> D[Build selected usage overview]
  D --> E[Local share preview]
  E --> F[User chooses copy or save]
Loading

Decision needed

Question Recommendation
Is the documented macOS 26 retained-widget adoption limitation acceptable for landing this feature? Accept the bounded adoption limitation: Allow landing with the documented limitation because existing widget data was preserved and no introduced provider-switch regression was established.

Why: The evidence proves working fresh-install interactions and a macOS 15 replacement, but cannot decide the acceptable release boundary for macOS 26 without maintainer intent.

Before merge

  • Resolve merge risk (P1) - Existing macOS 26 widgets may retain their old view after app replacement and not expose the new Share action promptly; the documented run preserved their data, and release-update adoption remains unresolved.
  • Complete next step (P2) - Obtain an explicit disposition of the macOS 26 adoption limitation, then land the prerequisite Expose usage snapshot sharing from the menu overview #3677 and refresh this branch.
  • Resolve maintainer decision - Resolve the maintainer decision shown above before merge.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test growth Sources +460/-52; tests and plist test +601/-13 The stacked diff includes justified routing, shared presentation, localization, and substantial native-proof coverage.
Native layout coverage 9 captured layouts; 2 installed Switcher sizes Synthetic captures cover all proposed layouts, while installed interaction evidence covers small and medium Switcher widgets.

Merge-risk options

Maintainer options:

  1. Accept delayed adoption on macOS 26 (recommended)
    Explicitly accept that retained widgets may continue showing their old interface after replacement while preserving their existing data.
  2. Pause for representative upgrade validation
    Keep the feature unmerged until macOS 26 release-upgrade evidence resolves whether retained widgets acquire the Share action.

Technical review

Best possible solution:

Keep one shared preview and filtered payload path, with an explicitly accepted or resolved macOS 26 retained-widget adoption boundary.

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

Not applicable as a bug reproduction: this adds a widget entry point. Supplied native runs demonstrate actual widget clicks opening populated previews.

Is this the best way to solve the issue?

Yes: reusing the existing filtered overview and preview is a narrow implementation, with a data-free route and separate user actions for export.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 1a57ef11cbe6.

Labels

Label justifications:

  • P3: This improves access to an existing local sharing workflow rather than repairing a blocked core workflow.
  • merge-risk: 🚨 compatibility: The supplied macOS 26 replacement run leaves adoption of the new action by retained widgets unresolved.
  • 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. Sufficient (linked_artifact): Installed WidgetKit small-body and medium-link clicks exercised the packaged app's URL handler and shared preview, producing populated warm/cold results; macOS 15 also preserved a retained widget across replacement. Inspected screenshots and documented native runs support this behavior, while macOS 26 adoption remains a separate compatibility decision.
  • proof: sufficient: Contributor real behavior proof is sufficient. Installed WidgetKit small-body and medium-link clicks exercised the packaged app's URL handler and shared preview, producing populated warm/cold results; macOS 15 also preserved a retained widget across replacement. Inspected screenshots and documented native runs support this behavior, while macOS 26 adoption remains a separate compatibility decision.

Evidence

What I checked:

  • Applicable repository policy: Read the complete root AGENTS.md and found no nested AGENTS.md or maintainer-notes directory. Applied guidance on provider isolation, native widget evidence, scoped changes, and avoiding credential prompts. Builds and tests were not executed under the read-only review contract. (AGENTS.md:1, 805bf31a4d45)
  • Current main and release still lack widget sharing: Current-main widget source has no Share link or widgetURL, while Settings already uses the existing share presenter. Inspection of v0.60.4 also found no widget share entry point. Reusing that presenter adds access without creating a competing export implementation. (Sources/CodexBarWidget/CodexBarWidgetViews.swift:9, 1a57ef11cbe6)
  • Route and export boundary: The parser accepts only the overview destination and version query, rejecting credentials, ports, paths, fragments, and additional data. App delivery invokes the existing preview; pasteboard writes and file saving remain explicit preview actions. (Sources/CodexBarCore/ShareStatsRoute.swift:13, 805bf31a4d45)
  • Native interaction and upgrade evidence: Read the complete proof document and inspected prepared screenshots plus installed-widget captures. They show populated Claude 110K/$0.45 previews and the retained widget's new affordance. Documented real widget clicks cover small-body and medium-link warm/cold launches, provider switching, and a macOS 15 retained-widget replacement. Product source is d223726; these observations are distinct from synthetic layout tests. (docs/research/widget-share-proof.md:67, 805bf31a4d45)
  • Native run identity: GitHub confirms success for the medium installed-widget run https://github.com/Chipagosfinest/CodexBar/actions/runs/35136329891 and retained-upgrade run https://github.com/Chipagosfinest/CodexBar/actions/runs/35135733524. Their workflow heads identify the external harness, not the packaged production revision. Semantic coverage comes from the inspected proof documentation and captures. (826deb28177b)
  • Remaining upgrade boundary: The macOS 26 replacement preserved the widget and data but did not adopt the new Share affordance within 90 seconds. Provider-button failures also occurred on the unmodified baseline, so they do not establish an introduced regression. Signed/Sparkle upgrades remain outside the supplied replacement proof. (docs/research/widget-share-proof.md:75, 805bf31a4d45)

Likely related people:

  • Peter Steinberger: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Chipagosfinest: 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.

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 (18 earlier review cycles; latest 8 shown)
  • reviewed 2026-09-16T16:11:47.217Z sha 72a8649 :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-16T16:58:17.429Z sha 5fd82cc :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-16T17:32:34.434Z sha 439abdd :: blocked before merge. :: none
  • reviewed 2026-09-16T18:05:53.301Z sha 439abdd :: blocked before merge. :: none
  • reviewed 2026-09-16T18:36:42.739Z sha e2ea0a1 :: blocked before merge. :: none
  • reviewed 2026-09-16T18:54:33.931Z sha db481d1 :: blocked before merge. :: none
  • reviewed 2026-09-17T03:44:06.513Z sha d67aabe :: blocked before merge. :: none
  • reviewed 2026-09-17T03:56:42.341Z sha d128a2d :: blocked before merge. :: none

@clawsweeper clawsweeper Bot added proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Sep 16, 2026
@clawsweeper clawsweeper Bot removed the proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. label Sep 16, 2026
@clawsweeper clawsweeper Bot added merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. proof: sufficient Contributor real behavior proof is sufficient. 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. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Sep 16, 2026
Localization catalogs conflicted because both sides appended new keys in the
same region; all 23 keep both sets. The gatekeeper allowlist entry main added
for PreferencesSpendDashboardPane.swift:519 is dropped: that construct moved
into ShareStatsPayload.swift on this branch and already has its own entry,
whose anchor shifts 361 -> 372 under the merged file.
…r task

A failed image copy left "Could not copy image" on the button and on its
accessibility label until the window was closed and reopened: only the success
path scheduled a reset, and the preceding cancel() had killed any pending one.
Both outcomes now share makeCopyFeedbackResetTask().

The presentation task's defer cleared whichever handle was stored rather than
its own, so a task resuming from its 50ms sleep after being superseded nil'd
its successor's handle and let a second presentation start concurrently. A
generation token means a task only clears the handle it installed.
…andoff

parse() short-circuited on an exact whole-string compare, so every component
check below it was unreachable and the 13-case rejection test proved nothing
about them. The comparisons run now, which also makes the case-insensitive
scheme and host tolerance real instead of advertised. The query is pinned as
raw percent-encoded text because URLComponents decodes queryItems, and
"version=%31" would otherwise read as "version=1".

A queued route had no expiry, so one that failed to deliver was retained
indefinitely and could pop an unrequested share window much later. It now
expires after 60 seconds: a queued route answers a click, and past that window
it no longer represents the user's intent.
… value

StatusItemController was already at exactly the 800-line type_body_length
ceiling, so adding the generation counter as a second stored property tipped it
to 801. Both now live in an OverviewSharePresentation value declared outside
the class body, which restores the count to 800 and keeps the two fields
together where their invariant lives.
@Chipagosfinest

Copy link
Copy Markdown
Contributor Author

ClawSweeper's remaining blocker on this one is a disposition only you can give, so here it is as a single question with the evidence attached.

Question: existing macOS 26 widgets may keep their previous view after the app is replaced, and so may not expose the new Share action until the widget is removed and re-added. Is that acceptable to ship?

What is demonstrated: the macOS 15 retained-widget upgrade preserves existing widget data and behavior, and installed small and medium Switcher interactions exercise the production URL, the app handoff, and the overview preview on both warm and cold launch.

What is not: prompt adoption of the new action by an already-installed macOS 26 widget across an app update.

For what it's worth, retained widgets not picking up changes until they are re-added is documented WidgetKit behavior rather than something specific to this change — Apple's own guidance for a changed widget configuration is to instruct people to remove and re-add it. Nothing here regresses an existing widget; the action is simply absent until the widget refreshes.

Happy to go either way:

Also worth noting, since the diff looks big: 13 of the 74 files are source. The rest are 23 localization catalogs, 33 proof artifacts, and 5 test files — and the source count drops further once #3677 lands, since this branch currently carries it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. proof: sufficient Contributor real behavior proof is sufficient. 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant