Skip to content

Add cmux comments list — read-only CLI surface for diff review comments - #9604

Merged
austinywang merged 14 commits into
manaflow-ai:mainfrom
haung921209:comments-list-cli
Aug 6, 2026
Merged

austinywang merged 14 commits into
manaflow-ai:mainfrom
haung921209:comments-list-cli

Conversation

@haung921209

@haung921209 haung921209 commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

Implements the CLI part of #9508.

Summary

  • Adds a comments command family with one read-only subcommand:
    cmux comments list [--repo <path>] [--all] [--json].
  • Backed by a new socket v2 method comments.list handled by the app, so
    DiffCommentStore stays the single authority. The store keeps a per-repo
    in-memory cache, so reading the store file from another process can go stale
    after in-app edits; routing reads through the app avoids exposing the key
    derivation and cache semantics as an external contract.
  • Default output lists pending comments only; --all includes comments already
    delivered to an agent through a TextBox submission (consumedAt set).
  • --repo defaults to the git repository containing the current directory.

Why

Agents that open a diff for human review (cmux diff --unstaged before
publishing a doc) currently have no supported way to ask "which review comments
exist for this repo, and which are still unaddressed?" The push direction
already works (pending pool → next TextBox submission); this adds the pull
direction for agent-driven loops. Details and motivation: #9508.

Changes

9 files, +38/−1:

  • Sources/TerminalController+CommentsCommands.swift (new) — comments.list
    handler: canonicalizes repo_root through DiffCommentStore, filters
    consumed comments unless include_consumed, and reuses the
    DiffCommentsBridge JSON mapping (plus a consumedAt field).
  • Sources/TerminalController.swift — dispatch case in
    v2LegacyMainActorResponse (next to the other app-side handlers) +
    comments.list in v2Capabilities().
  • Sources/Panels/DiffCommentsBridge.swift — commentJSON visibility
    private → internal for reuse (same wire shape as the webview bridge).
  • CLI/CMUXCLI+Comments.swift (new) — comments namespace: list resolves
    the git toplevel (default: cwd), calls comments.list, prints a human
    summary or --json.
  • CLI/cmux.swift, CLI/CMUXCLI+CommandSuggestions.swift — command dispatch,
    subcommandUsage, root usage line, known-command list.
  • docs/cli-contract.md — Top-Level Commands row + no-socket help probe.
  • cmuxTests/CommentsListPayloadTests.swift (new) — covers the reply shape.
    The handler delegates to a pure commentsListPayload(comments:repoRoot: includeConsumed:) so the filtering is testable without a socket.
  • Resources/Localizable.xcstrings — cli.comments.usage (en).
  • cmux.xcodeproj/project.pbxproj — file wiring, normalized with
    scripts/normalize-pbxproj.py (scripts/check-pbxproj.sh and
    scripts/lint-pbxproj-test-wiring.sh pass).

Hosting the handler app-side (rather than on ControlCommandCoordinator) kept
the diff minimal; happy to move it onto the coordinator with a seam context if
that is preferred.

Testing

Local, on macOS 26.5 / Xcode 26.6:

  • scripts/reload.sh --tag spike builds the app and CLI: BUILD SUCCEEDED.
  • scripts/check-pbxproj.sh and scripts/lint-pbxproj-test-wiring.sh pass.
  • xcodebuild -scheme cmux-unit -only-testing:cmuxTests/CommentsListPayloadTests -only-testing:cmuxTests/DiffCommentStoreTests test: TEST SUCCEEDED,
    11 cases (4 new + the 7 existing store cases).
  • tests/test_cli_contract_help.py against the built CLI: 151 help probes
    and 1 negative probe pass
    , including the new cmux comments --help.
  • cmux comments --help returns exit 0 with no socket available.
  • Against a running debug app, with comments saved for a scratch repository:
    • cmux comments list prints the anchored comment (file, line, anchor text,
      message); --json returns the same records.
    • Default output lists only pending comments; --all additionally lists a
      comment carrying consumedAt, labelled consumed.
    • --repo pointed at a non-git directory exits 1 with a clear message.

Not run to completion: the full cmux-unit suite. A partial run reached
632 passed / 35 failed, and every failure was in unrelated Browser suites
(theme settings, web view lifecycle, portals, remote store) — the per-suite
health that #8565 already tracks. No comments or diff test failed. Happy to
post a full-suite run if that is useful for review.


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


Summary by cubic

Adds a read-only cmux comments list CLI backed by v2 comments.list to list per-repo diff review comments. Extracts DiffComment and its wire mapping into the CmuxDiffComments package and fixes payload integration across the webview and socket to use a shared mapper.

  • New Features

    • cmux comments list [--repo <path>] [--all] [--json] (alias: ls): resolves the git toplevel (default: cwd), strictly rejects unknown flags/positionals and option-like --repo values, prints a concise summary or JSON; defaults to pending, --all includes consumedAt.
    • CLI text is localized across 20 locales and resolved via CMUXDiffViewerLocalization (uses the enclosing app bundle); header selects correct singular/plural and uses grammar-safe plural phrasing.
    • App-side comments.list: canonicalizes repo_root and builds replies with DiffCommentPayload (one ISO8601 formatter per reply).
  • Refactors

    • Introduced CmuxDiffComments package exposing DiffComment and DiffCommentPayload; adopted by the webview bridge, socket handler, CLI, and tests to keep mapping consistent and UI-free.
    • Made DiffCommentPayload instance-based to avoid per-comment allocations; fixed integration so both the webview and socket call it consistently, with tests covering the shared lifecycle JSON (including consumedAt).

Written for commit f9ecd31. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added cmux comments list (alias: ls) to view repository review comments.
    • Supports pending or all comments, JSON or human-readable output, repository selection, and detailed file ranges, anchors, states, and messages.
    • Added the comments.list capability for integrations.
  • Documentation

    • Added CLI help, usage text, contract documentation, and localized messages for comment listing.
  • Tests

    • Added coverage for filtering, timestamps, anchors, and empty results.

Review comments saved in the diff viewer already reach agents through the
TextBox pending pool (push). This adds the pull direction: a read-only CLI
that asks the running app for a repository's saved comments, so external
tools never depend on the store's key derivation or in-memory cache.

- comments.list v2 method: canonicalizes repo_root via DiffCommentStore and
  delegates to a pure commentsListPayload(comments:repoRoot:includeConsumed:)
  so the reply shape is testable without a socket
- cmux comments list [--repo <path>] [--all] [--json]: resolves the git
  toplevel (default: cwd), prints a human summary or JSON
- CommentsListPayloadTests: default listing omits consumed comments,
  include_consumed adds them with an ISO8601 consumedAt, anchor fields are
  preserved, an empty store reports zero
- cli-contract.md: command table row + no-socket help probe
@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a cmux comments list CLI namespace backed by a comments.list socket method. The command resolves Git repositories, filters consumed comments by default, supports JSON output, and returns comment ranges, anchors, states, messages, and timestamps.

Changes

Repository Comment Listing

Layer / File(s) Summary
Comment model and payload contract
Packages/macOS/CmuxDiffComments/*, Sources/DiffCommentStore.swift, Sources/Panels/DiffCommentsBridge.swift
Adds the shared DiffComment model and payload builders. Storage and bridge serialization use the shared module.
Socket listing endpoint
Sources/TerminalController+CommentsCommands.swift, Sources/TerminalController.swift
Adds comments.list dispatch and capability advertisement. The endpoint validates repositories, filters comments, and returns structured data.
CLI command surface
CLI/*Comments.swift, CLI/CMUXCLI+CommandSuggestions.swift, CLI/cmux.swift, Resources/Localizable.xcstrings, docs/cli-contract.md
Adds command discovery, list/ls handling, repository selection, JSON output, help text, localization, and CLI contract documentation.
Payload validation and project wiring
cmuxTests/CommentsListPayloadTests.swift, cmuxTests/DiffCommentStoreTests.swift, cmux.xcodeproj/project.pbxproj
Adds tests for filtering, timestamps, anchors, empty results, and counts. Registers the package and new source files in the Xcode targets.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CMUXCLI
  participant TerminalController
  participant DiffCommentStore
  participant DiffCommentPayload
  CMUXCLI->>TerminalController: Request comments.list with repo_root and includeConsumed
  TerminalController->>DiffCommentStore: Retrieve repository comments
  TerminalController->>DiffCommentPayload: Build filtered response
  DiffCommentPayload-->>CMUXCLI: Return repository, count, and comments
Loading

Possibly related issues

Possibly related PRs

  • manaflow-ai/cmux#8736: Both PRs register an additional top-level CLI command for recognition and typo suggestions.

Suggested reviewers: lawrencecchen


Important

Pre-merge checks failed

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

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Swiftpm Lockfiles ❌ Error The PR adds the CmuxDiffComments Xcode package reference, but the root Xcode Package.resolved is unchanged and no Package.resolved file appears in the PR diff. Include the root Xcode Package.resolved diff with the cmux.xcodeproj package-reference change; add package-local resolution files when standalone dependencies change.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (23 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed The new model is a pure SwiftPM value type with explicit Sendable conformance; the mapper is not Sendable/shared, and store, bridge, and comments handler remain MainActor-isolated.
Cmux Swift Blocking Runtime ✅ Passed The PR adds no semaphore, wait, sleep, delayed dispatch, polling, main-queue sync, or lock; it only calls the unchanged CLIProcessRunner for synchronous git-root resolution.
Cmux Browser Automation Off-Main ✅ Passed The PR adds only comments.list to the main-actor switch and capabilities; it changes no browser.* routing, worker policy, WebKit waits, or browser policy tests.
Cmux Expensive Synchronous Load ✅ Passed The diff adds no agent-history loader or transcript scan; comments.list reads only the per-repository DiffCommentStore cache/file, while the existing synchronous store load is unchanged.
Cmux Cache Substitution Correctness ✅ Passed The PR does not replace a fresh read: DiffCommentStore cache and its cold Data(contentsOf:) load are unchanged; new code only lists cached store data through a read-only handler.
Cmux No Hacky Sleeps ✅ Passed The complete feature diff changes only Swift, localization, Xcode project, tests, and docs; it adds no TypeScript, JavaScript, shell, or non-Swift runtime delay code.
Cmux Algorithmic Complexity ✅ Passed New comment listing uses one linear pass in DiffCommentPayload.list (line 48) and one render pass in CLI line 158; no nested scans or sorting were added. Bridge pool rescans predate this diff.
Cmux Swift Concurrency ✅ Passed The complete Swift diff adds no DispatchQueue, DispatchGroup, Combine, completion-handler, or fire-and-forget Task patterns; changed bridge code only uses a synchronous payload mapper.
Cmux Swift @Concurrent ✅ Passed The feature diff adds no async, @concurrent, or nonisolated async Swift code; new comments.list and payload methods are synchronous, and existing @MainActor dispatch remains unchanged.
Cmux Swift Package Boundaries ✅ Passed DiffComment and DiffCommentPayload are in the Foundation-only CmuxDiffComments SwiftPM target; changed app files are socket/WebKit/CLI glue, and DiffCommentStore persistence was only adapted.
Cmux Swift Logging ✅ Passed The only added print calls are in CLI/CMUXCLI+Comments.swift and render the intended comments command output; no runtime diagnostics, Logger misuse, NSLog, or secret logging was added.
Cmux User-Facing Error Privacy ✅ Passed The added errors and localized copy use generic repository, argument, and socket messages; scans found no vendor names, secrets, credentials, raw upstream errors, or prohibited implementation details.
Cmux Full Internationalization ✅ Passed New CLI/socket prose uses CMUXDiffViewerLocalization or String(localized:), and all 13 new catalog keys have translated entries for all 20 supported locales; no web surface changed.
Cmux Swiftui State Layout ✅ Passed The PR adds no SwiftUI imports, views, observation wrappers, geometry readers, lazy/list rows, or render-time state writes; DiffCommentsBridge is an AppKit/WebKit bridge.
Cmux Architecture Rethink ✅ Passed The diff adds no timing repair, polling, locks, observers, or side-channel state; CLI and socket use the existing DiffCommentStore, while DiffCommentPayload removes duplicate JSON mapping.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR adds no standalone window code or auxiliary-window identifiers; added Swift lines contain no window APIs, and scripts/lint_auxiliary_window_close_shortcuts.py passes.
Cmux Source Artifacts ✅ Passed All 15 changed paths are source, tests, localization, project configuration, or CLI documentation; no artifact-like paths or binary changes appear in the net diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed Changed production Sources add no test/debug guards or test-named members; the new public comment model and payload are used by DiffCommentStore, DiffCommentsBridge, and comments.list.
Cmux No Ambient Global State ✅ Passed New behavior is on CMUXCLI/TerminalController methods; DiffCommentPayload is constructable with instance json/list methods. Only new static is constant commentsUsage, with no new singleton or globa...
Title check ✅ Passed The title clearly and concisely identifies the new read-only cmux comments list CLI surface.
Description check ✅ Passed The description covers the change, rationale, implementation, testing, documentation, and known limitations; the demo and checklist sections are not completed.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

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

Inline comments:
In `@CLI/CMUXCLI`+Comments.swift:
- Around line 37-42: Update the comments list argument handling around
parseOption("--repo") to validate remainder before constructing params or
calling comments.list. Permit only the supported --all flag, and reject any
other -- option, preserving the existing include_consumed behavior for valid
input.

In `@Resources/Localizable.xcstrings`:
- Around line 35957-35967: Add localization entries for every locale represented
elsewhere in the Localizable.xcstrings catalog under cli.comments.usage,
retaining entries even when their value intentionally uses the accepted English
fallback. Keep the existing English text unchanged and ensure the key has
complete coverage for all supported locales.

In `@Sources/TerminalController`+CommentsCommands.swift:
- Line 26: Update the parameter parsing in the comments command around
includeConsumed to distinguish an absent include_consumed key from a present
non-Boolean value. Preserve false as the default only when the parameter is
absent; return invalid_params for malformed values such as strings, and add a
regression test covering this behavior.
- Around line 38-47: Update commentsListPayload and
DiffCommentsBridge.commentJSON to accept and reuse a single ISO8601DateFormatter
for the entire response. Remove formatter creation from the per-comment mapper
path, and construct listed with one pass while preserving the existing
consumedAt filtering and serialization behavior.
- Around line 22-25: Update the comments.list handler around canonicalRepoRoot
and commentsListPayload to validate repo_root with git rev-parse --show-toplevel
before reading comments. Reject paths that are not within a valid repository,
and use the resolved Git top-level path for both the response payload and
DiffCommentStore.shared.comments lookup instead of the normalized input path.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 587f8485-6334-49ff-8fed-eb86437eff30

📥 Commits

Reviewing files that changed from the base of the PR and between 4cf7cc3 and f8299cd.

📒 Files selected for processing (10)
  • CLI/CMUXCLI+CommandSuggestions.swift
  • CLI/CMUXCLI+Comments.swift
  • CLI/cmux.swift
  • Resources/Localizable.xcstrings
  • Sources/Panels/DiffCommentsBridge.swift
  • Sources/TerminalController+CommentsCommands.swift
  • Sources/TerminalController.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/CommentsListPayloadTests.swift
  • docs/cli-contract.md

Comment thread CLI/CMUXCLI+Comments.swift
Comment thread Resources/Localizable.xcstrings Outdated
Comment thread Sources/TerminalController+CommentsCommands.swift
Comment thread Sources/TerminalController+CommentsCommands.swift
Comment thread Sources/TerminalController+CommentsCommands.swift Outdated
…wn flags

- Localizable.xcstrings: add the ja entry for cli.comments.usage and add the
  socket.comments.missingRepoRoot key, which the handler referenced but the
  catalog never defined. Both keys now match the en+ja coverage that the
  other cli.*.usage and socket.* keys use.
- DiffCommentsBridge: add commentJSON(_:formatter:) so a caller mapping many
  comments allocates one ISO8601DateFormatter per reply instead of one per
  comment; the existing single-comment signature delegates to it.
- cmux comments list: reject unrecognized -- options instead of ignoring
  them, so a typo cannot read as a supported request.
@haung921209

Copy link
Copy Markdown
Contributor Author

Pushed 14fdf2a addressing the review.

Fixed

  • cli.comments.usage now has ja alongside en, and socket.comments.missingRepoRoot is defined — the handler referenced that key but the catalog never had it, so it was silently using its defaultValue. Good catch; that was a real bug rather than only coverage.
  • One ISO8601DateFormatter per reply via a new commentJSON(_:formatter:) overload.
  • comments list rejects unrecognized -- options instead of ignoring them.

Discussing rather than changing (details in the threads)

  • Strict typing for include_consumed: four neighbouring handlers coerce optional bools the same way, so I would rather do that as one consistent change than a local exception.
  • git rev-parse validation inside comments.list: it would mean a synchronous subprocess on the main actor, which no handler in that layer does today. Offered two non-forking alternatives.

Verification after the fixes, on macOS 26.5 / Xcode 26.6:

  • scripts/reload.sh build: BUILD SUCCEEDED; check-pbxproj.sh and lint-pbxproj-test-wiring.sh pass.
  • tests/test_cli_contract_help.py: 151 probes + 1 negative pass.
  • CommentsListPayloadTests + DiffCommentStoreTests: TEST SUCCEEDED, 11 cases.
  • Live against a running app: unknown flag exits 1 before the request; default lists 1 pending; --all lists 2 with the consumed label; --json serializes consumedAt.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
CLI/CMUXCLI+Comments.swift (1)

78-90: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Fail closed on an invalid comments.list payload.

printCommentsListPayload accepts malformed payloads and renders output with default values. If the response is not a top-level result with a comments array, or a comment is missing required fields, report a CLI error instead of returning 0 lines, an empty repo, or a line at 0. Pending comments intentionally omit consumedAt; do not add a separate null handling path for that field.

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

In `@CLI/CMUXCLI`+Comments.swift around lines 78 - 90, Update
printCommentsListPayload to validate the top-level result, require a comments
array, and require each comment’s required file, line, and repository fields
instead of substituting defaults such as “?”, an empty repository, or line 0;
report a CLI error and stop processing when validation fails. Preserve the
existing pending-state behavior where consumedAt is absent, without adding
separate null handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLI/CMUXCLI`+Comments.swift:
- Around line 37-43: Update the remainder validation in the comments-list option
parsing around parseOption and includeConsumed to reject every token except the
supported --all flag, including unexpected positional arguments. Preserve the
existing unknown-option error path and perform this validation before resolving
the repository.

In `@Resources/Localizable.xcstrings`:
- Around line 36082-36098: The localization entries cli.comments.usage and
socket.comments.missingRepoRoot are incomplete. Update both keys in
Resources/Localizable.xcstrings at lines 36082-36098 and 229905-229921 to
include reviewed entries for all 20 supported locales, preserving the existing
comments.list and repo_root content for socket.comments.missingRepoRoot.

In `@Sources/Panels/DiffCommentsBridge.swift`:
- Around line 227-236: Update the browser comments.list response to create a
single ISO8601DateFormatter and map each comment through
commentJSON(_:formatter:), avoiding the per-comment wrapper allocation. Keep the
commentJSON(_:) wrapper unchanged for single-comment responses.

---

Outside diff comments:
In `@CLI/CMUXCLI`+Comments.swift:
- Around line 78-90: Update printCommentsListPayload to validate the top-level
result, require a comments array, and require each comment’s required file,
line, and repository fields instead of substituting defaults such as “?”, an
empty repository, or line 0; report a CLI error and stop processing when
validation fails. Preserve the existing pending-state behavior where consumedAt
is absent, without adding separate null handling.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a7a62943-9cc2-4186-b0f6-3ed68acd39b5

📥 Commits

Reviewing files that changed from the base of the PR and between f8299cd and 14fdf2a.

📒 Files selected for processing (4)
  • CLI/CMUXCLI+Comments.swift
  • Resources/Localizable.xcstrings
  • Sources/Panels/DiffCommentsBridge.swift
  • Sources/TerminalController+CommentsCommands.swift

Comment thread CLI/CMUXCLI+Comments.swift
Comment thread Resources/Localizable.xcstrings
Comment thread Sources/Panels/DiffCommentsBridge.swift Outdated
cli.comments.usage and socket.comments.missingRepoRoot now carry entries for
all 20 locales the catalog supports. The reviewed en and ja values are
unchanged; the remaining 18 locales use the accepted English fallback, which
is how 229 of the catalog's other fully-covered keys are written.
@haung921209

Copy link
Copy Markdown
Contributor Author

Pushed 86311bc.

Conceding the localization point: .coderabbit.yaml requires every locale already supported by the touched catalog for catalog additions, and its pass condition only covers existing untranslated strings. My earlier argument leaned on exactly that debt, so it did not hold. Both new keys now carry all 20 locales — reviewed en and ja unchanged, the other 18 using the accepted English fallback that 229 of the catalog's fully-covered keys already use.

Current state of the review:

  • Fixed: locale coverage (86311bc), unknown-flag rejection and one formatter per reply (14fdf2a), plus the missing socket.comments.missingRepoRoot key that the review surfaced.
  • Withdrawn by the reviewer: strict include_consumed typing (four neighbouring handlers share the pattern; left for a repo-wide follow-up) and git rev-parse validation in the handler (synchronous subprocess on the main actor). I also dropped my own .git-stat alternative — it would not resolve the canonical Git root or cover worktrees and GIT_DIR layouts.

Verification on the pushed tree: BUILD SUCCEEDED, check-pbxproj.sh and lint-pbxproj-test-wiring.sh pass, tests/test_cli_contract_help.py 151 probes + 1 negative pass, CommentsListPayloadTests + DiffCommentStoreTests 11 cases pass, and the live paths (unknown flag exits 1, default lists pending only, --all adds the consumed entry with its consumedAt) behave as before.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@Resources/Localizable.xcstrings`:
- Around line 230016-230019: Update the localized string entry for
socket.comments.missingRepoRoot in Localizable.xcstrings to use product-facing
wording such as “A repository path is required.” for every supported locale,
removing internal identifiers like comments.list and repo_root while preserving
the existing translation structure and metadata.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 693cffb9-1e15-4472-92cb-e40744eaa9d1

📥 Commits

Reviewing files that changed from the base of the PR and between 14fdf2a and 86311bc.

📒 Files selected for processing (1)
  • Resources/Localizable.xcstrings

Comment thread Resources/Localizable.xcstrings Outdated
…bridge formatter

- comments list now rejects every unrecognized remainder token, not only ones
  starting with --, so a stray positional cannot be silently ignored.
- The webview bridge's comments.list response builds one ISO8601DateFormatter
  and passes it to commentJSON, matching the socket response.
- Route the command's user-facing text through the catalog: nine new
  cli.comments.* keys for the errors and list output, each covering all 20
  locales with the reviewed ja value and the accepted English fallback.
@haung921209

Copy link
Copy Markdown
Contributor Author

Pushed 4b7e990, and I have replied on every thread so the open/closed state is explicit.

Fixed in this round

  • comments list rejects every unrecognized remainder token, not only ones starting with --. My previous guard let cmux comments list unexpected through; it now exits 1 with the supported-flags message.
  • The webview bridge's comments.list response reuses one ISO8601DateFormatter too, so both response paths allocate one per reply rather than one per comment.
  • The command's own user-facing text now goes through the catalog: nine new cli.comments.* keys for the errors and list output, covering all 20 locales. This was the second half of the internationalization check — it flagged both the catalog entry and untranslated CLI output, and I had only fixed the first.

Discussing rather than changing

  • socket.comments.missingRepoRoot wording: the rule's forbidden list is vendor/provider/infra/credential detail, and 11 of the catalog's 57 socket.* messages already name a parameter or method ("Missing or invalid surface_id", "Missing or invalid window_id"). The CLI also fails with its own localized error before ever sending the request, so this string only reaches a direct socket caller. Details in the thread; happy to reword across all 20 locales if you disagree.

Closed from my side (no action pending): include_consumed typing, git rev-parse validation, and both localization threads.

Verification on 4b7e990: BUILD SUCCEEDED; check-pbxproj.sh passes; tests/test_cli_contract_help.py 151 probes + 1 negative pass; CommentsListPayloadTests + DiffCommentStoreTests 11 cases pass; live paths re-checked — stray positional and --al both exit 1, default lists pending only, --all adds the consumed entry, --json still carries count and consumedAt.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
Sources/Panels/DiffCommentsBridge.swift (1)

228-237: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Include consumedAt in the shared comment payload.

comments.list serializes comments through this mapper at Lines 169-170, but the mapper omits DiffComment.consumedAt. The CLI renderer in CLI/CMUXCLI+Comments.swift uses comment["consumedAt"] to display pending or consumed. As a result, cmux comments list --all labels every returned comment as pending.

Add consumedAt when it is present.

Proposed fix
         if let endSide = comment.endSide {
             json["endSide"] = endSide
         }
+        if let consumedAt = comment.consumedAt {
+            json["consumedAt"] = formatter.string(from: consumedAt)
+        }
         return json
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Panels/DiffCommentsBridge.swift` around lines 228 - 237, Update the
shared comment payload mapper commentJSON(_:formatter:) to include
DiffComment.consumedAt when it is non-nil, preserving the existing omission
behavior when absent. This ensures comments.list provides the consumedAt value
used by the CLI renderer.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLI/CMUXCLI`+Comments.swift:
- Around line 40-50: Validate the parsed repoOption immediately after
parseOption in the comments-list argument handling: if it begins with “--”,
reject it with a CLIError stating that --repo requires a path. Keep the existing
remainder validation and repository resolution unchanged, while allowing
dash-prefixed paths that do not start with “--”.
- Around line 111-118: Update the comments list header localization in the
surrounding comments-list flow to select a plural-specific key based on
comments.count, using cli.comments.list.header.one for a single comment and
cli.comments.list.header.other otherwise. Replace the literal comment(s)
localization string while preserving the existing count and repoRoot formatting
arguments.

---

Outside diff comments:
In `@Sources/Panels/DiffCommentsBridge.swift`:
- Around line 228-237: Update the shared comment payload mapper
commentJSON(_:formatter:) to include DiffComment.consumedAt when it is non-nil,
preserving the existing omission behavior when absent. This ensures
comments.list provides the consumedAt value used by the CLI renderer.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8fb21b41-5ec6-49c1-a5e7-290b6a429c74

📥 Commits

Reviewing files that changed from the base of the PR and between 86311bc and 4b7e990.

📒 Files selected for processing (3)
  • CLI/CMUXCLI+Comments.swift
  • Resources/Localizable.xcstrings
  • Sources/Panels/DiffCommentsBridge.swift

Comment thread CLI/CMUXCLI+Comments.swift
Comment thread CLI/CMUXCLI+Comments.swift Outdated
- parseOption takes the token after --repo verbatim, so `--repo --all` would
  resolve a repository named "--all" and hand it to git as a path. Reject a
  value starting with --, pointing at ./-name for dash-prefixed paths.
- Replace the literal "comment(s)" header with cli.comments.list.header.one
  and .other selected by count, matching how cli.memory.output.processCount is
  written. Both keys cover all 20 locales.
…trings

- Replace the English fallbacks with real translations for all 18 remaining
  locales across the 13 keys this PR adds. The internationalization rule lists
  copied English as an unacceptable way to fill a locale slot, so matching the
  catalog's existing fallback habit was not enough for new keys. CLI tokens and
  format specifiers are preserved verbatim.
- socket.comments.missingRepoRoot now reads "A repository path is required."
  The pre-merge privacy check wants API identifiers out of user-facing text;
  the CLI already fails with its own message first, so a direct socket caller
  loses nothing it cannot get from the invalid_params code.
- Document the four comments CLI helpers.
@haung921209

Copy link
Copy Markdown
Contributor Author

Pushed 05419f4 for the pre-merge checks. Two resolved, one I would like to discuss.

Full Internationalization — fixed. You were right and my earlier reasoning was wrong twice over. full-internationalization.md lists "copied English … used to satisfy a locale slot" as unacceptable, so matching the catalog's existing fallback habit did not clear the bar for new keys. All 13 keys this PR adds now carry real translations for every locale — ar, bs, da, de, es, fr, it, km, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, zh-Hant — with CLI tokens (--repo, --all, --json, list) and format specifiers (%@, %1\$lld) preserved verbatim. Verified: no locale value equals its en value, and every key has all 20 locales.

User-Facing Error Privacy — changed to your wording. socket.comments.missingRepoRoot now reads "A repository path is required." in all 20 locales, and the defaultValue in code matches. For the record, the inline reviewer had verified my counter-argument and withdrawn this finding, so the gate and the thread disagreed; I went with the gate because the cost is small — the CLI validates and fails with its own localized message before ever sending the request, so only a direct socket caller sees this string, and that caller still gets invalid_params to identify the field.

Docstring Coverage — fixed. Documented the four comments CLI helpers.

Swift Package Boundaries — I would rather not do this here. The check asks for Packages/macOS/CmuxDiffComments exposing a public DiffComment plus a pure mapper. The condition it detects is real, but it predates this PR: DiffComment, DiffCommentStore, DiffCommentsBridge, and DiffCommentSubmissionPool all live in the app target today, and TextBoxInput consumes them there. What this PR added beside them is one nonisolated static mapper plus its tests. Extracting the package means moving pre-existing types across a target boundary and updating the bridge, the submission pool, TextBoxInput, the existing DiffCommentStoreTests, and the Xcode project — a refactor whose risk has nothing to do with comments.list.

Two options, whichever you prefer:

  1. I move commentsListPayload off TerminalController into its own small type inside the app target now, keeping the diff contained, and file the package extraction as a follow-up covering all four pre-existing types.
  2. I do the full CmuxDiffComments extraction in this PR if you would rather it land together — say so and I will, with the caveat that the diff grows well beyond the CLI surface.

Verification on 05419f4: BUILD SUCCEEDED; check-pbxproj.sh and lint-pbxproj-test-wiring.sh pass; tests/test_cli_contract_help.py 151 probes + 1 negative pass; CommentsListPayloadTests + DiffCommentStoreTests 11 cases pass.

@haung921209

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

Both surfaces that serialize review comments now share one type that touches
neither AppKit nor controller state: TerminalController keeps only the socket
dispatch, DiffCommentsBridge keeps only the WebKit glue, and the mapping and
consumed-filtering live in DiffCommentPayload with the tests pointed at it.

This also makes the two response paths structurally identical, so the webview
reply cannot drift back to allocating a formatter per comment. If the project
wants this behind a SwiftPM package, it is now a file move rather than a
refactor.
@haung921209

Copy link
Copy Markdown
Contributor Author

Pushed fc5ef0d — I went ahead with option 1 from my last comment rather than leave the boundary finding sitting.

Sources/DiffCommentPayload.swift now owns the wire mapping for review comments. TerminalController keeps only the socket dispatch, DiffCommentsBridge keeps only the WebKit glue, and the JSON mapping plus the consumed-filtering live in one type that touches neither AppKit nor controller state. The tests point at it directly.

Two things this bought beyond tidiness:

  • The webview and socket replies now serialize through the same code, so the "one formatter per comment" problem you flagged on the bridge path cannot come back by drift.
  • If you do want Packages/macOS/CmuxDiffComments, it is now a file move plus package wiring rather than a refactor. For sizing: DiffComment is referenced in five files total (DiffCommentStore, DiffCommentsBridge, the socket handler, and the two test files), and TextBoxInput reaches the store without naming the type, so the extraction is smaller than I first assumed.

Still your call whether the package lands here or as a follow-up — I did not create it, because it would move types that predate this PR and I would rather a maintainer sanction that.

Verification on fc5ef0d: BUILD SUCCEEDED; check-pbxproj.sh and lint-pbxproj-test-wiring.sh (651 test files) pass; tests/test_cli_contract_help.py 151 probes + 1 negative pass; CommentsListPayloadTests + DiffCommentStoreTests 11 cases pass.

@haung921209

Copy link
Copy Markdown
Contributor Author

On the two remaining errors — I read both rule files before answering, and I think both are pointing at pre-existing structure rather than something this PR introduces. Happy to be overruled on either.

Expensive Synchronous Load. swift-expensive-sync-load.md scopes itself to "heavy synchronous agent-history disk, JSON, transcript, trajectory, or syscall loads", with RestorableAgentSessionIndex.load() as the canonical case and "any large JSON/JSONL file whose size grows with agent history" as the class. DiffCommentStore is a different shape:

  • One file per repository, holding review comments a person typed in the diff viewer. It grows with human authoring in one repo, not with agent history.
  • It is already bounded to the target key, which is what the rule's own remedy asks for ("Bound the scan to the focused workspace, surface, session, or target key as early as practical").
  • The decode is cache-guarded — one read per repo per process.

It is also not a path this PR put on the main actor. DiffCommentsBridge is @MainActor and already calls store.comments(repoRoot:) on every diff-viewer load, and TextBoxInput reaches the same store from UI code. My handler is a third caller of that same accessor. Notably the first review round evaluated this check as ✅ on the same code, with the reasoning "comments.list uses the per-repository cached DiffCommentStore, whose disk decode is cache-guarded".

There is also a correctness reason not to move only my caller off-main: the store's cache is what keeps the socket reply consistent with the bridge's writes. A second, background reader would mean two caches over one file, and a stale read is a real failure mode here — I hit it twice while testing, where an externally edited store file stayed invisible until the app restarted. If the project wants this read off-main, it should move for all three callers together, with the bridge's pending-pool registration staying on @MainActor. I am glad to do that as a follow-up.

Swift Package Boundaries. The ask has grown to include the model and the persistence boundary — DiffComment and DiffCommentStore — which are app-target types this PR did not add. fc5ef0d already made the pure part a standalone DiffCommentPayload, so the extraction is now a file move plus package wiring. I would still rather a maintainer sanction moving pre-existing types across a target boundary in a PR whose subject is a CLI surface. Say the word and I will do it here.

Docstring Coverage (warning): every function this PR adds carries a /// doc comment — the four comments CLI helpers, the socket handler, and all three DiffCommentPayload methods. If the 0.00% figure is counting something else (test methods, or the whole changed-file set), tell me what it measures and I will cover it.

@haung921209

Copy link
Copy Markdown
Contributor Author

@coderabbitai could you re-evaluate the two remaining pre-merge errors against the evidence in my previous comment, and tell me plainly whether you want me to implement them in this PR?

Specifically:

  1. Expensive Synchronous Load — your first review round evaluated this same code as ✅ ("comments.list uses the per-repository cached DiffCommentStore, whose disk decode is cache-guarded"), and swift-expensive-sync-load.md scopes the rule to loads that grow with agent history. DiffCommentsBridge and TextBoxInput already reach this @MainActor store; my handler is a third caller. Moving only my caller off-main would put two caches over one file, which is a stale-read bug I reproduced during testing. Do you still want a change, and if so should it cover all three callers?

  2. Swift Package Boundaries — after fc5ef0d the pure mapping is already isolated in DiffCommentPayload. The remaining ask moves DiffComment and DiffCommentStore, both pre-existing app-target types, into a new SwiftPM target. Confirm you want that inside this PR and I will do it; otherwise I will file it as a follow-up.

If you agree these are out of scope for a CLI-surface PR, please say so explicitly so a maintainer reading the gate knows the errors were considered rather than ignored.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Candidate rule files =='
fd -t f . | rg 'swift-expensive-sync-load\.md|swift-package-boundaries\.md|DiffCommentPayload\.swift|DiffCommentStore\.swift|DiffCommentsBridge\.swift|TerminalController.*Comments.*\.swift|TextBoxInput.*\.swift|Package\.swift$'

echo '== Source outlines =='
for f in \
  Sources/DiffCommentPayload.swift \
  Sources/DiffCommentStore.swift \
  Sources/Panels/DiffCommentsBridge.swift \
  Sources/TerminalController+CommentsCommands.swift
do
  if [ -f "$f" ]; then
    echo "--- $f"
    ast-grep outline "$f" --items all || true
  fi
done

echo '== Rule text =='
while IFS= read -r f; do
  echo "--- $f"
  sed -n '1,240p' "$f"
done < <(fd -t f . | rg 'swift-expensive-sync-load\.md|swift-package-boundaries\.md')

echo '== Relevant call sites and type declarations =='
rg -n -C 5 -P '\b(DiffCommentStore|DiffCommentPayload|commentsListPayload|commentJSON|comments\s*\(repoRoot:|comments\.list|TextBoxInput)\b' \
  Sources cmuxTests CLI 2>/dev/null || true

echo '== Swift package manifests =='
fd -t f '^Package\.swift$' . -x sh -c 'echo "--- $1"; sed -n "1,220p" "$1"' sh {} \;

Length of output: 6167


🏁 Script executed:

#!/bin/bash
set -u

echo '== Locate rule files, including hidden paths =='
find . -type f \( -name 'swift-expensive-sync-load.md' -o -name 'swift-package-boundaries.md' \) -print 2>/dev/null || true

echo '== PR changed-file summary =='
git diff --stat origin/main...HEAD 2>/dev/null || git diff --stat HEAD^...HEAD 2>/dev/null || true
git diff --name-status origin/main...HEAD 2>/dev/null || git diff --name-status HEAD^...HEAD 2>/dev/null || true

echo '== DiffComment payload and store =='
for f in Sources/DiffCommentPayload.swift Sources/DiffCommentStore.swift; do
  if [ -f "$f" ]; then
    echo "--- $f"
    nl -ba "$f" | sed -n '1,280p'
  fi
done

echo '== Socket handler =='
if [ -f Sources/TerminalController+CommentsCommands.swift ]; then
  nl -ba Sources/TerminalController+CommentsCommands.swift | sed -n '1,180p'
fi

echo '== Bridge and TextBoxInput store call contexts =='
rg -n -C 12 -P '\b(store|DiffCommentStore)\.comments\s*\(|\bDiffCommentStore\s*\(' \
  Sources/Panels/DiffCommentsBridge.swift Sources/TextBoxInput.swift Sources/TerminalController+CommentsCommands.swift 2>/dev/null || true

echo '== All comments(repoRoot:) declarations and calls =='
rg -n -C 6 -P '\bcomments\s*\(\s*repoRoot\s*:' Sources cmuxTests 2>/dev/null || true

echo '== Candidate package conventions =='
for f in Packages/macOS/CmuxGit/Package.swift Packages/macOS/CmuxFoundation/Package.swift; do
  if [ -f "$f" ]; then
    echo "--- $f"
    sed -n '1,180p' "$f"
  fi
done

Length of output: 16265


🏁 Script executed:

#!/bin/bash
set -u

echo '== Rule: expensive synchronous load =='
sed -n '1,260p' .github/review-bot-rules/swift-expensive-sync-load.md 2>/dev/null || true

echo '== Rule: Swift package boundaries =='
sed -n '1,300p' .github/review-bot-rules/swift-package-boundaries.md 2>/dev/null || true

echo '== DiffCommentPayload =='
sed -n '1,240p' Sources/DiffCommentPayload.swift 2>/dev/null || true

echo '== DiffCommentStore =='
sed -n '1,300p' Sources/DiffCommentStore.swift 2>/dev/null || true

echo '== Comments socket handler =='
sed -n '1,180p' Sources/TerminalController+CommentsCommands.swift 2>/dev/null || true

echo '== TerminalController isolation and dispatch context =='
rg -n -C 8 -P '`@MainActor`|class\s+TerminalController\b|extension\s+TerminalController\b|v2CommentsList\s*\(' \
  Sources/TerminalController.swift Sources/TerminalController+CommentsCommands.swift 2>/dev/null || true

echo '== Focused PR changes to payload, bridge, and socket handler =='
git diff --find-renames origin/main...HEAD -- \
  Sources/DiffCommentPayload.swift \
  Sources/Panels/DiffCommentsBridge.swift \
  Sources/TerminalController+CommentsCommands.swift 2>/dev/null \
  || git diff --find-renames HEAD^...HEAD -- \
  Sources/DiffCommentPayload.swift \
  Sources/Panels/DiffCommentsBridge.swift \
  Sources/TerminalController+CommentsCommands.swift 2>/dev/null \
  || true

echo '== DiffComment references outside tests =='
rg -n -C 3 -P '\bDiffComment\b|\bDiffCommentStore\b' Sources -g '*.swift' 2>/dev/null || true

Length of output: 80139


@haung921209 I re-evaluated both findings against the current code and the rule text.

  1. Expensive Synchronous Load: no change is required in this PR.

    I withdraw this finding.

    DiffCommentStore.loadFile(repoRoot:) reads one repository-keyed file. It caches that decoded file in cacheByRepoKey. The file is not agent history, a transcript, a whole-corpus store, or another data source covered by swift-expensive-sync-load.md.

    The new comments.list caller runs on @MainActor, but it uses the existing @MainActor store and its existing cache. The PR does not modify the store or worsen an existing whole-corpus load. Moving only this caller off-main would create a separate cache-coordination problem. If the project later changes this persistence design, it should change the bridge, TextBoxInput, and socket access together.

  2. Swift Package Boundaries: implement a package extraction in this PR.

    I do still require a change for this finding.

    Sources/DiffCommentPayload.swift is new reusable domain logic. It is independent of AppKit and controller state. Both the WebKit bridge and the socket API use it. Its tests run without UI construction. These are direct package-boundary signals under swift-package-boundaries.md.

    Use Packages/macOS/CmuxDiffComments as the package target. The smallest initial cut should expose public DiffComment and DiffCommentPayload. Update the app target, bridge, socket handler, and tests to import that package.

    DiffCommentStore can remain app-side only if the package contains no persistence logic. If the pre-merge check specifically requires the persistence boundary too, move DiffCommentStore in the same extraction and inject its app-specific directory configuration from the app composition layer. Do not retain a process-wide app dependency such as SessionRestorePolicy inside the package.


✏️ Learnings added
Learnt from: haung921209
URL: https://github.com/manaflow-ai/cmux/pull/9604

Timestamp: 2026-08-05T01:55:55.459Z
Learning: `Sources/DiffCommentStore.swift` is an `MainActor` repository-keyed review-comment store. Its `loadFile(repoRoot:)` method caches one decoded JSON file per repository key in `cacheByRepoKey`. Any future off-main migration must preserve one coherent cache for the WebKit bridge, `TextBoxInput`, and socket callers; moving only one caller can produce stale reads.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

Per the package-boundary review: the comment model and its wire mapping are
reusable domain logic shared by the WebKit bridge, the socket method, and the
tests, so they move behind a SwiftPM boundary.

- Packages/macOS/CmuxDiffComments exposes public DiffComment and
  DiffCommentPayload and depends on Foundation only.
- Persistence stays app-side: DiffCommentStore keeps its directory resolution
  and the SessionRestorePolicy test check, so no app-wide dependency crosses
  into the package.
- The app target, bridge, socket handler, and both test files import the
  package; no behavior changes.
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

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

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

@haung921209

Copy link
Copy Markdown
Contributor Author

Done in e4f26e5 — thanks for the clear adjudication.

Swift Package Boundaries: implemented as you specified. Packages/macOS/CmuxDiffComments now exposes public struct DiffComment and public enum DiffCommentPayload, and depends on Foundation only. Following your constraints:

  • Persistence stayed app-side. DiffCommentStore keeps its Application Support directory resolution and its SessionRestorePolicy.isRunningUnderAutomatedTests() check, so no process-wide app dependency crossed into the package.
  • Socket and WebView glue stayed app-side too: TerminalController keeps the dispatch, DiffCommentsBridge keeps the WebKit handler.
  • The app target, bridge, socket handler, and both test files import the package. The mapper moved as a rename, so the diff shows it as such.

Expensive Synchronous Load: noted, and thanks for recording the cache-coherence constraint — that matches what I hit in testing. If the persistence design is revisited later, the bridge, TextBoxInput, and the socket path need to move together.

Verification on e4f26e5:

  • scripts/reload.sh --tag spike: BUILD SUCCEEDED with the new package in the graph.
  • check-pbxproj.sh and lint-pbxproj-test-wiring.sh (651 test files) pass.
  • tests/test_cli_contract_help.py: 151 probes + 1 negative pass.
  • CommentsListPayloadTests + DiffCommentStoreTests: TEST SUCCEEDED, 11 cases — both now compiling against the package.
  • Live against a running app, unchanged behavior: default lists the pending comment only, --all adds the consumed one with its label, --repo --all and a stray positional each exit 1 with their own message.

@haung921209

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

The pre-merge table still reflects the pre-e4f26e5 tree. Since then: the CmuxDiffComments package extraction you asked for has landed, the expensive-sync-load finding was withdrawn in your last reply, and every string this PR adds carries real translations for all 20 locales.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

@haung921209 I will run a full review against the current PR tree. The review will evaluate the CmuxDiffComments package extraction and the current localization changes.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@cmux.xcodeproj/project.pbxproj`:
- Around line 11649-11652: Update the package field in the CmuxDiffComments
XCSwiftPackageProductDependency object to reference the PBXFileReference UUID
whose object definition is the CmuxDiffComments local Swift package, namely
CC0033E15A1B2C3D4E5F0010, instead of the CMUXDebugLog reference. Validate the
UUID against its actual package-reference definition.

In `@docs/cli-contract.md`:
- Line 105: Update the comments CLI entry in the contract to document both
supported subcommand names, `list` and its `ls` alias, while preserving the
existing options, behavior, and socket method details.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 21b81237-762d-4995-b005-a8fc16097c8b

📥 Commits

Reviewing files that changed from the base of the PR and between 4cf7cc3 and e4f26e5.

📒 Files selected for processing (15)
  • CLI/CMUXCLI+CommandSuggestions.swift
  • CLI/CMUXCLI+Comments.swift
  • CLI/cmux.swift
  • Packages/macOS/CmuxDiffComments/Package.swift
  • Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffComment.swift
  • Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffCommentPayload.swift
  • Resources/Localizable.xcstrings
  • Sources/DiffCommentStore.swift
  • Sources/Panels/DiffCommentsBridge.swift
  • Sources/TerminalController+CommentsCommands.swift
  • Sources/TerminalController.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/CommentsListPayloadTests.swift
  • cmuxTests/DiffCommentStoreTests.swift
  • docs/cli-contract.md

Comment thread cmux.xcodeproj/project.pbxproj
Comment thread docs/cli-contract.md Outdated
The product dependency I added carried CMUXDebugLog's package UUID with a
CmuxDiffComments comment, so a clean resolve could have associated the product
with the wrong package. My local builds hid it behind an already-resolved
SourcePackages cache.

Audited every XCSwiftPackageProductDependency in the project against its
referenced object: no other mismatch. Verified with SourcePackages deleted so
resolution ran from scratch.

Also documents the `ls` alias in the comments contract row.
…mespace

The ambient-global-state rule flags a caseless enum whose whole API is static
helpers, and it was the right call here: an instance can own the formatter, so
"one formatter per reply" is now a property of the type rather than something
each caller has to remember. Tests can inject a formatter too.

TerminalController, DiffCommentsBridge, and the tests construct a mapper; the
bridge's list reply collapses to comments.map(payload.json).
@haung921209

Copy link
Copy Markdown
Contributor Author

Pushed 10a04ae. One of the three new errors was right; for the other two I think the evidence points the other way.

No Ambient Global State — fixed, and it improved the design. You were right that a caseless enum whose whole API is static helpers is the flagged shape, and the exemption in the rule covers private/fileprivate file-scope helpers rather than a public type. DiffCommentPayload is now a constructable struct whose instance owns the ISO8601DateFormatter, so "one formatter per reply" became a property of the type instead of something each caller has to remember — which also closes the earlier bridge-path finding structurally. TerminalController, DiffCommentsBridge, and the tests construct a mapper, and the bridge's list reply is now comments.map(payload.json).

Swiftpm Lockfiles — I believe this is a false positive. cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved holds 14 pins and every one is a remote dependency (aexml, iroh-ffi, networkimage, pathkit, posthog-ios, …). None of the repository's ~40 local packages appear there — CMUXDebugLog, CmuxCore, and CmuxGit are all absent. CmuxDiffComments is an XCLocalSwiftPackageReference with zero external dependencies, so there is nothing for SwiftPM to pin and no lockfile diff to commit. If you know of a case where a local package does get pinned in this repo, point me at it and I will add the entry.

Swift Blocking Runtime — also not something this PR introduces. swift-blocking-runtime.md fails a diff that "introduces or materially expands" DispatchSemaphore, semaphore.wait(), sleeps, DispatchQueue.main.sync, or manual locks, and it explicitly allows "existing blocking code that the PR does not introduce or worsen". My diff adds no synchronization primitive; it calls CLIProcessRunner.runProcess, the CLI's standard exec helper, whose semaphores are internal to it. cmux diff resolves its git root the same way — CLI/cmux_open.swift calls that runner nine times, including gitSingleLine/gitStdout for rev-parse --show-toplevel, which is exactly what comments list does. The CLI is a short-lived synchronous process, not the socket, terminal, rendering, focus, or input path the rule emphasizes.

If you want git resolution moved off that helper, it should change for the whole CLI rather than this one call site, and I would rather do that as its own PR.

Verification on 10a04ae: BUILD SUCCEEDED; check-pbxproj.sh and lint-pbxproj-test-wiring.sh pass; tests/test_cli_contract_help.py 151 probes + 1 negative pass; CommentsListPayloadTests + DiffCommentStoreTests 11 cases pass.

@haung921209

Copy link
Copy Markdown
Contributor Author

@coderabbitai please adjudicate the two I pushed back on (Swiftpm Lockfiles, Swift Blocking Runtime) the way you did last round — tell me plainly whether you want changes in this PR, and I will implement or file follow-ups accordingly.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="origin/main"
if ! git rev-parse --verify -q "${base}^{commit}" >/dev/null; then
  base="HEAD^"
fi

echo '== Applicable rule files =='
for f in \
  .github/review-bot-rules/swiftpm-lockfiles.md \
  .github/review-bot-rules/swift-blocking-runtime.md
do
  if [ -f "$f" ]; then
    echo "--- $f"
    sed -n '1,300p' "$f"
  else
    echo "--- missing: $f"
  fi
done

echo '== Focused PR changes =='
git diff --find-renames --stat "$base...HEAD" -- \
  CLI \
  Packages/macOS/CmuxDiffComments \
  cmux.xcodeproj \
  ':(glob)**/Package.resolved' \
  2>/dev/null || true
git diff --find-renames -U25 "$base...HEAD" -- \
  CLI/CMUXCLI+Comments.swift \
  CLI/cmux.swift \
  Packages/macOS/CmuxDiffComments/Package.swift \
  cmux.xcodeproj/project.pbxproj \
  ':(glob)**/Package.resolved' \
  2>/dev/null || true

echo '== SwiftPM manifests and resolved files =='
fd -HI -t f '^(Package\.swift|Package\.resolved)$' . -x sh -c '
  echo "--- $1"
  sed -n "1,260p" "$1"
' sh {}

echo '== Xcode package references =='
rg -n -C 5 -P '\b(XCLocalSwiftPackageReference|XCRemoteSwiftPackageReference|CmuxDiffComments|packageReferences)\b' \
  cmux.xcodeproj/project.pbxproj 2>/dev/null || true

echo '== CLI process helper declarations and relevant calls =='
fd -t f -e swift . | while IFS= read -r f; do
  if rg -q -P '\b(class|struct|enum)\s+CLIProcessRunner\b|\b(runProcess|gitSingleLine|gitStdout)\s*\(' "$f"; then
    echo "--- $f"
    rg -n -C 7 -P '\b(class|struct|enum)\s+CLIProcessRunner\b|\b(runProcess|gitSingleLine|gitStdout)\s*\(' "$f"
  fi
done

echo '== Blocking primitives introduced by this PR =='
git diff -U3 "$base...HEAD" -- '*.swift' \
  | rg -n -C 3 -P '^[+](?!\+\+).*\b(DispatchSemaphore|semaphore\.wait|Thread\.sleep|sleep\s*\(|DispatchQueue\.main\.sync|NSLock|os_unfair_lock|pthread_mutex)\b' \
  || true

Length of output: 111479


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="origin/main"
if ! git rev-parse --verify -q "${base}^{commit}" >/dev/null; then
  base="HEAD^"
fi

echo '== Rule: SwiftPM lockfiles =='
sed -n '1,320p' .github/review-bot-rules/swiftpm-lockfiles.md 2>/dev/null || true

echo '== Rule: Swift blocking runtime =='
sed -n '1,360p' .github/review-bot-rules/swift-blocking-runtime.md 2>/dev/null || true

echo '== PR package and lockfile status =='
git diff --name-status "$base...HEAD" -- \
  Packages/macOS/CmuxDiffComments \
  cmux.xcodeproj/project.pbxproj \
  cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved \
  || true

echo '== CmuxDiffComments manifest =='
sed -n '1,220p' Packages/macOS/CmuxDiffComments/Package.swift 2>/dev/null || true

echo '== Xcode package references for CmuxDiffComments =='
rg -n -A12 -B8 -F 'CmuxDiffComments' cmux.xcodeproj/project.pbxproj 2>/dev/null || true
rg -n -A12 -B5 -P 'XCLocalSwiftPackageReference|XCRemoteSwiftPackageReference' cmux.xcodeproj/project.pbxproj 2>/dev/null || true

