Skip to content

Notify only after Pi agent settles - #8574

Merged
austinywang merged 12 commits into
manaflow-ai:mainfrom
mrohan-sq:mrohan/fix-pi-settled-notification
Jul 22, 2026
Merged

austinywang merged 12 commits into
manaflow-ai:mainfrom
mrohan-sq:mrohan/fix-pi-settled-notification

Conversation

@mrohan-sq

@mrohan-sq mrohan-sq commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Context

Pi's agent_end marks a low-level run, so cmux can announce completion before automatic retries, compaction, or follow-up work. Fixes #8568.

Summary

  • Defer modern Pi completion hooks until agent_settled and idle
  • Preserve pre-0.80.5 behavior through package-version detection
  • Cover retries, duplicate settlement, shutdown, notification failure, and npm-linked installations

Dependencies

None.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Delay Pi completion notifications until the agent has settled and the context is idle to avoid premature “done” signals during retries. Preserves legacy behavior for Pi versions before 0.80.5 and handles npm-linked, unknown, or malformed installs. Fixes #8568.

  • Bug Fixes
    • Publish completion on agent_settled only when ctx.isIdle(); de-duplicate settlements.
    • Track pending completion per session; preserve the latest assistant message and notification type; consume once.
    • Fall back to agent_end for legacy/unknown/malformed Pi with correct stop payload.
    • Send notification before stop and set cmux_notification_routed when delivered.
    • On shutdown before settlement, emit a single stop; ignore late settlements; respect hooks-disabled mode.
    • Detect Pi version by walking to the nearest package.json (realpath of npm bin symlink); supports @earendil-works/pi-coding-agent and @mariozechner/pi-coding-agent; gate modern behavior to >= 0.80.5.
    • Tests cover idle/busy settlement, retries, duplicates, shutdown, notification failure, legacy/unknown/malformed fallback, npm-linked detection, and hook order.

Written for commit 2c70f14. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved Pi turn lifecycle to defer completion notification and stop updates until the session settles, preventing duplicate events.
    • Added cross-turn pending completion tracking and ensured stop payloads include the latest assistant message.
    • Implemented Pi CLI version detection with feature gating, including correct behavior for modern, legacy, unknown, and malformed CLI layouts and proper busy/idle handling.
  • Tests
    • Expanded the Pi extension regression test suite with more realistic fake CLI/package scenarios and stronger assertions for hook sequencing, event suppression, and stop payload contents.

@coderabbitai

coderabbitai Bot commented Jul 21, 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 generated Pi extension stores completion data at agent_end, publishes notification and stop hooks at agent_settled when supported, and retains fallback behavior for unsupported or undetected Pi versions. Tests cover settlement timing, duplicate prevention, version detection, and payloads.

Changes

Pi completion settlement

Layer / File(s) Summary
Completion state and Pi capability detection
CLI/CMUXCLI+PiExtensionSourcePart1.swift
Adds pending completion state, lifecycle clearing, turn settlement, and Pi version detection for agent_settled support.
Settled completion hook flow
CLI/CMUXCLI+PiExtensionSourcePart2.swift
Stores completion data during agent_end, centralizes notification and stop publication, and consumes pending completion from idle agent_settled events.
Completion flow regression coverage
tests/test_pi_extension_install.py
Adds Pi installation fixtures and validates deferred settlement, busy and idle behavior, duplicate prevention, fallback handling, resume operations, and stop payload contents.

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

Sequence Diagram(s)

sequenceDiagram
  participant Pi
  participant PiExtension
  participant CmuxHooks
  Pi->>PiExtension: agent_end with completion data
  PiExtension->>PiExtension: store pendingCompletion
  Pi->>PiExtension: agent_settled when idle
  PiExtension->>PiExtension: settleTurn(sessionId)
  PiExtension->>CmuxHooks: notification
  PiExtension->>CmuxHooks: stop with last assistant message and turn id
Loading

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Full Internationalization ❌ Error PR adds hardcoded user-facing notification text "Task completed" in the Pi extension without localization or catalog entries. Replace it with String(localized:defaultValue:) (or equivalent) and add matching string-catalog translations for every supported locale.
Description check ⚠️ Warning The description includes context and summary, but it omits the required Testing, Demo Video, Review Trigger, and Checklist sections. Add the missing template sections: testing steps, demo video link or note, review-trigger block, and completed checklist items.
✅ Passed checks (23 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes cache completion at agent_end and publish stop on agent_settled with idle gating, matching #8568's requirements.
Out of Scope Changes check ✅ Passed The version detection, npm-linked handling, and extra tests all support the stated Pi settlement fix and related edge cases.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Cmux Swift Actor Isolation ✅ Passed HEAD^..HEAD changes only the Python test; no Swift isolation-sensitive declarations were introduced or worsened.
Cmux Swift Blocking Runtime ✅ Passed No new semaphores, waits, sleeps, main-queue syncs, timers, polling, or locks appear in the Pi Swift changes; the new logic is state/event driven.
Cmux Browser Automation Off-Main ✅ Passed PASS: The diff only changes a Pi extension regression test; no browser.*/WebKit/socketWorker/mainActor automation code or routing logic is touched.
Cmux Expensive Synchronous Load ✅ Passed No production Swift files changed; the only diff is a Python test update, so the expensive-sync-load rule isn’t implicated.
Cmux Cache Substitution Correctness ✅ Passed pendingCompletion is a transient, event-driven handoff; it’s cleared on begin/finish/settle/session_start and isn’t used in any persistence/history/undo/snapshot path.
Cmux No Hacky Sleeps ✅ Passed PASS: The only changed file is a Python regression test, and its time.sleep(0.05) sits in a test-only wait_for_text helper, which the rule allows.
Cmux Algorithmic Complexity ✅ Passed No scalable nested scans: the new loops are bounded (argv, 2-item argv prefix, 8-level package walk) or single-pass over one event/session; no batch rescans.
Cmux Swift Concurrency ✅ Passed No new DispatchQueue/Combine/Task-style patterns were added in the changed Swift files; the diff is embedded Pi JS source, not Swift concurrency code.
Cmux Swift @Concurrent ✅ Passed No changed Swift file in the PR diff adds or alters @concurrent/nonisolated async behavior; the only new async addition is in the embedded Pi JS template.
Cmux Swift Package Boundaries ✅ Passed PASS: the Swift changes are generated Pi-extension source glued into the CLI target, which the rule explicitly allows.
Cmux Swiftpm Lockfiles ✅ Passed PR diff from main touches only Swift source and test files; no .gitignore, SwiftPM, Xcode, workflow, or Package.resolved files changed, so the lockfile rule is not violated.
Cmux Swift Logging ✅ Passed No new Swift logging APIs were introduced; the only console.warn-based diagnostics preexist and weren’t changed in the diff.
Cmux User-Facing Error Privacy ✅ Passed Added user-facing text is generic (“Task completed”); no new errors/alerts expose upstream names, secrets, raw messages, or env details.
Cmux Swiftui State Layout ✅ Passed No SwiftUI view/state/layout code was added; the touched Swift files only embed Pi hook JS, and the test changes are unrelated.
Cmux Architecture Rethink ✅ Passed PASS: The Swift changes keep completion state in one session map, centralize settlement in one helper, and avoid sleeps, polling, locks, or duplicate wiring.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No Swift files changed; the PR only touches tests/test_pi_extension_install.py, so the auxiliary-window shortcut rule is not applicable.
Cmux Source Artifacts ✅ Passed Only changed path is a hand-written test file; the diff adds assertion cases and no local output, caches, temp dirs, or other source artifacts.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The Pi changes are in CLI/* fragments, not Sources/**, and they add production helpers rather than test/debug seams.
Cmux No Ambient Global State ✅ Passed No new Swift ambient globals were added; the changes only update static let embedded source strings, which the rule allows.
Title check ✅ Passed The title is concise and accurately captures the main change: delaying Pi completion until the agent settles.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@mrohan-sq
mrohan-sq marked this pull request as ready for review July 21, 2026 21:19
@greptile-apps

greptile-apps Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR delays Pi completion notifications until the agent is fully settled. The main changes are:

  • Tracks pending completion state across retries and follow-up work.
  • Uses agent_settled for modern Pi versions and agent_end for legacy versions.
  • Preserves notification and stop ordering while preventing duplicate completion hooks.
  • Adds coverage for retries, shutdown, failed notifications, linked installs, and version fallbacks.

Confidence Score: 5/5

The latest changes look safe to merge.

  • No additional blocking issues were found in the updated code.
  • The lifecycle tests cover retries, duplicate settlement, shutdown, hook failure, and compatibility paths.

Important Files Changed

Filename Overview
CLI/CMUXCLI+PiExtensionSourcePart1.swift Adds Pi version detection and session-scoped pending completion state.
CLI/CMUXCLI+PiExtensionSourcePart2.swift Publishes completion after settlement for modern Pi and retains legacy fallback behavior.
tests/test_pi_extension_install.py Expands lifecycle and version-detection coverage for the updated completion flow.

Reviews (5): Last reviewed commit: "fixup! Resolve npm-linked Pi package ver..." | Re-trigger Greptile

Comment thread CLI/CMUXCLI+PiExtensionSourcePart1.swift Outdated
Comment thread CLI/CMUXCLI+PiExtensionSourcePart2.swift

@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

🤖 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 `@CLI/CMUXCLI`+PiExtensionSourcePart1.swift:
- Around line 94-132: Update supportsAgentSettled so detectedPiVersion returning
no version or an unparseable version defaults to false rather than true.
Preserve the existing version threshold logic for reliably parsed versions,
ensuring undetectable installations follow the immediate completion-notification
path.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7294ea41-f392-4fab-be5d-54789e129cc2

📥 Commits

Reviewing files that changed from the base of the PR and between 1cf6b7f and 43f8470.

📒 Files selected for processing (3)
  • CLI/CMUXCLI+PiExtensionSourcePart1.swift
  • CLI/CMUXCLI+PiExtensionSourcePart2.swift
  • tests/test_pi_extension_install.py

Comment thread CLI/CMUXCLI+PiExtensionSourcePart1.swift

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_pi_extension_install.py (1)

287-337: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Shutdown right after a successful settlement isn't checked for a duplicate stop hook.

Line 313 calls session_shutdown({reason:"quit"}, ctx) immediately after agent_settled already published notification+stop (line 308-310) and after a duplicate-settlement no-op check (line 311-312). No assertion verifies completionHookCount() is unchanged by this shutdown call, and the very next session resets completionCount = await completionHookCount() at line 323 — silently absorbing any extra "stop" emitted here. Context snippet 3 (session_shutdown gated by if (!state.stopped)) suggests this is handled, but that's exactly the invariant this PR is meant to lock in for duplicate/early/late completion (per PR objective), and the interrupted-session flow already tests the mirror case (late agent_settled after shutdown, 332-333) — this symmetric case for "shutdown after already-settled" deserves the same explicit lock-in.

Based on .github/review-bot-rules/reliability-single-source-of-truth.md, which calls for consolidating to a single authority instead of leaving a fallback/duplicate path unverified.

✅ Suggested assertion to add before line 314
 await handlers.get("agent_settled")({}, ctx);
 if (await completionHookCount() !== completionCount) throw new Error("duplicate agent_settled emitted completion twice");
+const preShutdownCount = completionCount;
 await handlers.get("session_shutdown")({ reason: "quit" }, ctx);
+if (await completionHookCount() !== preShutdownCount) throw new Error("shutdown after settlement emitted a duplicate stop");
🤖 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 `@tests/test_pi_extension_install.py` around lines 287 - 337, Add an explicit
count-preservation assertion around the first session_shutdown call after
agent_settled in the main ctx flow: capture completionHookCount() before
shutdown, invoke session_shutdown({ reason: "quit" }, ctx), and verify the count
is unchanged. Keep the existing duplicate-settlement and interrupted-session
checks intact so shutdown after successful settlement is covered as a no-op.

Source: Path instructions

🤖 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 `@tests/test_pi_extension_install.py`:
- Around line 390-433: Refactor the repeated fallback scenarios in
tests/test_pi_extension_install.py:390-433 by adding a helper inside
check_source (such as testFallbackSession) that performs setup, handler calls,
and completion-count validation, then invoke it for legacy, unknown, and
malformed sessions; also refactor tests/test_pi_extension_install.py:549-572
with a find_stop_payload(payloads, session_id) helper and use it for all three
payload assertions.

---

Outside diff comments:
In `@tests/test_pi_extension_install.py`:
- Around line 287-337: Add an explicit count-preservation assertion around the
first session_shutdown call after agent_settled in the main ctx flow: capture
completionHookCount() before shutdown, invoke session_shutdown({ reason: "quit"
}, ctx), and verify the count is unchanged. Keep the existing
duplicate-settlement and interrupted-session checks intact so shutdown after
successful settlement is covered as a no-op.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: dfb99f4b-9a3e-40a5-a5fc-2bcfa702fc5a

📥 Commits

Reviewing files that changed from the base of the PR and between 43f8470 and a6d6a92.

📒 Files selected for processing (2)
  • CLI/CMUXCLI+PiExtensionSourcePart1.swift
  • tests/test_pi_extension_install.py

Comment thread tests/test_pi_extension_install.py
@mrohan-sq

Copy link
Copy Markdown
Contributor Author

🤖 Updated by Pi — added the explicit count-preservation assertion proving session_shutdown after successful settlement cannot emit a duplicate stop. The focused generated-extension test and tagged Debug build pass on head 1ae4a4cf80.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_pi_extension_install.py (1)

335-338: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert that hooks-disabled mode suppresses completion hooks.

The scenario invokes session_start and session_shutdown but never checks the result, so a regression could emit completion/stop hooks while CMUX_PI_HOOKS_DISABLED=1 and still pass.

Suggested assertion
 process.env.CMUX_PI_HOOKS_DISABLED = "1";
+const disabledCompletionCount = await completionHookCount();
 const disabledCtx = {
...
 await handlers.get("session_shutdown")({ reason: "disabled" }, disabledCtx);
+if (await completionHookCount() !== disabledCompletionCount) {
+  throw new Error("hooks-disabled mode emitted completion hooks");
+}
 delete process.env.CMUX_PI_HOOKS_DISABLED;
🤖 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 `@tests/test_pi_extension_install.py` around lines 335 - 338, Update the
hooks-disabled test around disabledCtx and its session_start/session_shutdown
calls to capture the emitted events and assert that no completion or stop hooks
are produced when CMUX_PI_HOOKS_DISABLED is set to "1". Preserve the existing
setup and lifecycle invocation while making the suppression behavior explicitly
testable.
🤖 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.

Outside diff comments:
In `@tests/test_pi_extension_install.py`:
- Around line 335-338: Update the hooks-disabled test around disabledCtx and its
session_start/session_shutdown calls to capture the emitted events and assert
that no completion or stop hooks are produced when CMUX_PI_HOOKS_DISABLED is set
to "1". Preserve the existing setup and lifecycle invocation while making the
suppression behavior explicitly testable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 03ac5cfb-93e7-4aff-9a76-12455ceba937

📥 Commits

Reviewing files that changed from the base of the PR and between a6d6a92 and 1ae4a4c.

📒 Files selected for processing (1)
  • tests/test_pi_extension_install.py

@cursor

cursor Bot commented Jul 22, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@mrohan-sq

Copy link
Copy Markdown
Contributor Author

🤖 Updated by Pi — added an explicit assertion that CMUX_PI_HOOKS_DISABLED=1 suppresses all completion hooks across session start and shutdown. The focused test and tagged Debug build pass on head 8a5ccc1839.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_pi_extension_install.py (1)

386-392: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert Notification → Stop for every fallback session.

The legacy, unknown, and malformed cases currently verify only that two completion commands occurred, while the later payload checks require only a Stop. A regression that omits Notification or emits two Stop hooks would pass despite breaking the preserved fallback contract. Filter completion payloads by session and assert the exact sequence ["Notification", "Stop"].

Also applies to: 408-414, 430-436, 540-575

🤖 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 `@tests/test_pi_extension_install.py` around lines 386 - 392, Update the
fallback-session assertions around the legacy, unknown, and malformed cases,
including the analogous later cases, to filter completion payloads by session
and assert the exact command sequence ["Notification", "Stop"]. Replace
count-only checks so missing Notification or duplicate Stop emissions fail while
preserving the existing fallback validation.
🤖 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.

Outside diff comments:
In `@tests/test_pi_extension_install.py`:
- Around line 386-392: Update the fallback-session assertions around the legacy,
unknown, and malformed cases, including the analogous later cases, to filter
completion payloads by session and assert the exact command sequence
["Notification", "Stop"]. Replace count-only checks so missing Notification or
duplicate Stop emissions fail while preserving the existing fallback validation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b763f89b-107b-455c-9447-ae01e088fc45

📥 Commits

Reviewing files that changed from the base of the PR and between 1ae4a4c and 8a5ccc1.

📒 Files selected for processing (1)
  • tests/test_pi_extension_install.py

@mrohan-sq

Copy link
Copy Markdown
Contributor Author

🤖 Updated by Pi — fallback coverage now asserts the exact Notification → Stop sequence independently for recognized legacy, unknown, and malformed Pi versions. Focused tests and the tagged Debug build pass on head 2c70f14421.

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.

Pi completion hook notifies before agent settles

2 participants