Skip to content

Arbitrate concurrent macOS Now Playing sessions - #1

Open
nonnil wants to merge 20 commits into
mainfrom
cc/now-playing-arbitration
Open

Arbitrate concurrent macOS Now Playing sessions#1
nonnil wants to merge 20 commits into
mainfrom
cc/now-playing-arbitration

Conversation

@nonnil

@nonnil nonnil commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

  • enumerate every current macOS Now Playing client through the existing Apple-signed /usr/bin/perl boundary
  • select only playing sessions by OS-owned lastPlayingDate, then the OS-elected player path, then a stable identifier
  • publish an explicit stopped snapshot when no session remains, allowing lighting to clear immediately, and fall back to another playing session when the selected one stops
  • document the arbitration policy and retain the existing mixed-system-audio behavior

Why

The previous adapter exposed only macOS's single elected Now Playing item. That made concurrent Spotify, Music, browser, or other playback sessions invisible to the application, so it could not deterministically move to another active source after the selected source stopped.

This change derives priority from each refresh of macOS-owned session state and does not persist a separate application playback-order history.

Validation

  • cargo fmt --all -- --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --all-targets --all-features
  • cargo build --release
  • Rust 1.88 locked test, Clippy, and release-build equivalents
  • embedded arm64 helper ad-hoc signature verification
  • standalone x86_64 helper compile, ad-hoc signing, and architecture verification
  • live /usr/bin/perl helper startup against the current macOS MediaRemote service (no active playback sessions at probe time)

Summary by CodeRabbit

  • New Features

    • Added embedded macOS media-session handling with automatic active-session selection.
    • Improved playback updates, metadata retention, artwork caching, and session switching.
    • Added support for arm64 and x86_64 macOS builds.
  • Bug Fixes

    • Improved handling of incomplete updates, stopped playback, unavailable services, and helper failures.
    • Added deterministic selection for multiple active sessions.
    • Added safeguards for oversized media payloads and artwork.
  • Documentation

    • Updated media-session behavior documentation and removed the Perl requirement.
  • Chores

    • Added automated formatting, linting, testing, and release-build checks.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The project replaces the Perl and Rust media-remote adapter with an embedded, signed macOS MediaRemote helper. Rust now arbitrates session payloads, manages artwork and playback state, and cleans up the helper process. CI builds and validates both macOS architectures.

Changes

Embedded macOS media session adapter

Layer / File(s) Summary
Helper build and launch
Cargo.toml, build.rs, src/media.rs, src/main.rs
The build compiles and ad-hoc signs the Objective-C helper. Rust extracts and launches it, validates readiness, and manages process cleanup. The probe message now refers to the helper.
MediaRemote session stream
src/media_sessions.m
The helper dynamically loads MediaRemote, discovers players, collects bounded session data and artwork, suppresses duplicate results, handles refresh timeouts, and emits refreshed JSON payloads.
Rust arbitration and source lifecycle
src/media.rs
Rust filters and orders playing sessions, builds snapshots, projects playback position, caches artwork, publishes stopped states, validates helper input, and directly terminates helper resources. Tests cover selection, deserialization, state transitions, artwork reuse, limits, and cleanup.
Documented arbitration contract
README.md
The README describes embedded session collection, deterministic arbitration, incomplete snapshot retention, session clearing, input limits, and reactive lighting based on the selected session.
Build and helper validation
.github/workflows/ci.yml
CI runs formatting, Clippy, tests, helper smoke tests, and locked release builds with architecture and code-signature validation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CargoBuild
  participant MediaRemoteSource
  participant EmbeddedHelper
  participant MediaRemote
  participant TrackSnapshot
  CargoBuild->>EmbeddedHelper: compile and ad-hoc sign
  MediaRemoteSource->>EmbeddedHelper: launch and read JSON lines
  EmbeddedHelper->>MediaRemote: discover players and request session data
  MediaRemote-->>EmbeddedHelper: return session metadata and playback state
  EmbeddedHelper-->>MediaRemoteSource: emit session payloads
  MediaRemoteSource->>TrackSnapshot: select candidate and update playback state
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: arbitration of concurrent macOS Now Playing sessions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cc/now-playing-arbitration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

nonnil commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

Stop-switch follow-up pushed in f5388ee.

Root cause: some MediaRemote sessions retain playbackRate=1 after pausing. When the player-scoped isPlaying request had not resolved before the helper timeout, that stale metadata could keep the stopped session selected.

