Skip to content

acpmux: record the PRs each session opens; the sidebar session tree counts them all - #21823

Merged
teamleaderleo merged 15 commits into
feat/next-sidebar-session-nestingfrom
feat/acpmux-session-pull-requests
Oct 19, 2026
Merged

teamleaderleo merged 15 commits into
feat/next-sidebar-session-nestingfrom
feat/acpmux-session-pull-requests

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

FYI @lawrencecchen: this is in your sidebar area. It stays behind sidebar.sessionTree (off by default) and lands under Leo's merge directive; no action needed.

Stacked on #21814 (base is its branch; retarget to feat-cmux-next once it lands).

Summary

The sidebar session tree's PR badge (#21814) counts only the pull request of each session folder's current branch, so a session that opened several PRs from different branches undercounts. This is the follow-up #21814 names.

  • acpmux remembers each shell tool call (kind: execute) whose whole command is gh pr create ..., until the call ends. Before gh it allows cd DIR && steps and VAR=value / env prefixes. The command comes from rawInput.command (a string, or the script of bash -lc), and the parse handles quotes, $( ) and heredocs, so Claude's --body "$(cat <<'EOF' ...)" form counts. A grep or echo that mentions gh pr create does not count, and neither does gh pr create && gh pr view 9. Of the call's output, only the last line that is nothing but a https://github.com/<owner>/<repo>/pull/<n> URL is kept, from rawOutput.stdout when the harness separates it (Claude's result mixes stdout and stderr). It joins SessionMeta.pull_requests, de-duplicated, oldest first, capped at 200. Harnesses send the command on tool_call and the output on later updates, which is why the call id is remembered. A fork starts with no PRs. Subagent tool calls are not counted.
  • SessionSummary.pullRequests: string[] is optional in the schema, so peers on older acpmux versions still parse. The protocol IR and the web types are regenerated. The codegen also picked up an existing doc-comment drift on LifecycleReceipt from the base.
  • CmuxNext: AgentActivityLane.pullRequests parses the field. SidebarSessionTreeFeed asks SidebarPullRequestStore for each workspace's folders and each opened URL. The store reads a URL with gh pr view <url> (new AgentChatPullRequestReader.read(url:)) on the same two-at-a-time queue and once-a-minute freshness. The badge counts each pull request once, even when a folder's branch PR is also an opened URL or two sessions name the same PR.

Testing

  • cargo test -p acpmux --test hub_integration session_pull_requests (Linux): red on the test-only commit 1591a4a with the detector disabled (left: []), green on bc84c70. The fake agent opens two PRs (command on the call, URL on the update; a string and an argv), runs gh pr view on a third, prints the first one again, greps for gh pr create with a URL in the output, and chains gh pr create && gh pr view 11. Only the two opened PRs count. The test checks the summary, the saved meta and _acpmux/sessions.

  • Full cargo test -p acpmux --test hub_integration (250 passed, 1 ignored), cargo test -p acpmux --lib (142 passed, including 3 new unit tests for the command parse, the argv/title source and the printed-URL rule), and cargo clippy -p acpmux --all-targets (clean) on Linux. protocol_ir_is_current was blessed and bun run acpmux-protocol:codegen was run.

  • Swift: new SidebarSessionTreeTests cases (lane parsing, asks/badge dedupe). These are not compiled locally because the dev box is Linux. They rely on the cmux-next CI lane. Not checked live in a tagged build.

  • After merging cmux NEXT sidebar: session tree with PR badges and keycaps (sidebar.sessionTree, off by default) #21814's review fixes (the feed moved to CmuxNextAgentLanes, the reader to CmuxNextAgentPane) and tightening the detector at 36152bd: full acpmux suites green again, clippy clean, acpmux-protocol:check current. The hosted --filter session_pull_requests run passed on bc84c70 and is re-running on 36152bd.

  • Peers: summaries go to attached clients and to the peer daemons the person configured (peers in their own acpmux config, reached with that peer's token or over ssh), which are their own machines. Those summaries already carry cwd, title, last prompt and preview.

Changelog

none (behind sidebar.sessionTree, off by default)

Proof

No UI change beyond the badge count behind an off-by-default flag; CI only.

Checklist

  • Behavior changes have added or updated tests
  • Localization: no new user-facing strings

🤖 Generated with Claude Code

teamleaderleo and others added 2 commits October 18, 2026 23:48
Red: pullRequests is absent, so the summary has [] (no detector yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion tree counts them

A tool call whose command runs `gh pr create` is remembered until it ends; the
github.com pull URLs its output prints join SessionMeta.pull_requests, once each,
and SessionSummary exposes them as optional pullRequests. CmuxNext parses them on
AgentActivityLane and SidebarSessionTreeFeed reads each with `gh pr view <url>`
beside the folder's branch PR, counting each pull request once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 18, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 59a227cb-9160-4ba2-9fa2-924077d63a2d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@blacksmith-sh

blacksmith-sh Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Found 28 test failures on Blacksmith runners:

Failures

Test View Logs
main.SideLanes.test_a_completed_failed_blacksmith_retry_is_not_watched_again/
test_a_completed_failed_blacksmith_retry_is_not_watched_again
View Logs
main.SideLanes.test_a_real_failure_on_cmux_next_attempt_two_is_not_rescued/
test_a_real_failure_on_cmux_next_attempt_two_is_not_rescued
View Logs
debug info {'CMUX_SWIFT_SANITIZE': 'address', 'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 's
wift --version' lacks '-debug-info-format none'/
debug info {'CMUX_SWIFT_SANITIZE': 'address', 'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 's
wift --version' lacks '-debug-info-format none'
View Logs
debug info {'CMUX_SWIFT_SANITIZE': 'address', 'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 's
wift package resolve --package-path Packages/macOS/Pkg --scratch-path Packages/macOS/
Pkg/.build-sanitize-address' lacks '-debug-info-format none'
View Logs
debug info {'CMUX_SWIFT_SANITIZE': 'address', 'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: wa
nt one build and two suites: ['swift package resolve --package-path Packages/macOS/
Pkg --scratch-path Packages/macOS/Pkg/
.build-sanitize-address', 'swift --version', 'swift build --build-tests -c debug -debug
-info-format none --sanitize=address --scratch-path Packages/macOS/Pkg/
.build-sanitize-address -Xlinker -no_compact_unwind --package-path Packages/macOS/
Pkg', 'swift test -c debug -debug-info-format none --sanitize=address --scratch-path Pa
ckages/macOS/Pkg/
.build-sanitize-address -Xlinker -no_compact_unwind --package-path Packages/macOS/
Pkg --skip-build --filter ATests', 'swift test -c debug -debug-info-format none --sanit
ize=address --scratch-path Packages/macOS/Pkg/
.build-sanitize-address -Xlinker -no_compact_unwind --package-path Packages/macOS/
Pkg --skip-build --filter BTests']
View Logs
debug info {'CMUX_SWIFT_SANITIZE': 'address'}: want one build and two suites: ['swift p
ackage resolve --package-path Packages/macOS/Pkg --scratch-path Packages/macOS/Pkg/
.build-sanitize-address', 'swift --version', 'swift build --build-tests -c debug --sani
tize=address --scratch-path Packages/macOS/Pkg/
.build-sanitize-address -Xlinker -no_compact_unwind --package-path Packages/macOS/
Pkg', 'swift test -c debug --sanitize=address --scratch-path Packages/macOS/Pkg/
.build-sanitize-address -Xlinker -no_compact_unwind --package-path Packages/macOS/
Pkg --skip-build --filter ATests', 'swift test -c debug --sanitize=address --scratch-pa
th Packages/macOS/Pkg/
.build-sanitize-address -Xlinker -no_compact_unwind --package-path Packages/macOS/
Pkg --skip-build --filter BTests']
View Logs
debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'dwarf'}: want one build and two suites: ['sw
ift package resolve --package-path Packages/macOS/
Pkg', 'swift --version', 'swift build --build-tests -c debug --package-path Packages/
macOS/Pkg', 'swift test -c debug --package-path Packages/macOS/
Pkg --skip-build --filter ATests', 'swift test -c debug --package-path Packages/macOS/
Pkg --skip-build --filter BTests']
View Logs
debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 'swift --version' lacks '-debug-info
-format none'/
debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 'swift --version' lacks '-debug-info
-format none'
View Logs
debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 'swift package resolve --package-pat
h Packages/macOS/Pkg' lacks '-debug-info-format none'
View Logs
debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: want one build and two suites: ['swi
ft package resolve --package-path Packages/macOS/
Pkg', 'swift --version', 'swift build --build-tests -c debug -debug-info-format none --
package-path Packages/macOS/
Pkg', 'swift test -c debug -debug-info-format none --package-path Packages/macOS/
Pkg --skip-build --filter ATests', 'swift test -c debug -debug-info-format none --packa
ge-path Packages/macOS/Pkg --skip-build --filter BTests']
View Logs

...and 18 more test failures. View all on Blacksmith

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Probably yours: CI fast guards failed: a guard step failed, FAIL 2.3s workflow-guard-tests / ci: Check that push and pull_request path filters agree. This PR merged before its CI finished: fix forward on main.

CI failed on 3e2f312545 (run 39314395922 attempt 1): 4 code, 8 unknown.
macOS jobs did not run: compile and app tests were skipped: Fast static checks failure.

Job Verdict Why
CI fast guards code a guard step failed
Fast static checks code a static check failed
guards / workflow-guard-tests / release-ios unknown no known signature; failed step: Validate iOS simulator build-once contract
guards / workflow-guard-tests / app-host-execution code a Python test failed
guards / workflow-guard-tests / preflight unknown no known signature; failed step: Validate reload shared DerivedData default
guards / workflow-guard-tests / release-tooling unknown no known signature; failed step: Validate retried Ghostty Zig package fetches
guards / workflow-guard-tests / quality-determinism code a Python test failed
guards / workflow-guard-tests / app-host-cache unknown no known signature; failed step: Validate iOS video recording failures
guards / workflow-guard-tests / release-notary unknown no known signature; failed step: Validate nightly API tag finalization
web / react-apps-check (2) unknown no known signature; failed step: Check diff viewer eager JS budget
web / react-apps-check (1) unknown no known signature; failed step: Check diff viewer eager JS budget
web / react-apps-check unknown no known signature; failed step: Check webview test shards
Matched log lines
CI fast guards: FAIL    2.3s  workflow-guard-tests / ci: Check that push and pull_request path filters agree
Fast static checks: FAILED localization (1.33s)
guards / workflow-guard-tests / app-host-execution: ERROR: test_failures_and_crashes_keep_swift_test_exit_status (__main__.DirectBundleSuiteTests.test_failures_and_crashes_keep_swift_test_exit_status)
guards / workflow-guard-tests / quality-determinism: FAIL: test_scans_only_test_sources (__main__.CommandLine.test_scans_only_test_sources)

Not re-run automatically: CI fast guards, Fast static checks, guards / workflow-guard-tests / release-ios, guards / workflow-guard-tests / app-host-execution, guards / workflow-guard-tests / preflight are not machine failures.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

CI fast guards failed on 8ce48bcee7 (https://github.com/manaflow-ai/cmux/actions/runs/39213840894). It does not block the merge; a red guard merged into main breaks it for every open PR.

Check that push and pull_request path filters agree

Test: python3 tests/test_ci_workflow_path_filter_parity.py

Traceback (most recent call last):
  File "/home/runner/_work/cmux/cmux/tests/test_ci_workflow_path_filter_parity.py", line 245, in <module>
    test_push_and_pull_request_filter_the_same_files()
  File "/home/runner/_work/cmux/cmux/tests/test_ci_workflow_path_filter_parity.py", line 156, in test_push_and_pull_request_filter_the_same_files
    assert not drifted, (
           ^^^^^^^^^^^
AssertionError: Every workflow that filters both events must filter them identically, or declare the difference in EXEMPTIONS or PUSH_ONLY_PATTERNS with a reason.
backend.yml: push and pull_request filter different files
  paths: guarded on push, not on pull requests: scripts/cmux-next/release/prod-promote.ts, scripts/cmux-next/release/production-approvals.json
cmux-next.yml: push and pull_request filter different files
  paths: guarded on pull requests, not on push: .github/actions/**, .github/workflows/**, Assets.xcassets/**, Native/**, Resources/**, TunnelExtension/**, cmuxTests/**, cmuxUITests/**, daemon/**, docs/configuration.md, scripts/cmux-next/**, scripts/cmux-next/state-roots-baseline.tsv, scripts/whats-new/**, tests/**
  paths: guarded on push, not on pull requests: .github/workflows/nightly.yml, scripts/apple-developer-id-certs/**, scripts/ci/*nightly*, scripts/ci/download-run-artifact.py, scripts/ci/finalize-release.py, scripts/ci/publish-release-assets.py, scripts/ci/signing-keychain.sh, scripts/ci/update-release-tag.py, scripts/ci/upload-r2-object.py, scripts/import-apple-devel ...

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Check that push and pull_request path filters agree'

Validate local hook trust boundary

Test: python3 tests/test_preflight_trust.py

  • PreflightTrustTests.test_annotated_tag_push_does_not_execute_candidate_code
subprocess.CalledProcessError: Command '['bash', 'scripts/install-git-hooks.sh']' returned non-zero exit status 1.
  • PreflightTrustTests.test_hook_installation_has_no_candidate_code_runner
subprocess.CalledProcessError: Command '['bash', 'scripts/install-git-hooks.sh']' returned non-zero exit status 1.
  • PreflightTrustTests.test_push_does_not_execute_candidate_check_through_unchanged_wrapper
subprocess.CalledProcessError: Command '['bash', 'scripts/install-git-hooks.sh']' returned non-zero exit status 1.
  • PreflightTrustTests.test_push_does_not_execute_candidate_runner
subprocess.CalledProcessError: Command '['bash', 'scripts/install-git-hooks.sh']' returned non-zero exit status 1.

... and 1 more failing tests in the run log.
Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate local hook trust boundary'

Run canonical CMUX CI guard profile

GitHub-hosted runner label: .github/workflows/gallery-link.yml:23:    runs-on: ubuntu-24.04
GitHub-hosted runner label: .github/workflows/nightly.yml:1463:    runs-on: ${{ inputs.nightly_next_unsigned_run != '' && 'ubuntu-24.04' || github.repository_owner != 'manaflow-ai' && 'macos-15' || (needs.decide.outputs.fast_build == 'true' && 'blacksmit
GitHub-hosted runner label: .github/workflows/nightly.yml:1600:    runs-on: ${{ inputs.nightly_next_unsigned_run != '' && 'ubuntu-24.04' || github.repository_owner != 'manaflow-ai' && 'macos-26' || needs.decide.outputs.fast_build != 'true' && github.run_
GitHub-hosted runner label: .github/workflows/intent-familiar-review.yml:21:    runs-on: ubuntu-24.04

... and 1 more failing tests in the run log.
Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Run canonical CMUX CI guard profile'

Validate package test selection

Test: python3 tests/test_ci_select_package_tests.py

Traceback (most recent call last):
  File "/home/runner/_work/cmux/cmux/tests/test_ci_select_package_tests.py", line 229, in <module>
    sys.exit(main())
             ^^^^^^
  File "/home/runner/_work/cmux/cmux/tests/test_ci_select_package_tests.py", line 131, in main
    check_package_output_behavior()
  File "/home/runner/_work/cmux/cmux/tests/test_ci_select_package_tests.py", line 112, in check_package_output_behavior
    assert result.returncode == 1 and count == 1, f"{package}: real error incorrectly tolerated: {result.returncode}"
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: GhosttyFixture: real error incorrectly tolerated: 1

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate package test selection'

Lint every workflow

.github/workflows/cmux-next-defaults-guard.yml:94:28: expected scalar node for string value but found alias node with "" tag [syntax-check]
   |
94 |           sparse-checkout: *defaults-paths
   |                            ^~~~~~~~~~~~~~~
.github/workflows/cmux-next-frame-pacing.yml:198:33: property "resolve-ref" is not defined in object type {build: {outputs: {}; result: string}; gate: {outputs: {run: string; tag: string}; result: string}} [expression]
    |
198 |       CMUX_PERF_SOURCE_SHA: ${{ needs.resolve-ref.outputs.sha }}
    |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.github/workflows/nightly.yml:34:3: maximum number of inputs for "workflow_dispatch" event is 10 but 17 inputs are provided. see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#providing-inputs [events]
   |
34 |   workflow_dispatch:
   |   ^~~~~~~~~~~~~~~~~~

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Lint every workflow'

Validate the package test lane's single-suite phase

debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: want one build and two suites: ['swift package resolve --package-path Packages/macOS/Pkg', 'swift --version', 'swift build --build-tests -c debug -debug-info-format none --package-path Packages/macOS/Pkg', 'swift test -c debug -debug-info-format none --package-path Packages/macOS/Pkg --skip-build --filter ATests', 'swift test -c debug -debug-info-format none --package-path Packages/macOS/Pkg --skip-build --filter BTests']
debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 'swift package resolve --package-path Packages/macOS/Pkg' lacks '-debug-info-format none'
debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 'swift --version' lacks '-debug-info-format none'
debug info {}: want one build and two suites: ['swift package resolve --package-path Packages/macOS/Pkg', 'swift --version', 'swift build --build-tests -c debug -debug-info-format none --package-path Packages/macOS/Pkg', 'swift test -c debug -debug-info-format none --package-path Packages/macOS/Pkg --skip-build --filter ATests', 'swift test -c debug -debug-info-format none --package-path Packages/macOS/Pkg --skip-build --filter BTests']

... and 7 more failing tests in the run log.
Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate the package test lane'"'"'s single-suite phase'

Validate side-lane placement onto idle owned runners

  • CmuxNextWiring.test_every_mac_job_reads_the_placement
AssertionError: Items in the first set but not the second:
'fast-tier'
  • CmuxNextWiring.test_placement_starts_only_where_attempt_1_may_take_the_side_label
AssertionError: False is not true

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate side-lane placement onto idle owned runners'

Validate the persistent-pool rescue

Test: python3 tests/test_ci_owned_pool_rescue.py

  • SideLanes.test_a_completed_failed_blacksmith_retry_is_not_watched_again
AssertionError: True is not false
  • SideLanes.test_a_real_failure_on_cmux_next_attempt_two_is_not_rescued
AssertionError: 'rerun-failed' unexpectedly found in ['jobs:2', 'run', 'pull', 'run', 'rerun-failed']

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate the persistent-pool rescue'

Validate every workflow guard is wired

Test: python3 tests/test_ci_workflow_guards_are_wired.py

Traceback (most recent call last):
  File "/home/runner/_work/cmux/cmux/tests/test_ci_workflow_guards_are_wired.py", line 110, in <module>
    test_every_workflow_guard_is_run_by_a_workflow()
  File "/home/runner/_work/cmux/cmux/tests/test_ci_workflow_guards_are_wired.py", line 89, in test_every_workflow_guard_is_run_by_a_workflow
    assert not unwired, (
           ^^^^^^^^^^^
AssertionError: these tests read .github/workflows/ but no workflow runs them, so the invariants they assert are not enforced:
  tests/test_cmux_next_web_bundle_cache.py
  tests/test_cmux_tui_republish_same_commit.py
  tests/test_nightly_next_post_publish.py
Add a step to .github/workflows/ci-guards.yml, or add an entry to UNWIRED in this file explaining why not.

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate every workflow guard is wired'

Validate workflow write permissions are job-scoped

Test: python3 tests/test_ci_workflow_permissions_are_job_scoped.py

  • WorkflowPermissionTests.test_current_tree_passes
AssertionError: Lists differ: ['gallery-link.yml grants pull-requests: w[271 chars]hem'] != []

First list contains 2 additional elements.
First extra element 0:
'gallery-link.yml grants pull-requests: write at workflow level; set the top level to `{}` or read scopes and declare writes on the jobs that need them'

+ []
- ['gallery-link.yml grants pull-requests: write at workflow level; set the top '
-  'level to `{}` or read scopes and declare writes on the jobs that need them',
-  'intent-familiar-review.yml grants pull-requests: write at workflow level; '
-  'set the top level to `{}` or read scopes and declare writes on the jobs that '
-  'need them']

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate workflow write permissions are job-scoped'

... and 6 more failed steps in the run log.

Agents: python3 scripts/ci/guard_attribution.py fix applies the mechanical fixes locally.

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

CI fast guards failed on 3aba03cf58 (https://github.com/manaflow-ai/cmux/actions/runs/39210545943). It does not block the merge; a red guard merged into main breaks it for every open PR.

Check that push and pull_request path filters agree

Test: python3 tests/test_ci_workflow_path_filter_parity.py

Traceback (most recent call last):
  File "/home/runner/_work/cmux/cmux/tests/test_ci_workflow_path_filter_parity.py", line 245, in <module>
    test_push_and_pull_request_filter_the_same_files()
  File "/home/runner/_work/cmux/cmux/tests/test_ci_workflow_path_filter_parity.py", line 156, in test_push_and_pull_request_filter_the_same_files
    assert not drifted, (
           ^^^^^^^^^^^
AssertionError: Every workflow that filters both events must filter them identically, or declare the difference in EXEMPTIONS or PUSH_ONLY_PATTERNS with a reason.
backend.yml: push and pull_request filter different files
  paths: guarded on push, not on pull requests: scripts/cmux-next/release/prod-promote.ts, scripts/cmux-next/release/production-approvals.json
cmux-next.yml: push and pull_request filter different files
  paths: guarded on pull requests, not on push: .github/actions/**, .github/workflows/**, Assets.xcassets/**, Native/**, Resources/**, TunnelExtension/**, cmuxTests/**, cmuxUITests/**, daemon/**, docs/configuration.md, scripts/cmux-next/**, scripts/cmux-next/state-roots-baseline.tsv, scripts/whats-new/**, tests/**
  paths: guarded on push, not on pull requests: .github/workflows/nightly.yml, scripts/apple-developer-id-certs/**, scripts/ci/*nightly*, scripts/ci/download-run-artifact.py, scripts/ci/finalize-release.py, scripts/ci/publish-release-assets.py, scripts/ci/signing-keychain.sh, scripts/ci/update-release-tag.py, scripts/ci/upload-r2-object.py, scripts/import-apple-devel ...

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Check that push and pull_request path filters agree'

Validate local hook trust boundary

Test: python3 tests/test_preflight_trust.py

  • PreflightTrustTests.test_annotated_tag_push_does_not_execute_candidate_code
subprocess.CalledProcessError: Command '['bash', 'scripts/install-git-hooks.sh']' returned non-zero exit status 1.
  • PreflightTrustTests.test_hook_installation_has_no_candidate_code_runner
subprocess.CalledProcessError: Command '['bash', 'scripts/install-git-hooks.sh']' returned non-zero exit status 1.
  • PreflightTrustTests.test_push_does_not_execute_candidate_check_through_unchanged_wrapper
subprocess.CalledProcessError: Command '['bash', 'scripts/install-git-hooks.sh']' returned non-zero exit status 1.
  • PreflightTrustTests.test_push_does_not_execute_candidate_runner
subprocess.CalledProcessError: Command '['bash', 'scripts/install-git-hooks.sh']' returned non-zero exit status 1.

... and 1 more failing tests in the run log.
Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate local hook trust boundary'

Run canonical CMUX CI guard profile

GitHub-hosted runner label: .github/workflows/intent-familiar-review.yml:21:    runs-on: ubuntu-24.04
GitHub-hosted runner label: .github/workflows/cmux-next-split-guard.yml:13:    runs-on: ubuntu-24.04
GitHub-hosted runner label: .github/workflows/gallery-link.yml:23:    runs-on: ubuntu-24.04
GitHub-hosted runner label: .github/workflows/nightly.yml:1463:    runs-on: ${{ inputs.nightly_next_unsigned_run != '' && 'ubuntu-24.04' || github.repository_owner != 'manaflow-ai' && 'macos-15' || (needs.decide.outputs.fast_build == 'true' && 'blacksmit

... and 1 more failing tests in the run log.
Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Run canonical CMUX CI guard profile'

Validate package test selection

Test: python3 tests/test_ci_select_package_tests.py

Traceback (most recent call last):
  File "/home/runner/_work/cmux/cmux/tests/test_ci_select_package_tests.py", line 229, in <module>
    sys.exit(main())
             ^^^^^^
  File "/home/runner/_work/cmux/cmux/tests/test_ci_select_package_tests.py", line 131, in main
    check_package_output_behavior()
  File "/home/runner/_work/cmux/cmux/tests/test_ci_select_package_tests.py", line 112, in check_package_output_behavior
    assert result.returncode == 1 and count == 1, f"{package}: real error incorrectly tolerated: {result.returncode}"
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: GhosttyFixture: real error incorrectly tolerated: 1

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate package test selection'

Lint every workflow

.github/workflows/cmux-next-defaults-guard.yml:94:28: expected scalar node for string value but found alias node with "" tag [syntax-check]
   |
94 |           sparse-checkout: *defaults-paths
   |                            ^~~~~~~~~~~~~~~
.github/workflows/cmux-next-frame-pacing.yml:198:33: property "resolve-ref" is not defined in object type {build: {outputs: {}; result: string}; gate: {outputs: {run: string; tag: string}; result: string}} [expression]
    |
198 |       CMUX_PERF_SOURCE_SHA: ${{ needs.resolve-ref.outputs.sha }}
    |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.github/workflows/nightly.yml:34:3: maximum number of inputs for "workflow_dispatch" event is 10 but 17 inputs are provided. see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#providing-inputs [events]
   |
34 |   workflow_dispatch:
   |   ^~~~~~~~~~~~~~~~~~

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Lint every workflow'

Validate the package test lane's single-suite phase

debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: want one build and two suites: ['swift package resolve --package-path Packages/macOS/Pkg', 'swift --version', 'swift build --build-tests -c debug -debug-info-format none --package-path Packages/macOS/Pkg', 'swift test -c debug -debug-info-format none --package-path Packages/macOS/Pkg --skip-build --filter ATests', 'swift test -c debug -debug-info-format none --package-path Packages/macOS/Pkg --skip-build --filter BTests']
debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 'swift package resolve --package-path Packages/macOS/Pkg' lacks '-debug-info-format none'
debug info {'CMUX_SWIFT_TEST_DEBUG_INFO': 'none'}: 'swift --version' lacks '-debug-info-format none'
debug info {}: want one build and two suites: ['swift package resolve --package-path Packages/macOS/Pkg', 'swift --version', 'swift build --build-tests -c debug -debug-info-format none --package-path Packages/macOS/Pkg', 'swift test -c debug -debug-info-format none --package-path Packages/macOS/Pkg --skip-build --filter ATests', 'swift test -c debug -debug-info-format none --package-path Packages/macOS/Pkg --skip-build --filter BTests']

... and 7 more failing tests in the run log.
Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate the package test lane'"'"'s single-suite phase'

Validate side-lane placement onto idle owned runners

  • CmuxNextWiring.test_every_mac_job_reads_the_placement
AssertionError: Items in the first set but not the second:
'fast-tier'
  • CmuxNextWiring.test_placement_starts_only_where_attempt_1_may_take_the_side_label
AssertionError: False is not true

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate side-lane placement onto idle owned runners'

Validate every workflow guard is wired

Test: python3 tests/test_ci_workflow_guards_are_wired.py

Traceback (most recent call last):
  File "/home/runner/_work/cmux/cmux/tests/test_ci_workflow_guards_are_wired.py", line 110, in <module>
    test_every_workflow_guard_is_run_by_a_workflow()
  File "/home/runner/_work/cmux/cmux/tests/test_ci_workflow_guards_are_wired.py", line 89, in test_every_workflow_guard_is_run_by_a_workflow
    assert not unwired, (
           ^^^^^^^^^^^
AssertionError: these tests read .github/workflows/ but no workflow runs them, so the invariants they assert are not enforced:
  tests/test_cmux_next_web_bundle_cache.py
  tests/test_cmux_tui_republish_same_commit.py
  tests/test_nightly_next_post_publish.py
Add a step to .github/workflows/ci-guards.yml, or add an entry to UNWIRED in this file explaining why not.

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate every workflow guard is wired'

Validate the persistent-pool rescue

Test: python3 tests/test_ci_owned_pool_rescue.py

  • SideLanes.test_a_completed_failed_blacksmith_retry_is_not_watched_again
AssertionError: True is not false
  • SideLanes.test_a_real_failure_on_cmux_next_attempt_two_is_not_rescued
AssertionError: 'rerun-failed' unexpectedly found in ['jobs:2', 'run', 'pull', 'run', 'rerun-failed']

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate the persistent-pool rescue'

Validate required checks are bounded

Test: python3 tests/test_ci_required_checks_are_bounded.py

unbounded CI:
  - .github/workflows/cmux-next-batch.yml runs on pull_request with no concurrency group, so every push leaves the superseded run running
  - .github/workflows/cmux-next-split-guard.yml runs on pull_request with no concurrency group, so every push leaves the superseded run running

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate required checks are bounded'

... and 6 more failed steps in the run log.

Agents: python3 scripts/ci/guard_attribution.py fix applies the mechanical fixes locally. This comment is updated in place on each push.

…g' into feat/acpmux-session-pull-requests

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextAgentLanes/SidebarSessionTreeFeed.swift
#	Packages/macOS/CmuxNext/Sources/CmuxNextApp/AgentChatPullRequest.swift
…and only the URL it prints

Review fixes for #21823: a grep or echo mentioning gh pr create, or a chained
gh pr create && gh pr view 9, no longer records links. The command is parsed
(quotes, $( ), heredocs) and must be gh pr create after cd steps and env
prefixes; only the last output line that is just a pull URL is kept, preferring
rawOutput.stdout when the harness separates it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gh exits 1 when the branch already has a pull request and prints its URL;
that call opened nothing. Review nit from coord-2 on #21823.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo and others added 4 commits October 19, 2026 06:27
…g' into feat/acpmux-session-pull-requests

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextAgentActivity/AgentActivityLane.swift
#	cmux-tui/crates/acpmux/tests/fake_agent.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g' into feat/acpmux-session-pull-requests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A harness that streams gh's "already exists: URL" in an in_progress update before the failed one
recorded that pull request; the URL is now held per call and recorded on completed only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 19, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

teamleaderleo and others added 3 commits October 18, 2026 23:59
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Red: a bare & splits `2>&1` and `&>file` into two commands, and only cd may
precede gh, so the usual `git push && gh pr create --fill 2>&1` is never
recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 19, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

teamleaderleo and others added 2 commits October 19, 2026 00:06
A redirect's & (2>&1, >&2, &>file) no longer splits the command, and steps
joined to gh by && may precede it, so `git push && gh pr create --fill 2>&1`
records its pull request. ;, ||, | and & before gh still don't count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	cmux-tui/crates/acpmux/src/hub/views.rs
#	cmux-tui/crates/acpmux/src/protocol/session.rs
#	cmux-tui/crates/acpmux/src/store.rs
@cursor

cursor Bot commented Oct 19, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

…adge (#21959)

* cmux NEXT sidebar: pull request hover card on the session tree's PR badge

Hovering a row's pull request badge lists each pull request its sessions opened
(acpmux pullRequests plus the folder's branch PR), newest first: title, a state
pill (Conflict / Checks failed / Checks passing / Draft / Merged / Closed),
owner/repo #N and +adds -dels; a click opens it in the focused pane's browser.
It scrolls past eight lines. The badge gains a red failing status. Live state
comes from one batched gh api graphql read per refresh (aliased pullRequest
fields, 50 per query) instead of one gh pr view per URL. Debug Settings toggle
sidebar.pullRequestCard, on by default; native gallery entry with conflict,
checks-failed, mixed, all-merged and empty fixtures; strings in 21 locales.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request card review fixes

A failed GraphQL read keeps the last answers (only a null alias clears one);
the query pins --hostname github.com and other hosts fall back to gh pr view
per URL; the card follows its badge when the row is reused or its pull
requests change; a hidden badge never makes the panel and resets its hover;
the workspace card and the pull request card dismiss each other; rows take
first mouse; same-moment pull requests list by number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request card opens only PR pages; settled PRs are read once; failing reads back off

The card opens a URL only when it is https://github.com (or the user's GH_HOST)
/<owner>/<repo>/pull/<n> with nothing else. A merged or closed PR's URL is not
asked again; a URL whose read failed waits freshness doubled per failure, up to
16x. Failed checks show red before a conflict. Review fixes from coord-2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: import Foundation for the PR item host check (ProcessInfo, URLComponents)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): the PR store re-reads open PRs on its own, re-asks closed ones, and keeps answers on partial GraphQL errors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: re-read open PRs every five minutes while shown, re-ask closed ones, keep answers on partial GraphQL errors

Review findings on #21814/#21959: a badge never refreshed once its session went idle, so CI that
failed later stayed green; a reopened PR never came back; an SSO or resource-limit error nulled
good answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): only an open pull request keeps the sidebar's recheck

Red: a folder never settles, so with the tree on the store re-reads every
folder every five minutes, branches with no pull request included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): the sidebar rechecks only open pull requests, and forgets unshown ones

- The five-minute recheck stays armed only while the tree shows an open or
  draft pull request. A folder never settled, so before this every session
  folder was re-read with gh every five minutes, branches without a pull
  request included.
- Answers, read times and failure counts for folders and URLs the tree no
  longer shows are dropped on each pass instead of growing for the app's life.
- The card's state pill uses Metrics.chipCornerRadius, not a capsule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo merged commit bc24c7c into feat/next-sidebar-session-nesting Oct 19, 2026
@teamleaderleo
teamleaderleo deleted the feat/acpmux-session-pull-requests branch October 19, 2026 19:51
@greptile-apps

greptile-apps Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 0/5

[Medium impact] The change reaches session summaries and the macOS sidebar’s pull-request display.

The PR needs the ownership, test-access, collection-scan, and failing-test fixes before merging.

Findings

  1. P0 Sidebars share popup state ▶
  2. P0 Tests add a shipping API ▶
  3. P0 PR lists repeat full scans ▶
  4. P1 The Swift test disagrees ▶
  5. P2 Failed checks can look green ▶
  6. P2 Offline PRs stay missing ▶

Summary

Records PRs opened by session shell calls and includes them in the sidebar's PR count.

  • Sessions save the pull requests their successful create commands open.
  • Session tree badges count pull requests opened from any session branch.
  • Pull-request badges open a card with details for every listed pull request.

No tests or native UI captures were run during this review. Native AppKit capture was unavailable in the Linux environment.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A["Shell tool call and result"] --> B["Detect successful gh pr create"]
  B --> C["Save session PR URLs"]
  C --> D["Session summary"]
  D --> E["AgentActivityLane"]
  E --> F["Collect folders and opened URLs"]
  F --> G["Read PR details"]
  G --> H["Deduplicate by PR URL"]
  H --> I["Sidebar badge and hover card"]
Loading

Reviews (1) · Last reviewed commit: "cmux NEXT sidebar: pull request hover ca..." · Reviewed by Greptile

/// card leaves the badge to it (`WorkspaceHoverCardController.hoverCardHit`).
@MainActor
final class SidebarPullRequestCardController {
static let shared = SidebarPullRequestCardController()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P0 Sidebars share popup state

SidebarPullRequestCardController.shared adds a singleton that owns the panel, hovered badge, and open callback. This violates the repository rule requiring new runtime state to have a scoped, injectable owner. Construct the controller on the sidebar owner and pass it to the badge and workspace hover card.

Rule Used: Flag new ambient global state in production Swift: a top-level (file-scope) func used as API, a top-level mutable var or a stub class/struct holding a global flag/once-token, a caseless enum/empty struct used purely as a static func/static let namesp... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +206 to +207
/// Whether the five-minute recheck is pending.
var rechecks: Bool { recheck.isScheduled }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P0 Tests add a shipping API

rechecks exposes the private timer's state solely for tests. This violates the repository rule against test-only accessors under Sources/. Remove the wrapper, make recheck internal, and let the test read recheck.isScheduled through @testable import.

Rule Used: Flag Swift files under a production Sources path (matching /Sources/ and not under /Tests/) that add a test-only or debug-only seam: a #if DEBUG (or other test-build-guarded) extension/member exposing internal state only for tests or a debugg... (source)

static func asks(_ lanes: [AgentActivityLane]) -> [String] {
let folders = Set(lanes.compactMap(\.cwd)).sorted()
var opened: [String] = []
for url in lanes.flatMap(\.pullRequests) where !opened.contains(url) { opened.append(url) }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P0 PR lists repeat full scans

asks(_:) scans the growing opened array for every URL. Duplicate removal therefore takes quadratic work across a workspace's sessions, on the main actor whenever the feed updates. This violates the repository rule against nested scans of user-owned collections. Keep a Set<String> for membership and append only when insertion succeeds.

Suggested change
for url in lanes.flatMap(\.pullRequests) where !opened.contains(url) { opened.append(url) }
var seen: Set<String> = []
for url in lanes.flatMap(\.pullRequests) where seen.insert(url).inserted { opened.append(url) }

Rule Used: Flag production code that adds nested full-collection scans, per-target rescans for batch actions, repeated sort/filter/map work in hot UI/socket/search/process paths, in-memory joins that belong in the data store, or unbenchmarked slower algorithms ... (source)

#expect(seven.mergeable == "conflicting")
#expect(seven.checks == .init(state: "failing", passed: 1, total: 2))
#expect((seven.additions, seven.deletions) == (120, 8))
#expect(SidebarSessionTreeFeed.item(seven).state == .conflict)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 The Swift test disagrees

aGraphQLAnswerReadsAsGhPrViewDoes expects .conflict for a PR whose checks are failing. item(_:) deliberately returns .failing before checking for a conflict, so this assertion cannot pass. Change the expectation to .failing to match the documented red-before-orange behavior.

Suggested change
#expect(SidebarSessionTreeFeed.item(seven).state == .conflict)
#expect(SidebarSessionTreeFeed.item(seven).state == .failing)

Comment on lines +96 to +97
commits(last: 1) { nodes { commit { statusCheckRollup { contexts(first: 100) { nodes { __typename \
... on CheckRun { name status conclusion detailsUrl } ... on StatusContext { context state targetUrl } } } } } } }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Failed checks can look green

The new GraphQL query reads only the first 100 check results, then treats that page as the complete result. If a PR has more checks and a later check fails, the sidebar can show it as passing. Read the rollup's overall state for the badge, or fetch all pages before deciding that checks passed.

Comment on lines +195 to +198
for url in urls {
guard let answer = read[url] else {
failures[url, default: 0] += 1
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Offline PRs stay missing

A URL whose first read fails gets no automatic retry when none of the other shown keys has an open answer. refresh(_:) cancels the timer before the read, and the failed read only updates the ignored failures dictionary. The badge can stay missing after the network recovers. Schedule a retry for unanswered URLs using their backoff, not only for URLs already known to be open.

teamleaderleo added a commit that referenced this pull request Oct 21, 2026
…essionTree, off by default) (#21814)

* cmux NEXT sidebar: session tree with pull request badges and keycaps (sidebar.sessionTree)

Claude desktop's Code tab, behind a setting that is off by default: a
workspace whose agent another workspace's agent started (acpmux spawnedBy)
lists under it on a thin guide line, each row carries its pull requests
(the branch PR of each session folder, as the chat header's chip reads it)
in a green, orange or purple badge, and the first rows show their
Command-number keycaps. The Command-1...9 order follows the drawn order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT media: the core tour turns on the session tree last

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 9bee46b

- regenerated webviews/src/pages/settings/generated/strings.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39176969610

* cmux NEXT sidebar: the session tree feed lives in CmuxNextAgentLanes

CmuxNextApp may not grow: the feed moves to CmuxNextAgentLanes (the app
passes its workspaces), and the gh pull request reader moves next to its
model in CmuxNextAgentPane so the chat chip and the sidebar share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT: CI target graph for CmuxNextAgentLanes' new dependencies

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: review fixes for the session tree

The moved gh reader's process holder carries its reviewed crash-allow
(only the thread-safe isRunning and terminate() cross threads), and the
sidebar asks each folder's pull request at most every five minutes, so
it never stalls the chat header's chip on the two shared gh slots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): settings exports for sidebar.sessionTree

Generated only: the fleet settings export (cmux-ci step
b05e67a26082185e11778f94, run-swift-testing-suites.sh export-settings at
7ad7c4e; CmuxNextSettings is unchanged since), the defaults snapshot
(defaults_guard.py snapshot) and the settings page strings (gen-strings.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT: CI target graph keeps CmuxNextAgentLanes' dependencies after the base merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): settings exports for sidebar.sessionTree after the base merge

Generated only: the fleet settings export (cmux-ci step
69f7e53cebcd649429577bcd, run-swift-testing-suites.sh export-settings at
215bf61; CmuxNextSettings is unchanged since), the defaults snapshot
(defaults_guard.py snapshot) and the settings page strings (gen-strings.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): lower the type-scale baseline for BrowserPopupPanel (base red: it no longer has raw font sizes)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next tour: turn sidebar.sessionTree back off after its step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): close the #require calls in the session tree lane test

The base merge in 08374a3 left both try #require(AgentActivityLane.of(...))
calls without their closing parenthesis, so CmuxNextAppTests did not build
(fast tier: "the tests do not build").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): the Markdown viewer test host implements FilePageHosting.openAppLink (base red: CmuxNextAppTests did not build)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at f2d34a4

- regenerated docs/mdm/com.manaflow.cmux.json
- regenerated docs/mdm/com.manaflow.cmux.plist
- regenerated docs/mdm/managed-preferences.md
- regenerated schemas/settings-export/settings-schema.sources.sha256
- regenerated schemas/settings/settings-schema.json
- regenerated Packages/macOS/CmuxNext/ci-target-graph.json
- regenerated web/data/cmux-shortcuts.generated.json
- regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts
- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39215245191

* acpmux: record the PRs each session opens; the sidebar session tree counts them all (#21823)

* acpmux: test that a session lists the pull requests gh pr create opened

Red: pullRequests is absent, so the summary has [] (no detector yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: record the pull requests each session opens; the sidebar session tree counts them

A tool call whose command runs `gh pr create` is remembered until it ends; the
github.com pull URLs its output prints join SessionMeta.pull_requests, once each,
and SessionSummary exposes them as optional pullRequests. CmuxNext parses them on
AgentActivityLane and SidebarSessionTreeFeed reads each with `gh pr view <url>`
beside the folder's branch PR, counting each pull request once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: count only a shell call whose whole command is gh pr create, and only the URL it prints

Review fixes for #21823: a grep or echo mentioning gh pr create, or a chained
gh pr create && gh pr view 9, no longer records links. The command is parsed
(quotes, $( ), heredocs) and must be gh pr create after cd steps and env
prefixes; only the last output line that is just a pull URL is kept, preferring
rawOutput.stdout when the harness separates it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: a failed gh pr create records no pull request

gh exits 1 when the branch already has a pull request and prints its URL;
that call opened nothing. Review nit from coord-2 on #21823.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): a gh pr create URL counts only once its call completes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: a gh pr create URL counts only when its call completes

A harness that streams gh's "already exists: URL" in an in_progress update before the failed one
recorded that pull request; the URL is now held per call and recorded on completed only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): AgentActivityLane.of takes no title

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): gh pr create counts after && steps and with 2>&1

Red: a bare & splits `2>&1` and `&>file` into two commands, and only cd may
precede gh, so the usual `git push && gh pr create --fill 2>&1` is never
recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: gh pr create counts after && steps and with 2>&1

A redirect's & (2>&1, >&2, &>file) no longer splits the command, and steps
joined to gh by && may precede it, so `git push && gh pr create --fill 2>&1`
records its pull request. ;, ||, | and & before gh still don't count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request hover card on the session tree's PR badge (#21959)

* cmux NEXT sidebar: pull request hover card on the session tree's PR badge

Hovering a row's pull request badge lists each pull request its sessions opened
(acpmux pullRequests plus the folder's branch PR), newest first: title, a state
pill (Conflict / Checks failed / Checks passing / Draft / Merged / Closed),
owner/repo #N and +adds -dels; a click opens it in the focused pane's browser.
It scrolls past eight lines. The badge gains a red failing status. Live state
comes from one batched gh api graphql read per refresh (aliased pullRequest
fields, 50 per query) instead of one gh pr view per URL. Debug Settings toggle
sidebar.pullRequestCard, on by default; native gallery entry with conflict,
checks-failed, mixed, all-merged and empty fixtures; strings in 21 locales.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request card review fixes

A failed GraphQL read keeps the last answers (only a null alias clears one);
the query pins --hostname github.com and other hosts fall back to gh pr view
per URL; the card follows its badge when the row is reused or its pull
requests change; a hidden badge never makes the panel and resets its hover;
the workspace card and the pull request card dismiss each other; rows take
first mouse; same-moment pull requests list by number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request card opens only PR pages; settled PRs are read once; failing reads back off

The card opens a URL only when it is https://github.com (or the user's GH_HOST)
/<owner>/<repo>/pull/<n> with nothing else. A merged or closed PR's URL is not
asked again; a URL whose read failed waits freshness doubled per failure, up to
16x. Failed checks show red before a conflict. Review fixes from coord-2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: import Foundation for the PR item host check (ProcessInfo, URLComponents)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): the PR store re-reads open PRs on its own, re-asks closed ones, and keeps answers on partial GraphQL errors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: re-read open PRs every five minutes while shown, re-ask closed ones, keep answers on partial GraphQL errors

Review findings on #21814/#21959: a badge never refreshed once its session went idle, so CI that
failed later stayed green; a reopened PR never came back; an SSO or resource-limit error nulled
good answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): only an open pull request keeps the sidebar's recheck

Red: a folder never settles, so with the tree on the store re-reads every
folder every five minutes, branches with no pull request included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): the sidebar rechecks only open pull requests, and forgets unshown ones

- The five-minute recheck stays armed only while the tree shows an open or
  draft pull request. A folder never settled, so before this every session
  folder was re-read with gh every five minutes, branches without a pull
  request included.
- Answers, read times and failure counts for folders and URLs the tree no
  longer shows are dropped on each pass instead of growing for the app's life.
- The card's state pill uses Metrics.chipCornerRadius, not a capsule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): take the base's generated files and Markdown test host after the merge

The merge kept stale copies of the shortcuts export, the devbox rebake list
and the agent-session spec, and a second openAppLink in the test host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* generated: restore base's shortcuts export (a stale autofix dropped paletteKey.toggleActions)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): keep the base's hook status conflicts (#21813) after the stack merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 4e00ea2

- vp fmt webviews/src/sidebar/SidebarPullRequestCard.gallery.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39315375602

* chore(next): rerun the PR autofix so it regenerates the settings exports

The last head was an autofix commit, which the autofix skips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 014d9cc

- regenerated docs/mdm/com.manaflow.cmux.json
- regenerated docs/mdm/com.manaflow.cmux.plist
- regenerated docs/mdm/managed-preferences.md
- regenerated schemas/settings-export/settings-schema.sources.sha256
- regenerated schemas/settings/settings-schema.json
- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39321341168

* test(next): the sidebar's PR recheck reads only open pull requests

A recheck called refresh(shown), which also re-read every folder without a
pull request every five minutes while one open PR showed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): the sidebar's PR recheck reads only open pull requests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 4dd489e

- docs/mdm/com.manaflow.cmux.json
- docs/mdm/com.manaflow.cmux.plist
- docs/mdm/managed-preferences.md
- schemas/defaults/cmux-next.json
- schemas/settings-export/settings-schema.sources.sha256
- schemas/settings/settings-schema.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39359164603

* cmux NEXT sidebar: the pull requests card is an owned card in the one hover card window

Base deleted SidebarUpdateNotesPanel (5cac4de, one morphing sidebar card), which the
PR card used as its window, so the merge did not compile. The card now shows through
HoverCardCoordinator.presentOwned, as the update highlights do: it takes clicks and
scrolls, a workspace card that shows morphs into it, and a row the pointer reaches next
takes the card back, so the manual dismissals between the two cards go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: AppServices reads the gh path from the moved PR reader (package access)

Base made CmuxNextApp call AgentChatPullRequestReader.findGitHubCLI(); this PR moves the
reader into CmuxNextAgentPane, where the method was internal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Leave ci-target-graph.json to the base autofix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
teamleaderleo added a commit that referenced this pull request Oct 21, 2026
…on tree (#22323)

* cmux NEXT sidebar: session tree with pull request badges and keycaps (sidebar.sessionTree)

Claude desktop's Code tab, behind a setting that is off by default: a
workspace whose agent another workspace's agent started (acpmux spawnedBy)
lists under it on a thin guide line, each row carries its pull requests
(the branch PR of each session folder, as the chat header's chip reads it)
in a green, orange or purple badge, and the first rows show their
Command-number keycaps. The Command-1...9 order follows the drawn order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT media: the core tour turns on the session tree last

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 9bee46b

- regenerated webviews/src/pages/settings/generated/strings.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39176969610

* cmux NEXT sidebar: the session tree feed lives in CmuxNextAgentLanes

CmuxNextApp may not grow: the feed moves to CmuxNextAgentLanes (the app
passes its workspaces), and the gh pull request reader moves next to its
model in CmuxNextAgentPane so the chat chip and the sidebar share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT: CI target graph for CmuxNextAgentLanes' new dependencies

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: review fixes for the session tree

The moved gh reader's process holder carries its reviewed crash-allow
(only the thread-safe isRunning and terminate() cross threads), and the
sidebar asks each folder's pull request at most every five minutes, so
it never stalls the chat header's chip on the two shared gh slots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): settings exports for sidebar.sessionTree

Generated only: the fleet settings export (cmux-ci step
b05e67a26082185e11778f94, run-swift-testing-suites.sh export-settings at
7ad7c4e; CmuxNextSettings is unchanged since), the defaults snapshot
(defaults_guard.py snapshot) and the settings page strings (gen-strings.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT: CI target graph keeps CmuxNextAgentLanes' dependencies after the base merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): settings exports for sidebar.sessionTree after the base merge

Generated only: the fleet settings export (cmux-ci step
69f7e53cebcd649429577bcd, run-swift-testing-suites.sh export-settings at
215bf61; CmuxNextSettings is unchanged since), the defaults snapshot
(defaults_guard.py snapshot) and the settings page strings (gen-strings.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): lower the type-scale baseline for BrowserPopupPanel (base red: it no longer has raw font sizes)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next tour: turn sidebar.sessionTree back off after its step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): close the #require calls in the session tree lane test

The base merge in 08374a3 left both try #require(AgentActivityLane.of(...))
calls without their closing parenthesis, so CmuxNextAppTests did not build
(fast tier: "the tests do not build").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): the Markdown viewer test host implements FilePageHosting.openAppLink (base red: CmuxNextAppTests did not build)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at f2d34a4

- regenerated docs/mdm/com.manaflow.cmux.json
- regenerated docs/mdm/com.manaflow.cmux.plist
- regenerated docs/mdm/managed-preferences.md
- regenerated schemas/settings-export/settings-schema.sources.sha256
- regenerated schemas/settings/settings-schema.json
- regenerated Packages/macOS/CmuxNext/ci-target-graph.json
- regenerated web/data/cmux-shortcuts.generated.json
- regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts
- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39215245191

* acpmux: record the PRs each session opens; the sidebar session tree counts them all (#21823)

* acpmux: test that a session lists the pull requests gh pr create opened

Red: pullRequests is absent, so the summary has [] (no detector yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: record the pull requests each session opens; the sidebar session tree counts them

A tool call whose command runs `gh pr create` is remembered until it ends; the
github.com pull URLs its output prints join SessionMeta.pull_requests, once each,
and SessionSummary exposes them as optional pullRequests. CmuxNext parses them on
AgentActivityLane and SidebarSessionTreeFeed reads each with `gh pr view <url>`
beside the folder's branch PR, counting each pull request once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: count only a shell call whose whole command is gh pr create, and only the URL it prints

Review fixes for #21823: a grep or echo mentioning gh pr create, or a chained
gh pr create && gh pr view 9, no longer records links. The command is parsed
(quotes, $( ), heredocs) and must be gh pr create after cd steps and env
prefixes; only the last output line that is just a pull URL is kept, preferring
rawOutput.stdout when the harness separates it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: a failed gh pr create records no pull request

gh exits 1 when the branch already has a pull request and prints its URL;
that call opened nothing. Review nit from coord-2 on #21823.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): a gh pr create URL counts only once its call completes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: a gh pr create URL counts only when its call completes

A harness that streams gh's "already exists: URL" in an in_progress update before the failed one
recorded that pull request; the URL is now held per call and recorded on completed only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): AgentActivityLane.of takes no title

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): gh pr create counts after && steps and with 2>&1

Red: a bare & splits `2>&1` and `&>file` into two commands, and only cd may
precede gh, so the usual `git push && gh pr create --fill 2>&1` is never
recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: gh pr create counts after && steps and with 2>&1

A redirect's & (2>&1, >&2, &>file) no longer splits the command, and steps
joined to gh by && may precede it, so `git push && gh pr create --fill 2>&1`
records its pull request. ;, ||, | and & before gh still don't count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request hover card on the session tree's PR badge (#21959)

* cmux NEXT sidebar: pull request hover card on the session tree's PR badge

Hovering a row's pull request badge lists each pull request its sessions opened
(acpmux pullRequests plus the folder's branch PR), newest first: title, a state
pill (Conflict / Checks failed / Checks passing / Draft / Merged / Closed),
owner/repo #N and +adds -dels; a click opens it in the focused pane's browser.
It scrolls past eight lines. The badge gains a red failing status. Live state
comes from one batched gh api graphql read per refresh (aliased pullRequest
fields, 50 per query) instead of one gh pr view per URL. Debug Settings toggle
sidebar.pullRequestCard, on by default; native gallery entry with conflict,
checks-failed, mixed, all-merged and empty fixtures; strings in 21 locales.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request card review fixes

A failed GraphQL read keeps the last answers (only a null alias clears one);
the query pins --hostname github.com and other hosts fall back to gh pr view
per URL; the card follows its badge when the row is reused or its pull
requests change; a hidden badge never makes the panel and resets its hover;
the workspace card and the pull request card dismiss each other; rows take
first mouse; same-moment pull requests list by number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request card opens only PR pages; settled PRs are read once; failing reads back off

The card opens a URL only when it is https://github.com (or the user's GH_HOST)
/<owner>/<repo>/pull/<n> with nothing else. A merged or closed PR's URL is not
asked again; a URL whose read failed waits freshness doubled per failure, up to
16x. Failed checks show red before a conflict. Review fixes from coord-2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: import Foundation for the PR item host check (ProcessInfo, URLComponents)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): the PR store re-reads open PRs on its own, re-asks closed ones, and keeps answers on partial GraphQL errors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: re-read open PRs every five minutes while shown, re-ask closed ones, keep answers on partial GraphQL errors

Review findings on #21814/#21959: a badge never refreshed once its session went idle, so CI that
failed later stayed green; a reopened PR never came back; an SSO or resource-limit error nulled
good answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): only an open pull request keeps the sidebar's recheck

Red: a folder never settles, so with the tree on the store re-reads every
folder every five minutes, branches with no pull request included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): the sidebar rechecks only open pull requests, and forgets unshown ones

- The five-minute recheck stays armed only while the tree shows an open or
  draft pull request. A folder never settled, so before this every session
  folder was re-read with gh every five minutes, branches without a pull
  request included.
- Answers, read times and failure counts for folders and URLs the tree no
  longer shows are dropped on each pass instead of growing for the app's life.
- The card's state pill uses Metrics.chipCornerRadius, not a capsule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): take the base's generated files and Markdown test host after the merge

The merge kept stale copies of the shortcuts export, the devbox rebake list
and the agent-session spec, and a second openAppLink in the test host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* generated: restore base's shortcuts export (a stale autofix dropped paletteKey.toggleActions)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): keep the base's hook status conflicts (#21813) after the stack merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 4e00ea2

- vp fmt webviews/src/sidebar/SidebarPullRequestCard.gallery.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39315375602

* chore(next): rerun the PR autofix so it regenerates the settings exports

The last head was an autofix commit, which the autofix skips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 014d9cc

- regenerated docs/mdm/com.manaflow.cmux.json
- regenerated docs/mdm/com.manaflow.cmux.plist
- regenerated docs/mdm/managed-preferences.md
- regenerated schemas/settings-export/settings-schema.sources.sha256
- regenerated schemas/settings/settings-schema.json
- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39321341168

* test(next): the sidebar's PR recheck reads only open pull requests

A recheck called refresh(shown), which also re-read every folder without a
pull request every five minutes while one open PR showed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): the sidebar's PR recheck reads only open pull requests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(next): sidebar chat history and pinning under the session tree

With sidebar.sessionTree on (default unchanged), chats stay in the sidebar
after their workspace closes: a Pinned block above the workspaces and a calm,
foldable Earlier block under them, 20 at a time with Show More. Built from
acpmux's chat index metadata only (no transcript loads), read lazily so it
stays cheap with thousands of chats. A row resumes its chat through the shared
Open Chat path; its right-click menu pins or unpins it. Pins and the Earlier
fold persist in UserDefaults. No project grouping.

hq#2569 gap #6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar chat history: Pin/Unpin runs a registered action; Earlier animates through Motion.sidebar

- SidebarChatHistoryView placed the blocks with Motion.animate(.move, in:),
  which base removed (every animation names its category now); it uses
  Motion.sidebar.animate, like the rest of the sidebar.
- A history row's Pin/Unpin menu ran its own selector, which
  check-menu-actions.py rejects. The row's menu now comes from
  ActionRegistry.makeContextMenu(for: .sidebarItem, target: sidebar-item:<chat
  key>, entries: [sidebar.chat.togglePin]), retitled Pin or Unpin for the
  row's chat. SidebarChatHistoryFeed.bind binds the action to the history's
  pins; the App binds it where it starts the feed. New action title in all
  21 locales; generated action exports are left to the autofix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray

* SidebarChatHistoryFeed: import Observation publicly

The public @observable class's conformance needs the module imported
publicly (fleet app compile, job bea228bd3cfa).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray

* Keep the sidebar string catalog in base's key order (content unchanged)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray

* Sidebar history: the row menu hook lives on SidebarChatHistory, not an all-static enum

The conventions check refused the namespace enum SidebarChatHistoryMenu. The menu
factory is now an instance hook on the history the rows already read, like onOpen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray

* Sidebar history: a group fold lays the list out again while the history blocks show

The fold splice moved the rows but not pinnedChatsY/earlierChatsY, so opening a group
pushed workspace rows under Earlier and closing one left a gap. With either block placed,
SidebarLayout.fold now declines and the list takes its full layout. Test included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
teamleaderleo added a commit that referenced this pull request Oct 22, 2026
…l-ups (#21836)

* cmux NEXT sidebar: session tree with pull request badges and keycaps (sidebar.sessionTree)

Claude desktop's Code tab, behind a setting that is off by default: a
workspace whose agent another workspace's agent started (acpmux spawnedBy)
lists under it on a thin guide line, each row carries its pull requests
(the branch PR of each session folder, as the chat header's chip reads it)
in a green, orange or purple badge, and the first rows show their
Command-number keycaps. The Command-1...9 order follows the drawn order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT media: the core tour turns on the session tree last

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 9bee46b

- regenerated webviews/src/pages/settings/generated/strings.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39176969610

* cmux NEXT sidebar: the session tree feed lives in CmuxNextAgentLanes

CmuxNextApp may not grow: the feed moves to CmuxNextAgentLanes (the app
passes its workspaces), and the gh pull request reader moves next to its
model in CmuxNextAgentPane so the chat chip and the sidebar share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT: CI target graph for CmuxNextAgentLanes' new dependencies

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: review fixes for the session tree

The moved gh reader's process holder carries its reviewed crash-allow
(only the thread-safe isRunning and terminate() cross threads), and the
sidebar asks each folder's pull request at most every five minutes, so
it never stalls the chat header's chip on the two shared gh slots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): settings exports for sidebar.sessionTree

Generated only: the fleet settings export (cmux-ci step
b05e67a26082185e11778f94, run-swift-testing-suites.sh export-settings at
7ad7c4e; CmuxNextSettings is unchanged since), the defaults snapshot
(defaults_guard.py snapshot) and the settings page strings (gen-strings.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT: CI target graph keeps CmuxNextAgentLanes' dependencies after the base merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): settings exports for sidebar.sessionTree after the base merge

Generated only: the fleet settings export (cmux-ci step
69f7e53cebcd649429577bcd, run-swift-testing-suites.sh export-settings at
215bf61; CmuxNextSettings is unchanged since), the defaults snapshot
(defaults_guard.py snapshot) and the settings page strings (gen-strings.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): the session tree draws every level at its depth (red)

The session tree flattens a child's child to depth 1 under the top parent.
These tests draw a three-level chain and a fifty-workspace tree four levels
deep and expect each row at its real depth, subtrees unbroken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(next): the session tree draws the real spawn tree, folds, and rolls up

With sidebar.sessionTree on, a workspace draws at its real depth under the
workspace whose agent started it, at any depth, instead of flattening every
descendant to depth 1 under the top parent. A parent gets a chevron that
folds its subtree (kept across launches), and a folded parent shows chips
for its hidden sessions: waiting on you, failed, running, finished unread.

The states come from acpmux's lanes (input, failed or lost, working, idle),
with an unread idle workspace counted as finished unread. A workspace with
no lane reads its row: a hook's waiting or error, its work, its unread mark.
Unread alone never counts as waiting on you.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): a patched row keeps its tree chevron and roll-up

A row's content patch rebuilds its content from the workspace; it kept the
tree depth already, and now keeps the parent's fold and roll-up too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(next): leave workspace groups' rows as the session tree drew them

Folds and roll-ups apply to a section's loose rows only; a workspace
group's members keep #21814's drawing (Leo: group code is another lane's).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): session tree review fixes

- Search finds a match inside a folded subtree (folds open while filtering,
  as groups do).
- A folded parent's roll-up follows a hidden session's patch: the list lays
  out again.
- A selected session never hides in a fold: selecting one opens the folds
  above it.
- The layout computes each list's children once, not once per row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): lower the type-scale baseline for BrowserPopupPanel (base red: it no longer has raw font sizes)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next tour: turn sidebar.sessionTree back off after its step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): close the #require calls in the session tree lane test

The base merge in 08374a3 left both try #require(AgentActivityLane.of(...))
calls without their closing parenthesis, so CmuxNextAppTests did not build
(fast tier: "the tests do not build").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): a filter draws a folded parent open; a deep chain keeps every link

Red: while filtering, treeOrder draws a folded parent's children but treeFold
still reads the fold, so the parent shows a collapsed chevron and a roll-up
above its own children.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): session tree folds open under a filter; reveal lives with the folds

- A filter opens every fold for the chevrons and roll-ups too, not only the
  order, so a matching folded parent no longer shows a collapsed chevron above
  its own children.
- treeChildren remembers each workspace's answer (one pass over a chain), and
  the layout hands treeOrder the children it already computed.
- A patch to a row nobody draws relays the list only when a folded ancestor
  hides it (hidesInFold), not whenever any fold exists.
- reveal(_:) moves here from the needs-you tray PR: the selection path in this
  PR calls it, so the branch did not build on its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): the Markdown viewer test host implements FilePageHosting.openAppLink (base red: CmuxNextAppTests did not build)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at f2d34a4

- regenerated docs/mdm/com.manaflow.cmux.json
- regenerated docs/mdm/com.manaflow.cmux.plist
- regenerated docs/mdm/managed-preferences.md
- regenerated schemas/settings-export/settings-schema.sources.sha256
- regenerated schemas/settings/settings-schema.json
- regenerated Packages/macOS/CmuxNext/ci-target-graph.json
- regenerated web/data/cmux-shortcuts.generated.json
- regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts
- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39215245191

* acpmux: record the PRs each session opens; the sidebar session tree counts them all (#21823)

* acpmux: test that a session lists the pull requests gh pr create opened

Red: pullRequests is absent, so the summary has [] (no detector yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: record the pull requests each session opens; the sidebar session tree counts them

A tool call whose command runs `gh pr create` is remembered until it ends; the
github.com pull URLs its output prints join SessionMeta.pull_requests, once each,
and SessionSummary exposes them as optional pullRequests. CmuxNext parses them on
AgentActivityLane and SidebarSessionTreeFeed reads each with `gh pr view <url>`
beside the folder's branch PR, counting each pull request once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: count only a shell call whose whole command is gh pr create, and only the URL it prints

Review fixes for #21823: a grep or echo mentioning gh pr create, or a chained
gh pr create && gh pr view 9, no longer records links. The command is parsed
(quotes, $( ), heredocs) and must be gh pr create after cd steps and env
prefixes; only the last output line that is just a pull URL is kept, preferring
rawOutput.stdout when the harness separates it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: a failed gh pr create records no pull request

gh exits 1 when the branch already has a pull request and prints its URL;
that call opened nothing. Review nit from coord-2 on #21823.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): a gh pr create URL counts only once its call completes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: a gh pr create URL counts only when its call completes

A harness that streams gh's "already exists: URL" in an in_progress update before the failed one
recorded that pull request; the URL is now held per call and recorded on completed only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): AgentActivityLane.of takes no title

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): gh pr create counts after && steps and with 2>&1

Red: a bare & splits `2>&1` and `&>file` into two commands, and only cd may
precede gh, so the usual `git push && gh pr create --fill 2>&1` is never
recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: gh pr create counts after && steps and with 2>&1

A redirect's & (2>&1, >&2, &>file) no longer splits the command, and steps
joined to gh by && may precede it, so `git push && gh pr create --fill 2>&1`
records its pull request. ;, ||, | and & before gh still don't count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request hover card on the session tree's PR badge (#21959)

* cmux NEXT sidebar: pull request hover card on the session tree's PR badge

Hovering a row's pull request badge lists each pull request its sessions opened
(acpmux pullRequests plus the folder's branch PR), newest first: title, a state
pill (Conflict / Checks failed / Checks passing / Draft / Merged / Closed),
owner/repo #N and +adds -dels; a click opens it in the focused pane's browser.
It scrolls past eight lines. The badge gains a red failing status. Live state
comes from one batched gh api graphql read per refresh (aliased pullRequest
fields, 50 per query) instead of one gh pr view per URL. Debug Settings toggle
sidebar.pullRequestCard, on by default; native gallery entry with conflict,
checks-failed, mixed, all-merged and empty fixtures; strings in 21 locales.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request card review fixes

A failed GraphQL read keeps the last answers (only a null alias clears one);
the query pins --hostname github.com and other hosts fall back to gh pr view
per URL; the card follows its badge when the row is reused or its pull
requests change; a hidden badge never makes the panel and resets its hover;
the workspace card and the pull request card dismiss each other; rows take
first mouse; same-moment pull requests list by number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: pull request card opens only PR pages; settled PRs are read once; failing reads back off

The card opens a URL only when it is https://github.com (or the user's GH_HOST)
/<owner>/<repo>/pull/<n> with nothing else. A merged or closed PR's URL is not
asked again; a URL whose read failed waits freshness doubled per failure, up to
16x. Failed checks show red before a conflict. Review fixes from coord-2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: import Foundation for the PR item host check (ProcessInfo, URLComponents)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): the PR store re-reads open PRs on its own, re-asks closed ones, and keeps answers on partial GraphQL errors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT sidebar: re-read open PRs every five minutes while shown, re-ask closed ones, keep answers on partial GraphQL errors

Review findings on #21814/#21959: a badge never refreshed once its session went idle, so CI that
failed later stayed green; a reopened PR never came back; an SSO or resource-limit error nulled
good answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): only an open pull request keeps the sidebar's recheck

Red: a folder never settles, so with the tree on the store re-reads every
folder every five minutes, branches with no pull request included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): the sidebar rechecks only open pull requests, and forgets unshown ones

- The five-minute recheck stays armed only while the tree shows an open or
  draft pull request. A folder never settled, so before this every session
  folder was re-read with gh every five minutes, branches without a pull
  request included.
- Answers, read times and failure counts for folders and URLs the tree no
  longer shows are dropped on each pass instead of growing for the app's life.
- The card's state pill uses Metrics.chipCornerRadius, not a capsule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): take the base's generated files and Markdown test host after the merge

The merge kept stale copies of the shortcuts export, the devbox rebake list
and the agent-session spec, and a second openAppLink in the test host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* generated: restore base's shortcuts export (a stale autofix dropped paletteKey.toggleActions)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): keep the base's hook status conflicts (#21813) after the stack merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 4e00ea2

- vp fmt webviews/src/sidebar/SidebarPullRequestCard.gallery.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39315375602

* chore(next): rerun the PR autofix so it regenerates the settings exports

The last head was an autofix commit, which the autofix skips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 014d9cc

- regenerated docs/mdm/com.manaflow.cmux.json
- regenerated docs/mdm/com.manaflow.cmux.plist
- regenerated docs/mdm/managed-preferences.md
- regenerated schemas/settings-export/settings-schema.sources.sha256
- regenerated schemas/settings/settings-schema.json
- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39321341168

* test(next): the sidebar's PR recheck reads only open pull requests

A recheck called refresh(shown), which also re-read every folder without a
pull request every five minutes while one open PR showed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): the sidebar's PR recheck reads only open pull requests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): a closed parent's fold leaves the saved fold list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): the session tree saves only folds the tree still has

The saved fold list grew with every folded workspace that later closed. Each
save now keeps only folds on a live parent (once the feed has passed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 4dd489e

- docs/mdm/com.manaflow.cmux.json
- docs/mdm/com.manaflow.cmux.plist
- docs/mdm/managed-preferences.md
- schemas/defaults/cmux-next.json
- schemas/settings-export/settings-schema.sources.sha256
- schemas/settings/settings-schema.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39359164603

* chore(next): the session tree's root walk ends on its own condition, not `while true`

The concurrency scanner reads `while true` as a loop that can spin. The walk is
the same: it stops at a known answer, a cycle, or a workspace whose parent is
not in the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d2c-88e4-7fc2-b58e-9a55abdaced8&host=cosmic-gray

* refactor(next): the session tree's loose-row drawing lives in SidebarLayout+SessionTree

SidebarLayout.swift was already over the 400-line god-file limit on the base
(455 lines) and this PR grew it to 472, which `check-no-godfiles --base` refuses.
`treeRows` now builds each loose row's depth, chevron and roll-up beside
`treeOrder`, and `emit` takes that row. Same order, folds and roll-ups; the
file is 454 lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d2c-88e4-7fc2-b58e-9a55abdaced8&host=cosmic-gray

* chore(next): the sidebar catalog keeps base's key order after the merge

The key-wise merge driver kept this branch's key order, which moved keys that
#22983 placed on base. Same keys and values; the catalog's diff against base is
now only the seven session tree strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d2c-88e4-7fc2-b58e-9a55abdaced8&host=cosmic-gray

* cmux-next: a chevron folds right away; a reveal keeps other folds

The chevron (and its VoiceOver actions) only toggled the tree and left the
redraw to observation, which the calm-movement hold deferred until the
pointer left the sidebar; it now reloads the list like the tabs
disclosure. reveal() no longer prunes the saved folds, since at launch it
runs for the restored selection before every lane has loaded. The sidebar
catalog goes back to base's key order, so its diff is only the new keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d2c-88e4-7fc2-b58e-9a55abdaced8&host=cosmic-gray

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
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