echo '== Resolved-package identities and locations =='
python3 - <<'PY'
import json
from pathlib import Path
for path in Path('.').rglob('Package.resolved'):
    try:
        data = json.loads(path.read_text())
    except Exception as exc:
        print(f'{path}: cannot parse: {exc}')
        continue
    pins = data.get('pins', data.get('object', {}).get('pins', []))
    print(f'--- {path} ({len(pins)} pins)')
    for pin in pins:
        state = pin.get('state', {})
        print(f"{pin.get('identity') or pin.get('package')}: {pin.get('location') or pin.get('repositoryURL')} {state.get('revision', '')}")
PY

echo '== Exact comments CLI implementation =='
sed -n '1,210p' CLI/CMUXCLI+Comments.swift 2>/dev/null || true

echo '== Exact process-runner implementation =='
sed -n '250,430p' CLI/CMUXCLI+Process.swift 2>/dev/null || true

echo '== Existing git-root helper usages =='
rg -n -C 6 -P '\b(gitSingleLine|gitStdout|CLIProcessRunner\.runProcess)\s*\(' \
  CLI/CMUXCLI+Comments.swift CLI/cmux_open.swift CLI/CMUXCLI+Process.swift 2>/dev/null || true

echo '== Relevant added lines only =='
git diff -U12 "$base...HEAD" -- CLI/CMUXCLI+Comments.swift CLI/CMUXCLI+Process.swift \
  Packages/macOS/CmuxDiffComments/Package.swift cmux.xcodeproj/project.pbxproj \
  ':(glob)**/Package.resolved' \
  2>/dev/null || true

Length of output: 166697


@haung921209 I re-evaluated both findings. I do not want changes in this PR.

  1. SwiftPM Lockfiles: no change is required.

    CmuxDiffComments is an XCLocalSwiftPackageReference.

    Its Package.swift declares no dependencies.

    The PR does not modify any Package.resolved file.

    The inspected lockfiles contain remote package pins only. They do not contain the existing local package references. There is no revision to pin for CmuxDiffComments.

  2. Swift Blocking Runtime: no change is required.

    CLI/CMUXCLI+Comments.swift calls the existing CLIProcessRunner.runProcess helper for git rev-parse --show-toplevel.

    The PR does not add or modify DispatchSemaphore, NSLock, a blocking wait, a sleep, or a polling loop.

    CLIProcessRunner contains existing semaphore and lock usage, but this PR does not change that implementation. The same helper already supports the cmux diff Git commands.

    The blocking-runtime rule applies when the diff introduces or materially expands a blocking primitive. That condition is not met here.

I withdraw both findings. A process-runner redesign can be a separate, CLI-wide change if the project decides to remove its existing synchronous execution model.


✏️ Learnings added
Learnt from: haung921209
URL: https://github.com/manaflow-ai/cmux/pull/9604

Timestamp: 2026-08-05T02:46:30.573Z
Learning: In this repository, Xcode local Swift packages declared as `XCLocalSwiftPackageReference` do not appear in `Package.resolved` when they have no remote dependencies. `Packages/macOS/CmuxDiffComments` is such a dependency-free local package, so its addition does not require a lockfile update.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@haung921209

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

Everything you asked for has landed: the CmuxDiffComments extraction, the constructable mapper, real translations for all 20 locales on every key, the --repo and stray-argument guards, plural header keys, the ls alias in the contract, and the corrected package reference. Three findings you withdrew (expensive sync load, lockfiles, blocking runtime) need no code. Please refresh the pre-merge table against 10a04ae so a maintainer sees the current state.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

@haung921209 I will run a full review against the current PR tree. I will refresh the pre-merge table from the current review results.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@Resources/Localizable.xcstrings`:
- Around line 36189-36312: Update the localization entry for
cli.comments.list.header.other to define locale-aware plural-category variants,
especially for Russian, Polish, and Ukrainian, using the correct forms for
counts such as 2 while preserving the existing translations for other
categories. Ensure the comment-count formatting selects the variant through the
platform’s locale-aware plural rules rather than always using the “other” form.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5ac428d1-7c94-444b-84ef-4c84f23b8629

📥 Commits

Reviewing files that changed from the base of the PR and between 4cf7cc3 and 10a04ae.

📒 Files selected for processing (15)
  • CLI/CMUXCLI+CommandSuggestions.swift
  • CLI/CMUXCLI+Comments.swift
  • CLI/cmux.swift
  • Packages/macOS/CmuxDiffComments/Package.swift
  • Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffComment.swift
  • Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffCommentPayload.swift
  • Resources/Localizable.xcstrings
  • Sources/DiffCommentStore.swift
  • Sources/Panels/DiffCommentsBridge.swift
  • Sources/TerminalController+CommentsCommands.swift
  • Sources/TerminalController.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/CommentsListPayloadTests.swift
  • cmuxTests/DiffCommentStoreTests.swift
  • docs/cli-contract.md

Comment thread Resources/Localizable.xcstrings
… strings

The Slavic plural finding was right, but CLDR variations do not fire here: the
CLI resolves strings against its own bundle, which carries no string catalog, so
every localized CLI string renders its defaultValue. Measured — with the header
moved to variations, count=1 printed "1 review comments".

So selection stays in code (correct singular today) and the plural strings avoid
letting the numeral govern the noun, which keeps one form grammatical for every
count above one: ru "Комментарии ревью: %1$lld", uk "Коментарі рецензування:
%1$lld", pl "Komentarze przeglądu: %1$lld", ar "تعليقات المراجعة: %1$lld".
Bosnian already used a form valid for every count above one.
@haung921209

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

0391f22 is the current head. Every finding from all rounds is either fixed or withdrawn, and the last one confirmed. Please refresh the pre-merge table so a maintainer sees the state of the branch rather than an earlier tree.

On the CLI localization-bundle gap: yes please, open the follow-up issue — that work is real but belongs outside a PR whose subject is one read-only command.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Current PR head and changed files =='
git rev-parse HEAD
git show -s --format='%H%n%s' HEAD
base="origin/main"
if ! git rev-parse --verify -q "${base}^{commit}" >/dev/null; then
  base="HEAD^"
fi
git diff --name-status "$base...HEAD"
git diff --stat "$base...HEAD"

echo '== Final CLI comments implementation outline =='
ast-grep outline CLI/CMUXCLI+Comments.swift --items all 2>/dev/null || true
sed -n '1,260p' CLI/CMUXCLI+Comments.swift 2>/dev/null || true

echo '== CLI localization bundle references and localization catalog wiring =='
rg -n -C 5 -P '\b(Bundle|bundle|Localizable|localized|localizedString|String\(localized:|NSLocalizedString)\b' \
  CLI Resources Sources -g '*.swift' -g '*.xcstrings' 2>/dev/null | head -n 1000 || true

echo '== Changed localization keys and CLI contract =='
git diff -U8 "$base...HEAD" -- Resources/Localizable.xcstrings docs/cli-contract.md CLI/cmux.swift \
  2>/dev/null || true

echo '== Current tests related to comments listing =='
ast-grep outline cmuxTests/CommentsListPayloadTests.swift --items all 2>/dev/null || true
sed -n '1,260p' cmuxTests/CommentsListPayloadTests.swift 2>/dev/null || true

echo '== Requester comment permalink =='
gh api repos/manaflow-ai/cmux/issues/9604/comments --paginate \
  --jq '.[] | select(.user.login == "haung921209" and (.body | contains("CLI localization-bundle gap"))) | .html_url' \
  | tail -n 1

Length of output: 157135


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Shared package public API =='
ast-grep outline Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments --items all 2>/dev/null || true
sed -n '1,220p' Packages/macOS/CmuxDiffComments/Package.swift
sed -n '1,240p' Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffComment.swift
sed -n '1,260p' Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffCommentPayload.swift

echo '== Socket handler and bridge mapping =='
sed -n '1,220p' Sources/TerminalController+CommentsCommands.swift
rg -n -C 8 -P '\bcomments\.list\b|\bv2CommentsList\s*\(|\bDiffCommentPayload\b' \
  Sources/TerminalController.swift Sources/Panels/DiffCommentsBridge.swift

echo '== Xcode local-package reference and target linkage =='
rg -n -C 6 -F 'CmuxDiffComments' cmux.xcodeproj/project.pbxproj

echo '== Create requested follow-up issue =='
issue_body=$(cat <<'EOF'
## Summary

The `cmux` CLI uses `String(localized:)` for user-visible strings, but its executable bundle does not include the app string catalog. As a result, CLI output uses each call site's `defaultValue` instead of the translations in `Resources/Localizable.xcstrings`.

PR `#9604` adds localized `cli.comments.*` strings and documents this limitation in `CLI/CMUXCLI+Comments.swift`. The limitation also affects existing CLI localization calls. This work is outside the scope of the read-only `cmux comments list` command.

## Required changes

- Define how the CLI locates and loads the localized resources when it runs from supported app and development layouts.
- Package or otherwise expose the required string catalog resources to the CLI.
- Update CLI localization lookup so `String(localized:)` resolves from the intended resource bundle.
- Preserve `defaultValue` behavior when the localization resources are unavailable.
- Add focused tests or probes for at least one non-English locale and the fallback path.

## Affected areas

- `CLI/` localization lookup and executable resource discovery.
- `Resources/Localizable.xcstrings` resource packaging.
- Xcode target and build-resource configuration.
- CLI contract or test probes, as needed.

## Rationale

The current CLI command is correct for its English fallback output. A repository-wide bundle solution is required before the translated catalog values can reach CLI users.

## Acceptance criteria

- A supported non-English locale changes CLI output from the English `defaultValue` to the catalog translation.
- The CLI still produces the `defaultValue` when localization resources are not available.
- Existing CLI commands that use `String(localized:)` use the same resource-resolution path.
- The solution does not require each CLI command to implement custom bundle lookup.

## Backlinks

- Pull request: https://github.com/manaflow-ai/cmux/pull/9604
- Requester comment: https://github.com/manaflow-ai/cmux/pull/9604#issuecomment-5187113377
- Requested by: `@haung921209`
EOF
)

gh issue create \
  --repo manaflow-ai/cmux \
  --title 'Make localized string-catalog resources available to the cmux CLI' \
  --body "$issue_body" \
  --assignee haung921209

Length of output: 22957


@haung921209 Full review completed against 0391f22.

I found no new blocking issue in the current tree. The CmuxDiffComments package extraction, comments.list dispatch and capability wiring, test-target linkage, and CLI command integration are present.

The pre-merge findings from earlier revisions remain fixed or withdrawn. The CLI localization-bundle gap is the only follow-up item. It does not block this command.

I created and assigned #9623: Make localized string-catalog resources available to the cmux CLI. The issue links to this PR and your comment. It defines the required bundle resolution, fallback behavior, and locale coverage.

✅ Action performed

Full review finished.

The internationalization check is right that cmux-cli ships no string catalog,
so String(localized:) in the CLI renders its default value — measured earlier
when a plural variation failed to select. Rather than add a resource phase to a
command-line tool, this routes the command's strings through
CMUXDiffViewerLocalization, which already resolves
CLIExecutableLocator.enclosingAppBundle() and honors AppleLanguages.

Verified per locale: help renders 使い方 under ja and 사용법 under ko, the list
header renders "Комментарии ревью: 3" under ru, state labels render 未対応 and
в ожидании, and the guards render their Russian and Japanese messages.

Also documents DiffComment.init and the payload tests.
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

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

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

@haung921209

Copy link
Copy Markdown
Contributor Author

Pushed 0d93f18.

Full Internationalization — fixed, and the check was pointing at something real. You are right that cmux-cli has no resource phase, so the catalog never reaches it. Rather than bundle resources into a command-line tool, I routed this command's strings through CMUXDiffViewerLocalization, which already resolves CLIExecutableLocator.enclosingAppBundle() and honors AppleLanguages — the mechanism the repo built for exactly this gap.

It now actually localizes, which I could not claim before:

$ cmux comments --help                      → Usage: cmux comments <subcommand> [options]
$ AppleLanguages='(ja)' cmux comments --help → 使い方: cmux comments <subcommand> [options]
$ AppleLanguages='(ko)' cmux comments --help → 사용법: cmux comments <subcommand> [options]

$ AppleLanguages='(ru)' cmux comments list --repo <3 comments>
Комментарии ревью: 3 (репозиторий: …)
- S.md:2 [в ожидании]

$ AppleLanguages='(ja)' cmux comments list --repo <3 comments>
レビューコメント 3 件 (リポジトリ: …)
- S.md:2 [未対応]

$ AppleLanguages='(ru)' cmux comments list --repo --all
Error: --repo требует путь. Путь, начинающийся с дефиса, укажите как ./-name
$ AppleLanguages='(ja)' cmux comments list --repo /tmp
Error: cmux comments には git リポジトリが必要です: /tmp

So the 20 locales are load-bearing now instead of decorative, and this is the first CLI command in the repo where they are. The CLI-wide switch for the other commands is still the follow-up you offered to file.

Swiftpm Lockfiles — this is the one you already adjudicated and withdrew, with the learning recorded: local XCLocalSwiftPackageReference packages with no remote dependencies do not appear in Package.resolved, and CmuxDiffComments is exactly that. The gate table seems not to have picked up its own verdict. Nothing to change unless you have since found a counterexample in this repo.

Docstring Coverage — documented DiffComment.init and the payload tests, which is what the 33% figure was missing.

Verification on 0d93f18: BUILD SUCCEEDED; check-pbxproj.sh and lint-pbxproj-test-wiring.sh pass; tests/test_cli_contract_help.py 151 probes + 1 negative pass; CommentsListPayloadTests + DiffCommentStoreTests 11 cases pass; plus the per-locale output above.

@haung921209

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@cmux.xcodeproj/project.pbxproj`:
- Line 5314: Add the existing CmuxDiffComments PBXBuildFile entry with
productRef CC0033E15A1B2C3D4E5F0011 to the cmux app Frameworks build phase
A5001030, preserving the packageProductDependencies declaration and the separate
cmuxTests linkage.

In
`@Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffCommentPayload.swift`:
- Around line 19-35: Update DiffCommentPayload.json(_:) to serialize the
optional consumedAt lifecycle field, then remove the list-only consumedAt
mapping in
Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffCommentPayload.swift
lines 49-52 so the shared mapper owns the complete contract. In
Sources/Panels/DiffCommentsBridge.swift lines 170-171, continue using the shared
mapper without adding separate lifecycle-state handling.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 79780be1-e19e-4f9d-a960-093429f515c2

📥 Commits

Reviewing files that changed from the base of the PR and between 4cf7cc3 and 0d93f18.

📒 Files selected for processing (15)
  • CLI/CMUXCLI+CommandSuggestions.swift
  • CLI/CMUXCLI+Comments.swift
  • CLI/cmux.swift
  • Packages/macOS/CmuxDiffComments/Package.swift
  • Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffComment.swift
  • Packages/macOS/CmuxDiffComments/Sources/CmuxDiffComments/DiffCommentPayload.swift
  • Resources/Localizable.xcstrings
  • Sources/DiffCommentStore.swift
  • Sources/Panels/DiffCommentsBridge.swift
  • Sources/TerminalController+CommentsCommands.swift
  • Sources/TerminalController.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/CommentsListPayloadTests.swift
  • cmuxTests/DiffCommentStoreTests.swift
  • docs/cli-contract.md

Comment thread cmux.xcodeproj/project.pbxproj
@haung921209

Copy link
Copy Markdown
Contributor Author

State of the branch at 0d93f18, for whoever picks this up:

All review threads are closed. Thirteen findings across five rounds — fixed: locale coverage and real translations, the missing socket.comments.missingRepoRoot key, one formatter per reply, unknown-flag and stray-positional rejection, --repo value validation, plural-correct headers, the ls alias, the CmuxDiffComments extraction, the constructable mapper, the corrected package reference, and app-bundle string resolution. Withdrawn after discussion: strict include_consumed typing, git rev-parse validation in the handler, expensive-synchronous-load, and blocking-runtime.

Remaining gate items, both of which I believe need no code:

  • Swiftpm Lockfiles — you adjudicated this one yourself and withdrew it, with the learning recorded: a local XCLocalSwiftPackageReference with no remote dependencies does not appear in Package.resolved. The file holds 14 pins, all remote, and none of the repo's ~40 local packages. Writing a pin for a dependency-free local package to satisfy the gate would put a fiction in the lockfile, so I have not.
  • Docstring Coverage (warning, 66.67%) — every declaration this PR adds is documented. What remains undocumented is the pre-existing test methods in DiffCommentStoreTests, a file I touched only to add an import. Say the word and I will document them, but I would rather not pad the diff with cosmetic comments on code the PR does not otherwise change.

Happy to keep iterating if anything else surfaces.

@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

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

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

@haung921209
haung921209 requested a review from austinywang August 6, 2026 01:29
@austinywang
austinywang merged commit 9f0c47f into manaflow-ai:main Aug 6, 2026
9 checks passed
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.

2 participants