The adapter now marks player-scoped playback state as resolved explicitly, runs those requests on a dedicated serial queue, and excludes unresolved/error states instead of reviving the stale rate. Added a regression proving that an unresolved newer session falls back to another resolved playing session.

Validation: Rust 1.88 locked tests, Clippy with warnings denied, release build, arm64 helper signature verification, and x86_64 helper build/signature/architecture verification all pass.

@nonnil
nonnil marked this pull request as ready for review August 11, 2026 12:46
@nonnil

nonnil commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/media.rs (1)

2-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Gate the shared items for non-macOS builds.

Clippy reports POSITION_EDGE_TOLERANCE_SECONDS, PlaybackCandidate, and select_playback_candidate as unused. mod platform is the only non-test consumer, and it is macOS-only, so a non-macOS check build warns on all three items. If CI runs Clippy with -D warnings on any non-macOS target, the build fails.

Add a cfg gate that keeps the items available for the macOS build and for tests.

♻️ Proposed change
+#[cfg(any(target_os = "macos", test))]
 const POSITION_EDGE_TOLERANCE_SECONDS: f64 = 2.0;

+#[cfg(any(target_os = "macos", test))]
 #[derive(Clone, Debug, Deserialize)]
 #[serde(rename_all = "camelCase")]
 struct PlaybackCandidate {
+#[cfg(any(target_os = "macos", test))]
 fn select_playback_candidate(candidates: &[PlaybackCandidate]) -> Option<&PlaybackCandidate> {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/media.rs` around lines 2 - 48, Add a non-macOS exclusion gate to
POSITION_EDGE_TOLERANCE_SECONDS, PlaybackCandidate, and
select_playback_candidate so they compile only for macOS or tests. Ensure the
existing macOS behavior and test availability remain unchanged.

Source: Linters/SAST tools

README.md (1)

30-30: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Record the unresolved-session rule.

The paragraph states that only sessions that macOS reports as playing are considered. The adapter applies a stricter rule. select_playback_candidate in src/media.rs requires both playing and playing_resolved. The helper in src/media_sessions.m leaves playingResolved=NO when the per-player playback request does not answer within the refresh timeout, so such a session is excluded even when it is playing.

Add one sentence that states that a session with an unresolved playback state is excluded.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 30, Update the README paragraph describing eligible Now
Playing sessions to add that sessions with an unresolved playback state are
excluded, matching the `select_playback_candidate` requirement for both
`playing` and `playing_resolved`.
src/media_sessions.pl (1)

13-14: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the XSUB calling-convention assumption.

dl_install_xsub registers the symbol as an XSUB. Perl calls it with the XS signature void (*)(pTHX_ CV*), but chroma_media_sessions_stream is declared as void (void) in src/media_sessions.m. The call works only because the extra arguments are ignored on arm64 and x86_64, and because the function never returns a value. Add a short comment that records this contract. A future change of the helper signature to return a value or to read arguments will break silently.

📝 Proposed comment
+# The helper is installed as an XSUB. Perl invokes it with the XS signature
+# void (*)(pTHX_ CV *), so chroma_media_sessions_stream must stay a
+# no-argument, no-return-value function that blocks on its own run loop.
 DynaLoader::dl_install_xsub("main::chroma_media_sessions_stream", $symbol);
 chroma_media_sessions_stream();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/media_sessions.pl` around lines 13 - 14, Add a concise comment
immediately before the dl_install_xsub registration or
chroma_media_sessions_stream call documenting that Perl invokes the XSUB with
pTHX and CV* arguments while the helper is void(void), and that correctness
relies on ignoring those arguments and returning no value. Keep the existing
registration and invocation unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/media_sessions.m`:
- Around line 32-40: Update integerProperty to invoke objc_msgSend with an int
return type, then explicitly widen that int to the function’s long result.
Preserve the existing selector-presence handling and ensure the
processIdentifier value used to build stableID remains consistent across
refreshes.
- Around line 125-128: Fix ARC ownership for both dynamic alloc/init calls in
the media-session path, including the objc_msgSend invocation using
initWithOrigin:client:player:. Expose these calls through suitable Objective-C
declarations or accurately annotated function types so ARC models the retained
initializer result and consumed self; do not use
CFBridgingRetain/CFBridgingRelease as a workaround.

In `@src/media.rs`:
- Around line 353-362: Update the snapshot handling in the surrounding run flow
to call ArtworkDelivery::should_deliver before cloning received.snapshot. When
delivery is allowed, clone the snapshot with its artwork; otherwise use
TrackSnapshot::clone_without_artwork so the decoded DynamicImage is not copied.
Preserve elapsed_time and track_key handling while avoiding artwork cloning in
the common rejection path.

---

Nitpick comments:
In `@README.md`:
- Line 30: Update the README paragraph describing eligible Now Playing sessions
to add that sessions with an unresolved playback state are excluded, matching
the `select_playback_candidate` requirement for both `playing` and
`playing_resolved`.

In `@src/media_sessions.pl`:
- Around line 13-14: Add a concise comment immediately before the
dl_install_xsub registration or chroma_media_sessions_stream call documenting
that Perl invokes the XSUB with pTHX and CV* arguments while the helper is
void(void), and that correctness relies on ignoring those arguments and
returning no value. Keep the existing registration and invocation unchanged.

In `@src/media.rs`:
- Around line 2-48: Add a non-macOS exclusion gate to
POSITION_EDGE_TOLERANCE_SECONDS, PlaybackCandidate, and
select_playback_candidate so they compile only for macOS or tests. Ensure the
existing macOS behavior and test availability remain unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5a114391-00e8-418e-ba52-1139ec659d19

📥 Commits

Reviewing files that changed from the base of the PR and between 321129c and f5388ee.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • Cargo.toml
  • README.md
  • build.rs
  • src/media.rs
  • src/media_sessions.m
  • src/media_sessions.pl

Comment thread src/media_sessions.m
Comment thread src/media_sessions.m Outdated
Comment thread src/media.rs Outdated

@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: f5388ee432

ℹ️ 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".

Comment thread src/media_sessions.m Outdated
Comment thread src/media.rs
Comment thread src/media_sessions.m Outdated
Comment thread src/media_sessions.m Outdated
Comment thread src/media.rs Outdated
@nonnil

nonnil commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

Review trace for https://github.com/hauntedfail/Codex-Micro-Chroma/pull/1#pullrequestreview-4906401280\n\nClassification: must_fix; fixed in 92134aa. I verified and addressed all three inline findings plus all three review-body nitpicks: non-macOS/test cfg gates, the unresolved-session/incomplete-refresh README contract, and the Perl XSUB calling-convention comment. Validation: cargo fmt --all --check; cargo clippy --all-targets --all-features -- -D warnings; cargo test --all-targets --all-features (51 passed); cargo build --all-targets --all-features; git diff --check.

@nonnil

nonnil commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

Review trace for https://github.com/hauntedfail/Codex-Micro-Chroma/pull/1#pullrequestreview-4906428549\n\nClassification: must_fix; fixed in 92134aa. I verified and addressed all five child findings: partial refresh publication, pause/resume identity, playback-query error handling, repeated artwork encoding, and helper EOF/read-error propagation. Validation: cargo fmt --all --check; cargo clippy --all-targets --all-features -- -D warnings; cargo test --all-targets --all-features (51 passed); cargo build --all-targets --all-features; git diff --check.

@nonnil

nonnil commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

Please re-review the current head 92134aa. All previously reported items were addressed, replied to inline, and resolved; the full local verification suite passes.

@nonnil

nonnil commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please re-review the current head 92134aa. All previously reported items were addressed, replied to inline, and resolved; the full local verification suite passes.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

@nonnil I will re-review the current head of PR #1.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

@nonnil I will review the current head, 92134aa, of PR #1.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 92134aac84

ℹ️ 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".

Comment thread src/media_sessions.m Outdated
Comment thread src/media.rs Outdated
@nonnil

nonnil commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

Review trace for #1 (review)

Classification: must_fix; fixed in 20619a5. Both child findings were addressed: last-playing-date request errors now invalidate incomplete refreshes, and unchanged selected artwork reuses exact-key decoded cache state without per-poll image cloning. Validation: cargo fmt --all --check; cargo clippy --all-targets --all-features -- -D warnings; cargo test --all-targets --all-features (55 passed); cargo build --all-targets --all-features; git diff --check.

@nonnil

nonnil commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please re-review the current head 20619a5. Both follow-up findings from review 4906949704 were addressed, replied to inline, and resolved; the full local verification suite passes.

@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: 20619a5074

ℹ️ 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".

Comment thread src/media_sessions.m Outdated
Comment thread src/media_sessions.m Outdated
Comment thread src/media_sessions.m Outdated
@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Resolved the aggregate Codex review at commit 2de5f89. All three inline findings were fixed, replied to individually, reacted to, and resolved: stopped-client date failures no longer freeze healthy playback, playing sessions require metadata resolution, and non-finite MediaRemote numbers are filtered before JSON serialization. The broader audit also removed the unsafe Perl XSUB bridge, hardened helper startup/environment/tool resolution, added pinned least-privilege macOS PR CI, and removed stale runtime documentation. Local validation passed on Rust 1.88: format, Clippy with warnings denied, 58 tests, doc tests, debug build, arm64/x86_64 release builds, universal binary signing/version smoke, helper ready/environment smoke, RustSec audit (1,211 advisories / 80 dependencies, zero vulnerabilities), and diff/secret checks. CI is now running on this head.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/media.rs (2)

522-542: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Deduplicate the three failure arms of the ready handshake.

Each arm repeats child.kill(), child.wait(), and reader.join(), then calls bail!. Only the message differs. A future change to the shutdown sequence must be applied three times.

♻️ Proposed refactor
-            match ready_rx.recv_timeout(HELPER_READY_TIMEOUT) {
-                Ok(Ok(())) => {}
-                Ok(Err(error)) => {
-                    let _ = child.kill();
-                    let _ = child.wait();
-                    let _ = reader.join();
-                    bail!("{error}");
-                }
-                Err(mpsc::RecvTimeoutError::Timeout) => {
-                    let _ = child.kill();
-                    let _ = child.wait();
-                    let _ = reader.join();
-                    bail!("MediaRemote session helper did not become ready within {HELPER_READY_TIMEOUT:?}");
-                }
-                Err(mpsc::RecvTimeoutError::Disconnected) => {
-                    let _ = child.kill();
-                    let _ = child.wait();
-                    let _ = reader.join();
-                    bail!("MediaRemote session helper reader stopped before ready");
-                }
-            }
+            let ready_error = match ready_rx.recv_timeout(HELPER_READY_TIMEOUT) {
+                Ok(Ok(())) => None,
+                Ok(Err(error)) => Some(error),
+                Err(mpsc::RecvTimeoutError::Timeout) => Some(format!(
+                    "MediaRemote session helper did not become ready within {HELPER_READY_TIMEOUT:?}"
+                )),
+                Err(mpsc::RecvTimeoutError::Disconnected) => Some(
+                    "MediaRemote session helper reader stopped before ready".to_owned(),
+                ),
+            };
+            if let Some(error) = ready_error {
+                let _ = child.kill();
+                let _ = child.wait();
+                let _ = reader.join();
+                bail!("{error}");
+            }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/media.rs` around lines 522 - 542, Deduplicate the failure handling in the
ready handshake match around ready_rx.recv_timeout: have each failure case
produce its distinct error message, then perform child.kill(), child.wait(), and
reader.join() once in shared control flow before bailing. Preserve the existing
messages and successful Ok(Ok(())) behavior.

496-499: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Malformed session lines are dropped without any signal.

If the helper payload schema drifts, serde_json::from_str::<SessionPayload> fails and the loop silently skips the line. The Now Playing state then freezes at the last valid payload with no diagnostic. The stopped path at Lines 508-518 reports its reason on stderr; this path reports nothing.

Log the first parse failure, or log at a throttled rate, so schema drift is visible.

♻️ Proposed change
                         Some(Ok(line)) => {
-                            let Ok(payload) = serde_json::from_str::<SessionPayload>(&line) else {
-                                continue;
-                            };
+                            let payload = match serde_json::from_str::<SessionPayload>(&line) {
+                                Ok(payload) => payload,
+                                Err(error) => {
+                                    if !reported_parse_error {
+                                        reported_parse_error = true;
+                                        eprintln!(
+                                            "MediaRemote session helper sent an unparsable payload: {error}"
+                                        );
+                                    }
+                                    continue;
+                                }
+                            };

Declare let mut reported_parse_error = false; before the loop.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/media.rs` around lines 496 - 499, Add a mutable parse-error reporting
guard before the session-line loop, then update the
`serde_json::from_str::<SessionPayload>` failure branch to log the first
malformed payload error to stderr and mark it reported, while continuing to skip
invalid lines without repeatedly logging every failure.
.github/workflows/ci.yml (1)

48-52: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider verifying the embedded helper architecture in CI.

build.rs compiles and ad-hoc signs codex_micro_chroma_media_sessions per target architecture. The workflow builds both slices but does not assert that the embedded helper matches the requested target. A cross-arch mismatch would only appear at runtime on a user machine.

Add a check that runs lipo -info or file on the helper in target/<triple>/release/build/*/out/codex_micro_chroma_media_sessions, and codesign -dv to confirm the ad-hoc signature.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 48 - 52, Update the “Build arm64
release binary” and “Build x86_64 release binary” CI steps to inspect the
generated codex_micro_chroma_media_sessions under each target’s release build
output, using lipo -info or file to verify the expected architecture and
codesign -dv to verify its ad-hoc signature. Ensure the checks locate the helper
within the build/*/out directory and fail the workflow on mismatches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/media_sessions.m`:
- Around line 99-121: Update the candidate-processing logic in the session
helper around the playing/metadataResolved checks so lastPlayingDateError no
longer sets valid to NO. When that flag is present, treat lastPlayingDate as
unavailable by removing or omitting its value from publicCandidate, allowing the
Rust selector to fall back to elected and stableId; retain the strict
invalidation for metadataResolved failures and existing publication behavior.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 48-52: Update the “Build arm64 release binary” and “Build x86_64
release binary” CI steps to inspect the generated
codex_micro_chroma_media_sessions under each target’s release build output,
using lipo -info or file to verify the expected architecture and codesign -dv to
verify its ad-hoc signature. Ensure the checks locate the helper within the
build/*/out directory and fail the workflow on mismatches.

In `@src/media.rs`:
- Around line 522-542: Deduplicate the failure handling in the ready handshake
match around ready_rx.recv_timeout: have each failure case produce its distinct
error message, then perform child.kill(), child.wait(), and reader.join() once
in shared control flow before bailing. Preserve the existing messages and
successful Ok(Ok(())) behavior.
- Around line 496-499: Add a mutable parse-error reporting guard before the
session-line loop, then update the `serde_json::from_str::<SessionPayload>`
failure branch to log the first malformed payload error to stderr and mark it
reported, while continuing to skip invalid lines without repeatedly logging
every failure.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a8673ef3-5e9f-44ec-bcb8-91ffb950ac7a

📥 Commits

Reviewing files that changed from the base of the PR and between 20619a5 and 2de5f89.

📒 Files selected for processing (6)
  • .github/workflows/ci.yml
  • README.md
  • build.rs
  • src/main.rs
  • src/media.rs
  • src/media_sessions.m
🚧 Files skipped from review as they are similar to previous changes (2)
  • build.rs
  • README.md

Comment thread src/media_sessions.m
@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Addressed the complete CodeRabbit review at #1 (review) in signed commits e353e6d and 26647eb. The Major inline finding now degrades lastPlayingDate request failures to a missing date and falls back to macOS election/stable ID without weakening metadata or playback-state completeness. The three review-body quality items are also handled: the reader reports the first malformed post-ready payload, pre-ready termination uses one shared cleanup path, and CI asserts exactly one helper per target plus lipo architecture and codesign validity. Validation passed on Rust 1.88 with Clippy warnings denied, 60 tests, doc tests, debug and both release builds, strict Objective-C compile, and local arm64/x86_64 helper verification. The inline thread was replied to, reacted to, and resolved.

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please re-review current head 26647eb for final merge readiness after all prior inline and aggregate feedback was addressed. CI is green; please focus on correctness, stop/fallback arbitration, Objective-C/MediaRemote async behavior, helper lifecycle/security, and CI integrity.

@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: 26647ebb4f

ℹ️ 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".

Comment thread src/media_sessions.m
@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@codex review

Final re-review request for current head 5d45bd2. Since the previous request, scoped requestIsPlaying errors now degrade only that candidate to playingResolved=false instead of freezing the whole snapshot; global timeouts and successful scoped states remain unchanged. CI is green with 62 local tests and both helper architecture/signature checks. Please confirm no remaining correctness, security, performance, Objective-C/MediaRemote, or CI findings.

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@codex review\n\nFinal exact-head review requested for e55f249. The only findings on the previous exact head were that the Objective-C smoke harness was not executed in CI; this commit adds the strict compile/run gate. Please review the full PR for any remaining correctness, security, availability, lifecycle, arbitration, resource-bound, CI, or code-quality problem.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: e55f249487

ℹ️ 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".

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Review 4912979688 aggregate resolution trace

All five findings were adjudicated as valid and fixed on the current head 0cc988a:

  • 3763561171: winner-first artwork budgeting (5287930)
  • 3763561174: single-pass ordinary payload parsing (0cc988a)
  • 3763561175: complete OS client-list scan in bounded batches (5287930)
  • 3763561179: fallible finite timestamp conversion (0cc988a)
  • 3763561182: allocation-bounded UTF-8 extraction (5287930; CI smoke coverage e55f249)

Each original comment now has an inline fix reply and +1 reaction, and each review thread is resolved. Current CI is green.

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Review 4913091571 aggregate resolution trace

Both findings were adjudicated as valid and fixed on the current head 0cc988a:

  • 3763661878: tokenized per-stage watchdog rearmed for enumeration, every fixed-size batch, and enrichment (0cc988a)
  • 3763661884: Objective-C MEDIA_SESSIONS_SMOKE compile/run added to PR CI (e55f249)

Each original comment now has an inline fix reply and +1 reaction, and both review threads are resolved. Current CI is green.

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please perform a fresh review of exact head 0cc988a. The seven delayed findings from reviews 4912979688 and 4913091571 have been fixed, replied to inline, reacted to, and resolved. Please re-check correctness, security/resource bounds, stop-to-fallback behavior, multi-session arbitration, and CI coverage.

@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: 0cc988a546

ℹ️ 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".

Comment thread src/media.rs Outdated
@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Review 4913234070 aggregate resolution trace

The sole finding was adjudicated as valid and fixed on current head 7ed7336:

  • 3763776868: invalidate artwork delivery state after an authoritative artwork-less observation so recovered same-key artwork is redelivered.

The original comment has an inline fix reply and +1 reaction, and its review thread is resolved. Regression coverage plus Rust 1.88 fmt, Clippy with warnings denied, all 79 tests, Objective-C smoke, and dual-architecture CI are being revalidated on this head.

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please perform a fresh clean-slate review of exact head 7ed7336. The latest P2 from review 4913234070 is fixed with a focused regression, replied to inline, reacted to, and resolved. Please re-check the full PR, especially artwork recovery delivery state, stop/fallback behavior, multi-session arbitration, security/resource bounds, and CI coverage.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/media_sessions.m (1)

349-370: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

rankedPlayingCandidates exists only for the smoke build and duplicates the production ranking path.

Production selection uses insertTopRecord with streaming inserts. The smoke test exercises both, but rankedPlayingCandidates is a second implementation of the same policy that no production code calls. If the ranking policy changes later, the two can diverge and the test will still pass.

Consider expressing the batch-order tests at Lines 814-847 through insertTopRecord, then removing rankedPlayingCandidates.

Also applies to: 814-847

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/media_sessions.m` around lines 349 - 370, Replace the smoke-only batch
ranking tests that use rankedPlayingCandidates with tests exercising the
production insertTopRecord streaming path, preserving the existing ordering and
limit assertions. Once those tests use insertTopRecord, remove the duplicate
rankedPlayingCandidates helper and its conditional compilation block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/media_sessions.m`:
- Around line 628-652: Update the scoped-playing callback in the request path
around isPlayingSelector so an error invokes getInfoForPlayer(playerPath, NO,
queue, ...) with resolvePlayingFromRate enabled, using the same metadata
completion and dispatch-group cleanup as the existing fallback. Preserve the
direct playing assignment for successful requests and ensure each branch leaves
batchGroup exactly once, allowing failed scoped requests to set playingResolved
through the playback-rate fallback.

---

Nitpick comments:
In `@src/media_sessions.m`:
- Around line 349-370: Replace the smoke-only batch ranking tests that use
rankedPlayingCandidates with tests exercising the production insertTopRecord
streaming path, preserving the existing ordering and limit assertions. Once
those tests use insertTopRecord, remove the duplicate rankedPlayingCandidates
helper and its conditional compilation block.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e496cad7-c800-4ebe-9dbb-4ee19d69d4e5

📥 Commits

Reviewing files that changed from the base of the PR and between 18b6a06 and 7ed7336.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • README.md
  • src/media.rs
  • src/media_sessions.m
🚧 Files skipped from review as they are similar to previous changes (3)
  • README.md
  • src/media.rs
  • .github/workflows/ci.yml

Comment thread src/media_sessions.m
@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Review 4913261195 aggregate adjudication trace

The sole comment 3763799236 was adjudicated as non-actionable intent mismatch on current head 7ed7336. Falling back to metadata playbackRate after an implemented scoped isPlaying API returns an error can revive stale playing state and regress immediate stop/fallback behavior. The intentional contract is: missing scoped selector uses metadata fallback; scoped error leaves only that client unresolved, Rust excludes it and selects another resolved active session or clears, then retries on the next refresh. README.md and three focused unresolved-scoped-state regressions cover this behavior.

The original comment has an inline rationale, -1 reaction, and resolved thread. No code change was made for this invalid suggestion.

@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: 7ed73366a5

ℹ️ 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".

Comment thread src/media_sessions.m Outdated
@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Review 4913288880 aggregate resolution trace

The sole finding 3763820505 was adjudicated as valid and fixed on current head 5129acd. Artwork is now selected winner-first from exact pre-allocation base64/cache costs, encoded only when both bounded budgets admit it, and retained in the cache without stale-entry or duplicate-ID oversubscription.

The original comment has an inline fix reply and +1 reaction, and its thread is resolved. Production/smoke Objective-C -Werror builds, budget/cache regressions, Rust 1.88 fmt/Clippy/all 79 tests, and cargo-audit pass.

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please perform another fresh clean-slate review of exact head 5129acd. The latest P2 from review 4913288880 is fixed with pre-allocation winner-first artwork budgeting and cache regressions, replied to inline, reacted to, and resolved. Please re-check the full PR, especially phase-2 async lifetime, resource/accounting invariants, stop/fallback and arbitration, security, and CI.

@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: 5129acd99a

ℹ️ 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".

Comment thread src/media_sessions.m Outdated
Comment thread src/media_sessions.m
@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Review 4913392542 aggregate resolution trace

Both findings were valid and are fixed on current head d4e69c2:

  • 3763908954: phase-2 callback metadata is immediately whitelist-sanitized and bounded; artwork >8 MiB is never retained. Winner-ranked one-record phases plus a 24 MiB cache provide an explicit 32 MiB retained-artwork ceiling.
  • 3763908957: phase-1 top-K coalesces duplicate stable IDs, keeping the higher-ranked record or the first exact tie, so downstream selection and artwork ownership cannot diverge.

Both comments have inline fix replies and +1 reactions, and both threads are resolved. Rust 1.88 fmt/Clippy/all 79 tests, Objective-C arm64/x86_64 smoke -Werror, cargo-audit, remote SHA, and GitHub signature verification pass.

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please perform a final fresh clean-slate review of exact head d4e69c2. All prior valid findings have been fixed, replied to inline, reacted to, and resolved. Please re-check the complete PR for correctness, security/resource bounds, duplicate/session arbitration, stop-to-fallback behavior, metadata/artwork recovery, and CI coverage.

@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: d4e69c2759

ℹ️ 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".

Comment thread src/media_sessions.m
Comment thread src/media_sessions.m Outdated
@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Review 4913539143 aggregate adjudication trace

Current head: 7784252.

  • 3764032756 was adjudicated as an intent mismatch: per-client lastPlayingDate errors deliberately degrade to missing date and OS-election/stable-ID tie-breaks; suppressing the complete snapshot can indefinitely preserve stale lighting and block stop/fallback. Inline rationale, -1 reaction, resolved.
  • 3764032763 was valid and fixed: missing/non-finite playbackRate fallbacks remain unresolved, while finite rates authoritatively resolve stopped/playing. Inline fix evidence, +1 reaction, resolved.

Rust 1.88 fmt/Clippy/all 79 tests, Objective-C arm64/x86_64 smoke, remote SHA, and GitHub signature verification pass.

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please perform a final fresh clean-slate review of exact head 7784252. The valid missing-playbackRate fallback issue from review 4913539143 is fixed with focused smoke coverage; the date-error comment was adjudicated against the documented availability contract. All threads are resolved. Please re-check the complete PR for any remaining actionable correctness, security/resource, arbitration, stop/fallback, metadata/artwork, or CI issues.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 7784252c8b

ℹ️ 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".

@nonnil

nonnil commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@codex review

Retrying because the prior exact-head request finished without a review object or result reaction. Please review exact head 7784252 and report any remaining actionable issues; all existing 29 threads are resolved and CI/CodeRabbit are green.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 7784252c8b

ℹ️ 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".

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant