Skip to content

feat: add project-local cmux Artifacts - #8610

Open
austinywang wants to merge 250 commits into
mainfrom
feat-cmux-artifacts
Open

austinywang wants to merge 250 commits into
mainfrom
feat-cmux-artifacts

Conversation

@austinywang

@austinywang austinywang commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Implements the project-local cmux Artifacts and Notes filesystem described in #8587:

  • stores ordinary user-organizable files under session roots at <project>/.cmux/<agent-session>/{artifacts,notes}
  • keeps _session.json and _workspace.json markers beside the two content folders so renamed/moved session folders are rediscovered without a path index
  • keeps hidden provenance and import staging under .cmux/.metadata/
  • reuses the shared chat artifact pipeline for selective automatic capture, content dedupe, bounded scanning/search, provenance, Git privacy checks, and manual “Save to cmux Artifacts” actions
  • adds socketless cmux artifact list|path|open|add|search and cmux note list|path|read|write|append|search|open|rm
  • adds a beta-gated Artifacts & Notes right-sidebar tab backed by a live filesystem watcher, fuzzy/content search, previews, drag-out, Finder reveal, copy path, and .cmux/... copy-reference actions
  • adds the cmux-project-files repo skill so agents use the CLI to read/write the filesystem without guessing session folder names

Architecture

<project>/.cmux/
  <agent-session>/
    _session.json
    _workspace.json
    artifacts/
    notes/
  .metadata/
    provenance/
    imports/

The live filesystem is authoritative. LocalArtifactRepository owns storage and scanning; capture policy/provenance remains separate; CLI and app entrypoints share the repository; the sidebar projects immutable row snapshots below its lazy-list boundary. Content and session folders can be reorganized as ordinary files, while marker lookup reconnects later writes to a moved session.

Git’s per-checkout exclude receives targeted patterns for session artifacts/, notes/, marker files, and .metadata/; project configuration such as .cmux/artifacts.json remains independently trackable.

Testing

  • arch -arm64 swift test --quiet — CmuxArtifacts: 105 tests
  • arch -arm64 swift test --quiet — CmuxAgentChat: 375 tests
  • arch -arm64 swift test --quiet — CmuxAgentChatUI: 108 tests
  • arch -arm64 swift test --quiet — CmuxSettingsUI: 115 tests
  • project normalization, workspace package grouping, Package.resolved policy, targeted app-test wiring, Swift parse, JSON/catalog validation, skill validation, and git diff --check
  • localization audit: every changed CLI/sidebar/settings key has English and Japanese values
  • all newly added Swift files are below 500 lines; no budget TSV was changed

Per task constraints, no app build, xcodebuild, XCUITest, reload, launch, or local UI recording was run.

Demo Video

Not recorded because this task explicitly disallows building or launching the app locally.

Review Trigger

Ready for automated and maintainer review at 940bed384e.

Checklist

  • Session-rooted .cmux filesystem with sibling Artifacts and Notes
  • Automatic and manual artifact capture with bounded policy and provenance
  • Artifact and Note CLI behavior coverage
  • Live beta-gated sidebar tree, search, preview, drag, reveal, and references
  • English and Japanese localization audit
  • Agent skill scaffolded and validated
  • Current origin/main merged with zero local conflicts

Refs #8587


Note

Medium Risk
Touches local filesystem writes, artifact read authorization, and transcript provenance; changes are largely additive with fail-closed checks but affect security-sensitive path handling.

Overview
Adds socketless CLI access to project-local Artifacts and Notes under <project>/.cmux via new cmux artifact and cmux note commands (list/path/read/write/search/open/add/rm), wired through shared LocalArtifactRepository helpers for project discovery, agent-session identity from environment, and JSON or human output.

Exposes an artifacts right-sidebar mode in CLI parsing/help alongside existing sidebar controls, and documents the workflow in a new cmux-project-files skill entry.

Hardens the chat artifact filesystem layer: ArtifactByteReader moves to low-level Darwin opens with canonical-path and device/inode pinning, bounded directory listing (including isIncomplete), and safer thumbnails; panel/terminal authorization stores retain identities for reads. Transcript parsing now tracks capture authorization separately (successful apply_patch/Bash redirection mutations only), with bounded tool-path extraction and new shell mutation detection.

CI: Depot unit tests install Node 20 and forward TEST_RUNNER_PATH so XCTest hosts can run JavaScript helpers.

Reviewed by Cursor Bugbot for commit 6c4bbbc. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a project-scoped Artifacts package with filesystem capture, deduplication, provenance, search, CLI commands, agent-chat saving, mobile capability wiring, and a beta-gated macOS right-sidebar browser.

Changes

Artifacts feature

Layer / File(s) Summary
Artifact storage and capture
Packages/macOS/CmuxArtifacts/...
Adds artifact models, configuration, bounded scanning and reading, provenance recording, deduplication, Git exclusion handling, local repository persistence, and capture-service policy enforcement.
Agent chat and mobile save flow
Sources/Mobile/..., Packages/Shared/CmuxAgentChat/..., Packages/iOS/...
Adds session artifact capture coordination, revision/cursor tracking, chat-source save capabilities, mobile RPC handling, and iOS save actions with localized feedback.
CLI support
CLI/..., cmuxTests/BundledCLILinkageTests.swift
Adds artifact argument parsing, list/path/open/add/search commands, JSON and text output, localized errors and help, async CLI routing, and end-to-end coverage.
Right-sidebar browser
Sources/ArtifactSidebar*.swift, Sources/RightSidebar*.swift, Sources/ContentView*.swift
Adds beta-gated artifact mode selection, sidebar tree/search/add interactions, previews, Finder and pasteboard actions, thumbnails, focus integration, and workspace wiring.
Settings, localization, and build wiring
Sources/Settings*.swift, Resources/Localizable.xcstrings, cmux.xcodeproj/project.pbxproj, Packages/macOS/CmuxArtifacts/README.md
Registers the package and sources, adds artifact settings/search entries, localizes CLI and UI strings, and documents the package layout and contracts.
Validation
Packages/macOS/CmuxArtifacts/Tests/..., cmuxTests/...
Adds tests for capture policy, project discovery, repository persistence, deduplication, provenance safety, bounded search, sidebar behavior, capability gating, cancellation, and chat batch classification.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

  • manaflow-ai/cmux#3537 — Uses the same right-sidebar beta-feature gating and mode-availability plumbing extended here for artifacts.
🚥 Pre-merge checks | ✅ 4 | ❌ 21

❌ Failed checks (1 warning, 20 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Cmux Swift Actor Isolation ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Swift Blocking Runtime ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Browser Automation Off-Main ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Expensive Synchronous Load ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Cache Substitution Correctness ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux No Hacky Sleeps ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Algorithmic Complexity ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Swift Concurrency ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Swift @Concurrent ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Swift Package Boundaries ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Swiftpm Lockfiles ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Swift Logging ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux User-Facing Error Privacy ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Full Internationalization ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Swiftui State Layout ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Architecture Rethink ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Swift Auxiliary Window Close Shortcuts ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux Source Artifacts ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux No Test Or Debug Seam In Production Source ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
Cmux No Ambient Global State ❓ Inconclusive Repository clone failed, so this custom check could not run with code access. Retry the review run. If this persists, inspect pre-merge custom-check logs for infrastructure or agent runtime failures.
✅ Passed checks (4 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.
Title check ✅ Passed The title clearly summarizes the primary change: adding project-local cmux Artifacts.
Description check ✅ Passed The description includes all required sections and provides detailed scope, architecture, testing, demo rationale, review trigger, and checklist status.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-cmux-artifacts

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.

@austinywang

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds project-local cmux Artifacts across the app, CLI, and mobile chat. The main changes are:

  • A filesystem-backed artifact store with provenance and SHA-256 deduplication.
  • Automatic artifact capture grouped by workspace and session.
  • Artifact list, path, open, add, and search CLI commands.
  • A beta macOS sidebar for browsing, searching, previewing, and managing artifacts.
  • Chat and capability-gated mobile flows for saving files into the project store.

Confidence Score: 5/5

This looks safe to merge.

  • Completed assistant turns now enter the artifact capture path.
  • Moved artifacts can be recovered without the sidebar node limit.
  • No blocking issue was found in the updated code.

Important Files Changed

Filename Overview
Packages/Shared/CmuxAgentChat/Sources/CmuxAgentChat/Model/ChatMessageCollection+BatchClassification.swift Adds shared classification for completed assistant turns and committed agent prose.
Sources/Mobile/AgentChat/AgentChatTranscriptService.swift Schedules artifact capture for completed assistant turns as well as session lifecycle events.
Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactDeduplicationScanner.swift Adds streaming deduplication recovery that does not depend on the sidebar scan budget.
Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift Implements project-local storage, path safety, provenance lookup, and deduplicated imports.

Reviews (4): Last reviewed commit: "fix(artifacts): satisfy pre-merge archit..." | Re-trigger Greptile

Comment thread Sources/Mobile/AgentChat/AgentChatTranscriptService.swift Outdated
Comment thread Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift Outdated

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

🤖 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`+Artifacts.swift:
- Around line 69-70: Replace the inline absolute-path construction in the
artifact handling flow with the existing repository or record accessor that
resolves an artifact’s stored path. Reuse that accessor for record.relativePath
instead of instantiating ArtifactStorePaths directly, keeping the resolved path
consistent with the capture service’s layout logic.
- Around line 52-68: The `add` case in the artifact CLI currently discards
`ArtifactImportOutcome.skipped` details. Handle the outcome’s skipped path
explicitly and map its `ArtifactSkipReason` into the thrown `CLIError`,
preserving the existing success record handling and providing actionable
messages for provenance, extension, size, and file-type rejections.

In
`@Packages/iOS/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Artifacts/ChatArtifactViewerPager.swift`:
- Line 22: Update the save-related Task usage in ChatArtifactViewerPager,
especially the flows around isSavingToArtifacts and the referenced save
handlers, so the operation has an owned lifecycle. Store the task for
cancellation on pager teardown or route saving through a model-owned cancellable
operation, and ensure completion cannot mutate view state or present a toast
after the pager is gone.

In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureDirectoryFinder.swift`:
- Around line 14-40: Resolve the capture directory once at the
ArtifactCaptureService batch boundary, then pass that resolved directory through
each candidate import. Update the capture-directory resolver and its callers to
reuse this value instead of rerunning markerDirectories scans for every
candidate; preserve the existing session-marker, workspace-marker, and fallback
selection behavior during the single batch resolution.

In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureService.swift`:
- Around line 118-131: Add a dedicated ArtifactSkipReason case for
pathOutsideStore, then update ArtifactStoreError.skipReason to map
.pathOutsideStore to that case instead of .notARegularFile. Keep the existing
mappings for other errors unchanged.

In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactDigestCalculator.swift`:
- Around line 6-9: Update ArtifactDigestCalculator.digest to replace per-byte
String(format:) conversion with a fixed hexadecimal lookup table and a
preallocated or bounded output buffer, preserving the same lowercase
64-character SHA-256 result while avoiding per-byte formatted-string
allocations.

In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactPathResolver.swift`:
- Around line 20-25: Update ArtifactPathResolver.swift at lines 20-25 so
isInsideStore uses the private canonicalPath(_:) helper for both paths,
preserving consistent alias-aware containment. Update
ArtifactGitIgnoreManager.swift at lines 48-55 so relativeIgnoreEntry delegates
to ArtifactPathResolver().relativePath(projectRoot, root: worktreeRoot) instead
of manually using hasPrefix; both sites require direct changes.

In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift`:
- Around line 9-12: Update document(...) and the record(...) flow to distinguish
a missing metadata file from corrupt or undecodable JSON. Preserve the existing
document when decoding fails, and surface the corruption or quarantine the file
rather than allowing record(...) to create a new document and overwrite prior
provenance events.
- Around line 22-27: Update the provenance setup and write paths around
rejectSymbolicLink to validate the complete parent chain, including
paths.cmuxDirectory and paths.artifactsRoot, before createDirectory or any
atomic write. Apply the same checks at the referenced second location,
preserving validation of metadataRoot, provenanceRoot, and the final metadata
URL.

In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarModel.swift`:
- Around line 210-214: Remove the Task.sleep(for:) delay from the searchTask
flow in ArtifactSidebarModel and replace the fixed-time debounce with an
input-driven or event-driven trigger that coalesces search updates. Preserve
cancellation of superseded searches and the existing
store.search(projectRoot:query:) behavior.

In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactTreeScanner.swift`:
- Around line 67-83: The directory scan should enforce the remaining node budget
before materializing all children. Update the scanning logic around
contentsOfDirectory and the node-building loop to use a streaming, budget-aware
directory enumerator, stop once remaining reaches zero, set truncated
accordingly, and collect only eligible nodes within the budget.

In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift`:
- Around line 371-373: Centralize the recursive ArtifactNode tree flattening in
a shared helper, such as an extension on [ArtifactNode] in ArtifactNode.swift.
Remove the private flatten(_:) implementations from
Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift
lines 371-373 and
Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSearchEngine.swift
lines 38-40, and update both callers to use the shared helper.
- Around line 98-111: Update the deduplication flow in importFile and
existingFile to first resolve the digest-keyed provenance document under
.cmux/provenance using the existing metadata symbols, read its
lastKnownRelativePath, and verify that the referenced artifact still exists and
matches the requested size before returning it. Retain the current allFiles scan
as a fallback only when the indexed path is missing or stale, preserving
moved-file deduplication without rehashing every size-matching file on the
normal path.

In `@Resources/Localizable.xcstrings`:
- Around line 242712-242724: Update the value for the
“rightSidebar.artifacts.empty.message” localization key so the empty-state copy
refers to files saved or added to the project, rather than only agent-created
files. Apply the broader wording consistently across the affected localizations
while preserving the existing localization structure.

In `@Sources/ArtifactSidebarThumbnailView.swift`:
- Line 12: Introduce a shared in-memory thumbnail cache for
ArtifactSidebarThumbnailView, keyed by fileURL, and consult it in
loadThumbnailIfNeeded before invoking QLThumbnailGenerator; store successfully
generated thumbnails for reuse across row remounts and scrolls. Preserve the
existing loading and failure behavior, and verify whether relying on
QLThumbnailGenerator’s undocumented internal caching is sufficient before
omitting the explicit cache.

In `@Sources/Mobile/AgentChat/AgentChatTranscriptService`+ArtifactCapture.swift:
- Around line 7-21: Update the artifact capture flow in the surrounding
transcript service so transcript-path resolution, artifactIndex.snapshot, and
artifactCaptureCoordinator.capture execute off the main actor on an owner-backed
task or actor. Bind that task to the session lifecycle and cancel it when the
session is removed, while preserving the existing early returns when resolution
or snapshotting fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a25d3c3d-6048-41b7-94df-767d17dad859

📥 Commits

Reviewing files that changed from the base of the PR and between 4daa937 and 4837f70.

⛔ Files ignored due to path filters (1)
  • cmux.xcworkspace/contents.xcworkspacedata is excluded by !**/*.xcworkspace/contents.xcworkspacedata
📒 Files selected for processing (112)
  • CLI/ArtifactCLIArguments.swift
  • CLI/CMUXCLI+Artifacts.swift
  • CLI/CMUXCLI+CommandSuggestions.swift
  • CLI/cmux.swift
  • Packages/Shared/CmuxAgentChat/Sources/CmuxAgentChat/Artifacts/ChatArtifactSaveResult.swift
  • Packages/Shared/CmuxAgentChat/Sources/CmuxAgentChat/Source/ChatEventSource.swift
  • Packages/iOS/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Artifacts/ChatArtifactLoader.swift
  • Packages/iOS/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Artifacts/ChatArtifactViewerPager.swift
  • Packages/iOS/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Resources/Localizable.xcstrings
  • Packages/iOS/CmuxAgentChatUI/Tests/CmuxAgentChatUITests/ChatArtifactLoaderTests.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileChatEventSource.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+AgentChat.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+Capabilities.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellWorkspaceCapabilityTests.swift
  • Packages/macOS/CmuxArtifacts/Package.swift
  • Packages/macOS/CmuxArtifacts/README.md
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactAncestorDirectories.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCandidate.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureConfiguration+Codable.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureConfiguration.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureContext.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureDirectoryFinder.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureDirectoryResolution.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureService.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCapturing.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactDigestCalculator.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactFileKind.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactFuzzyMatcher.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactGitIgnoreManager.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactImportOutcome.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactMetadataDocument.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactNode.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactPathResolver.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProjectLocator.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenance.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceEvent.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactRecord.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSearchEngine.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSearchResult.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSessionMarker.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarFailure.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarModel.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarPhase.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarRowSnapshot.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarWorkspace.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSkipReason.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSnapshot.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactStoreError.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactStorePaths.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactStoring.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactTreeScanner.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactWorkspaceMarker.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactCaptureServiceTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactProjectLocatorTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactSearchEngineTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactSidebarModelTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactTestSupport.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ConfiguredArtifactStore.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/LocalArtifactRepositoryTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/SidebarArtifactStore.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/SidebarCaptureAddCall.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/SidebarCaptureSpy.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/BetaFeaturesCatalogSection.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSectionID.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BetaFeaturesSection.swift
  • Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift
  • Resources/Localizable.xcstrings
  • Sources/App/WorkspaceRuntimeSettings.swift
  • Sources/AppDelegate+Artifacts.swift
  • Sources/AppDelegate.swift
  • Sources/ArtifactSidebarPanelView.swift
  • Sources/ArtifactSidebarRowActions.swift
  • Sources/ArtifactSidebarRowView.swift
  • Sources/ArtifactSidebarThumbnailView.swift
  • Sources/CommandPalette/CommandPaletteSettingsToggle.swift
  • Sources/ContentView+Artifacts.swift
  • Sources/ContentView+RightSidebarCommandPalette.swift
  • Sources/ContentView.swift
  • Sources/MainWindowFocusController.swift
  • Sources/Mobile/AgentChat/AgentArtifactCaptureCoordinator.swift
  • Sources/Mobile/AgentChat/AgentArtifactCaptureSaveError.swift
  • Sources/Mobile/AgentChat/AgentChatTranscriptService+ArtifactCapture.swift
  • Sources/Mobile/AgentChat/AgentChatTranscriptService+BatchClassification.swift
  • Sources/Mobile/AgentChat/AgentChatTranscriptService.swift
  • Sources/Mobile/MobileHostService+Capabilities.swift
  • Sources/NSView+RightSidebarKeyboardFocus.swift
  • Sources/RightSidebarKeyboardFocusBridge.swift
  • Sources/RightSidebarKeyboardFocusView.swift
  • Sources/RightSidebarMode+Availability.swift
  • Sources/RightSidebarMode.swift
  • Sources/RightSidebarPanelView.swift
  • Sources/RightSidebarRemoteCommand.swift
  • Sources/RightSidebarToolPanel.swift
  • Sources/SettingsNavigation.swift
  • Sources/SettingsSearchAliases.swift
  • Sources/TerminalController+MobileChat.swift
  • Sources/TerminalController+MobileChatArtifacts.swift
  • Sources/Workspace.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/BundledCLILinkageTests.swift
  • cmuxTests/FileDropOverlayViewTests.swift
  • cmuxTests/FileExplorerStateModePersistenceTests.swift
  • cmuxTests/HiddenRightSidebarContentMountingTests.swift
  • cmuxTests/RightSidebarCommandPaletteTests.swift
  • cmuxTests/RightSidebarRemoteCommandTests.swift
  • cmuxTests/ShortcutAndCommandPaletteTests.swift
  • cmuxTests/WindowAndDragTests.swift
  • cmuxTests/WorkspaceContentViewVisibilityTests.swift

Comment thread CLI/CMUXCLI+Artifacts.swift
Comment thread CLI/CMUXCLI+Artifacts.swift Outdated
Comment thread Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift Outdated
Comment thread Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift Outdated
Comment thread Resources/Localizable.xcstrings Outdated
Comment thread Sources/ArtifactSidebarThumbnailView.swift
Comment thread Sources/Mobile/AgentChat/AgentChatTranscriptService+ArtifactCapture.swift Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
Sources/ArtifactSidebarPanelView.swift (2)

48-54: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add an explicit accessibility label to the Add button.

safeHelp is help text, not a VoiceOver action label. The icon-only button should provide a localized accessibility label such as “Add files to Artifacts.”

🤖 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/ArtifactSidebarPanelView.swift` around lines 48 - 54, Add an explicit
localized accessibility label to the icon-only Button invoking presentAddPanel,
using the same “Add files to Artifacts” wording as safeHelp. Keep the existing
safeHelp, disabled state, and accessibilityIdentifier unchanged.

82-84: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Tie asynchronous sidebar actions to an owned lifecycle.

These discarded Task handles cannot be cancelled when the sidebar is hidden, rebound, or destroyed. Store/cancel the operations in the model or otherwise tie them to a caller-owned lifecycle.

As per coding guidelines, meaningful asynchronous work must not be fire-and-forget unless it is stored, cancellable, or caller-owned.

Also applies to: 165-167

🤖 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/ArtifactSidebarPanelView.swift` around lines 82 - 84, Update the
retry action in ArtifactSidebarPanelView and the corresponding action at the
other referenced location so each asynchronous refresh is tied to an owned
lifecycle. Store the Task in the model or an appropriate view-owned property,
cancel any existing or obsolete task when the sidebar is hidden, rebound, or
destroyed, and clear the reference when the operation completes.

Source: Coding guidelines

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

Outside diff comments:
In `@Sources/ArtifactSidebarPanelView.swift`:
- Around line 48-54: Add an explicit localized accessibility label to the
icon-only Button invoking presentAddPanel, using the same “Add files to
Artifacts” wording as safeHelp. Keep the existing safeHelp, disabled state, and
accessibilityIdentifier unchanged.
- Around line 82-84: Update the retry action in ArtifactSidebarPanelView and the
corresponding action at the other referenced location so each asynchronous
refresh is tied to an owned lifecycle. Store the Task in the model or an
appropriate view-owned property, cancel any existing or obsolete task when the
sidebar is hidden, rebound, or destroyed, and clear the reference when the
operation completes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8957251d-8662-4ab6-a31e-997534edc37f

📥 Commits

Reviewing files that changed from the base of the PR and between 4837f70 and fef3f38.

📒 Files selected for processing (14)
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactDigestCalculator.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactGitIgnoreManager.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSourceSnapshot.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactStorePaths.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactTreeScanner.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactDigestCalculatorTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactSourceSnapshotTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/LocalArtifactRepositoryTests.swift
  • Sources/ArtifactSidebarPanelView.swift
  • Sources/Mobile/AgentChat/AgentChatTranscriptService+ArtifactCapture.swift
  • Sources/Mobile/AgentChat/AgentChatTranscriptService.swift
  • Sources/RightSidebarPanelView.swift
  • Sources/TerminalController+MobileChatArtifacts.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (3)
Sources/ArtifactSidebarPanelView.swift (1)

173-175: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Move manual capture into a model-owned cancellable operation.

This fire-and-forget task can outlive the panel; onDisappear only stops watcher and search tasks. Have ArtifactSidebarModel retain/cancel the capture task in stop()/deinit, then invoke a synchronous startAddingFiles(_:) entry point here.

Call-site direction
- Task { await model.addFiles(urls) }
+ model.startAddingFiles(urls)

As per coding guidelines, meaningful Task { ... } work must be stored, cancellable, or tied to a caller-owned operation.

🤖 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/ArtifactSidebarPanelView.swift` around lines 173 - 175, Replace the
fire-and-forget Task at the panel call site with the model’s synchronous
startAddingFiles(_:) entry point. In ArtifactSidebarModel, retain the capture
task, cancel it from stop() and deinit, and ensure the operation’s cancellation
is handled consistently with the existing watcher and search tasks.

Source: Coding guidelines

Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarModel.swift (2)

281-287: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Flatten into one accumulator instead of repeatedly copying subtrees.

Each recursive call builds an array and every ancestor copies all descendant rows via append(contentsOf:). A fully expanded tree therefore becomes O(n²) copies and transient allocations on each rebuild; use one inout result buffer.

Proposed fix
 private func flattened(nodes: [ArtifactNode], depth: Int) -> [ArtifactSidebarRowSnapshot] {
-    nodes.flatMap { node in
-        var result = [row(node: node, depth: depth)]
-        if node.isDirectory, expandedPaths.contains(node.relativePath) {
-            result.append(contentsOf: flattened(nodes: node.children, depth: depth + 1))
-        }
-        return result
+    var result: [ArtifactSidebarRowSnapshot] = []
+    appendFlattened(nodes: nodes, depth: depth, into: &result)
+    return result
+}
+
+private func appendFlattened(
+    nodes: [ArtifactNode],
+    depth: Int,
+    into result: inout [ArtifactSidebarRowSnapshot]
+) {
+    for node in nodes {
+        result.append(row(node: node, depth: depth))
+        if node.isDirectory, expandedPaths.contains(node.relativePath) {
+            appendFlattened(nodes: node.children, depth: depth + 1, into: &result)
+        }
     }
 }

As per coding guidelines, avoid per-item nested work over scalable collections. As per path instructions, apply algorithmic-complexity.md to production Swift paths.

🤖 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
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarModel.swift`
around lines 281 - 287, Update flattened(nodes:depth:) to accumulate rows into a
single inout [ArtifactSidebarRowSnapshot] buffer rather than returning and
repeatedly appending arrays from recursive calls. Preserve the existing node
ordering, depth values, and expanded-directory filtering while eliminating
per-subtree array copies.

Sources: Coding guidelines, Path instructions


167-185: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep capture completion bound to the workspace that started it.

After captureService.add suspends, this reads the current bindingRevision. If a rebind or stop() occurred, it reloads the old projectRoot using the new revision, so the stale-result guard accepts it and can restore rows/phase for an inactive or different workspace.

Proposed fix
 public func addFiles(_ urls: [URL]) async {
     guard let projectRoot, let workspace else { return }
+    let revision = bindingRevision
     actionFailure = nil
     let context = ArtifactCaptureContext(
         projectRoot: projectRoot,
         workspaceID: workspace.id,
         workspaceTitle: workspace.title
@@
                 )
             }
+            guard revision == bindingRevision, self.projectRoot == projectRoot else { return }
-            await reload(projectRoot: projectRoot, revision: bindingRevision)
+            await reload(projectRoot: projectRoot, revision: revision)
         } catch {
+            guard revision == bindingRevision, self.projectRoot == projectRoot else { return }
             actionFailure = .add
         }
     }
🤖 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
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarModel.swift`
around lines 167 - 185, Update addFiles to capture the binding revision
alongside projectRoot and workspace before the asynchronous capture loop begins,
then pass that captured revision to reload instead of reading the mutable
bindingRevision after await. Ensure the completion remains associated with the
workspace and binding that initiated the operation, so rebinding or stop()
causes the stale-result guard to reject it.
🤖 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 `@Sources/Mobile/AgentChat/AgentArtifactCaptureCoordinator.swift`:
- Around line 57-69: Update the completion logic in the capture coordinator
around the await captureService.capture loop so
completedGenerationBySession[record.sessionID] is only advanced when
snapshot.generation is newer than the value already recorded. Preserve the
existing capture and backlog behavior while preventing an older generation from
overwriting a newer completed generation.

---

Outside diff comments:
In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarModel.swift`:
- Around line 281-287: Update flattened(nodes:depth:) to accumulate rows into a
single inout [ArtifactSidebarRowSnapshot] buffer rather than returning and
repeatedly appending arrays from recursive calls. Preserve the existing node
ordering, depth values, and expanded-directory filtering while eliminating
per-subtree array copies.
- Around line 167-185: Update addFiles to capture the binding revision alongside
projectRoot and workspace before the asynchronous capture loop begins, then pass
that captured revision to reload instead of reading the mutable bindingRevision
after await. Ensure the completion remains associated with the workspace and
binding that initiated the operation, so rebinding or stop() causes the
stale-result guard to reject it.

In `@Sources/ArtifactSidebarPanelView.swift`:
- Around line 173-175: Replace the fire-and-forget Task at the panel call site
with the model’s synchronous startAddingFiles(_:) entry point. In
ArtifactSidebarModel, retain the capture task, cancel it from stop() and deinit,
and ensure the operation’s cancellation is handled consistently with the
existing watcher and search tasks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 929b0d54-1597-487d-bff1-468dae99a84c

📥 Commits

Reviewing files that changed from the base of the PR and between fef3f38 and 4ba8b67.

📒 Files selected for processing (14)
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureConfiguration.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureService.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactDeduplicationIndexBuilder.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactImportAttempt.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarModel.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactStoring.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactTreeScanner.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/PreparedArtifactImport.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactSidebarModelTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/LocalArtifactRepositoryTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/SidebarArtifactStore.swift
  • Sources/ArtifactSidebarPanelView.swift
  • Sources/Mobile/AgentChat/AgentArtifactCaptureCoordinator.swift

Comment thread Sources/Mobile/AgentChat/AgentArtifactCaptureCoordinator.swift Outdated
@austinywang

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (2)
Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift (1)

33-46: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Serialize provenance read-modify-write operations.

Concurrent calls for the same digest can both read the same document, append different events, and then atomically replace the file; the last writer wins and silently drops the other event. Route per-store recording through an actor or serialized repository operation, and add a concurrent recording test.

As per coding guidelines, use actor-based ownership rather than adding a manual lock for shared asynchronous state.

🤖 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
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift`
around lines 33 - 46, Serialize the read-modify-write flow containing
document(paths:digest:), event append, and metadataURL write by moving per-store
recording state into an actor or actor-owned repository; avoid manual locks.
Ensure concurrent recordings for the same digest retain every event, and add a
concurrent recording test covering this behavior.

Source: Coding guidelines

Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactPathResolver.swift (1)

66-75: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make destination selection atomic with the copy.

uniqueDestination only checks whether a candidate exists and then returns it. Concurrent captures can both select the same URL, causing one import to fail or overwrite another artifact. Reserve the destination through a per-store actor/serialized import operation, or use exclusive-create-and-retry at the write boundary.

As per coding guidelines, use actor-based ownership rather than adding a manual lock for shared asynchronous state.

🤖 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
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactPathResolver.swift`
around lines 66 - 75, Update uniqueDestination and the corresponding import flow
to reserve each selected destination atomically with the copy, preventing
concurrent captures from choosing the same URL. Prefer actor-based ownership or
a serialized per-store import operation; alternatively, perform exclusive
creation at the write boundary and retry candidate selection on collision. Do
not rely on fileExists checks alone or add a manual lock.

Source: Coding guidelines

🤖 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
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactDeduplicationIndexBuilder.swift`:
- Around line 17-18: Update the deduplication index-building flow around
recorder.document and existingByDigest so a single metadata read failure does
not clear the entire batch’s deduplication state. Continue resolving the
affected digest through the fallback scanner, or propagate the error to fail the
import closed; never treat all prepared items as new because one provenance file
is corrupt or unreadable.

In `@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactNode.swift`:
- Around line 59-64: The tree-flattening helper is internal, forcing duplicate
traversal logic in the CLI. In
Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactNode.swift#L59-L64,
make flattenedArtifactNodes() public; in CLI/CMUXCLI+Artifacts.swift#L231-L235,
remove artifactFiles(in:) and replace its call sites with
nodes.flattenedArtifactNodes().filter { !$0.isDirectory }.

In `@Sources/Mobile/AgentChat/AgentChatTranscriptResolver.swift`:
- Line 61: Update transcriptPath(for:) so Task.isCancelled does not return nil,
which represents a missing transcript to
AgentChatTranscriptService.ensureTailer(for:). Propagate cancellation
distinctly, preferably by throwing CancellationError or using a dedicated
result, and update callers to preserve cancellation without caching the session
in failedResolutions.

In `@Sources/Mobile/AgentChat/AgentChatTranscriptService`+ArtifactCapture.swift:
- Around line 49-56: Update replaceArtifactCaptureTask to associate each stored
task with a per-session generation token, and have the task remove its
dictionary entry upon completion or cancellation only when that token still
matches the current replacement. In
Sources/Mobile/AgentChat/AgentChatTranscriptService+ArtifactCapture.swift lines
49-56, prevent older tasks from removing newer tasks; in
Sources/Mobile/AgentChat/AgentChatTranscriptService.swift line 18, ensure the
artifactCaptureTasks storage retains only active capture tasks rather than
completed historical-session handles.

---

Outside diff comments:
In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactPathResolver.swift`:
- Around line 66-75: Update uniqueDestination and the corresponding import flow
to reserve each selected destination atomically with the copy, preventing
concurrent captures from choosing the same URL. Prefer actor-based ownership or
a serialized per-store import operation; alternatively, perform exclusive
creation at the write boundary and retry candidate selection on collision. Do
not rely on fileExists checks alone or add a manual lock.

In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift`:
- Around line 33-46: Serialize the read-modify-write flow containing
document(paths:digest:), event append, and metadataURL write by moving per-store
recording state into an actor or actor-owned repository; avoid manual locks.
Ensure concurrent recordings for the same digest retain every event, and add a
concurrent recording test covering this behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3acf0e8a-dd2f-4909-ae70-935c42fcc982

📥 Commits

Reviewing files that changed from the base of the PR and between 4ba8b67 and ebb60f1.

📒 Files selected for processing (21)
  • CLI/CMUXCLI+Artifacts.swift
  • Packages/iOS/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Artifacts/ChatArtifactViewerPager.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureService.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactDeduplicationIndexBuilder.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactGitIgnoreManager.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactNode.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactPathResolver.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSearchEngine.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSidebarModel.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSkipReason.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactTreeScanner.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactRepositorySafetyTests.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/DirectoryEnumerationRecordingFileManager.swift
  • Resources/Localizable.xcstrings
  • Sources/ArtifactSidebarPanelView.swift
  • Sources/Mobile/AgentChat/AgentArtifactCaptureCoordinator.swift
  • Sources/Mobile/AgentChat/AgentChatTranscriptResolver.swift
  • Sources/Mobile/AgentChat/AgentChatTranscriptService+ArtifactCapture.swift
  • Sources/Mobile/AgentChat/AgentChatTranscriptService.swift

Comment thread Sources/Mobile/AgentChat/AgentChatTranscriptResolver.swift Outdated
@austinywang

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift (1)

37-50: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject provenance whose embedded digest does not match its filename.

A decodable <digest>.json with a different document.digest is appended to and rewritten, leaving content-addressed metadata internally inconsistent. Fail closed before mutation (and add a valid-JSON/wrong-digest regression test).

Proposed fix
         var document = try document(paths: paths, digest: digest) ?? ArtifactMetadataDocument(
             version: 1,
             digest: digest,
             lastKnownRelativePath: relativePath,
             size: size,
             events: []
         )
+        guard document.digest == digest, document.size == size else {
+            throw ArtifactStoreError.corruptProvenance(
+                metadataURL(paths: paths, digest: digest).path
+            )
+        }
         document.lastKnownRelativePath = relativePath
🤖 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
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift`
around lines 37 - 50, Validate the decoded document’s digest against the
requested digest immediately after the document(paths:digest:) result is loaded
and before updating lastKnownRelativePath or events. Reject mismatches by
throwing, leaving the existing metadata file untouched; preserve creation of new
documents and normal updates when the digests match. Add a regression test
covering valid JSON whose embedded digest differs from the filename digest.
♻️ Duplicate comments (1)
Sources/Mobile/AgentChat/AgentArtifactCaptureCoordinator.swift (1)

71-74: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Prevent stale snapshots from regressing completion state.

The final ownership check only protects overlapping captures. If a newer snapshot completes first and an older snapshot is delivered afterward, the older generation passes the equality guards and overwrites completedGenerationBySession; the newer snapshot is then captured again. Compare against the authoritative generation ordering (or reject stale snapshots) before updating completion state, and add a test for this sequential stale-delivery case.

🤖 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/Mobile/AgentChat/AgentArtifactCaptureCoordinator.swift` around lines
71 - 74, Update the final completion-state handling in the capture coordinator
around the inFlightGenerationBySession guard to reject snapshots whose
generation is older than the authoritative completed or current generation, not
merely snapshots matching the in-flight generation. Prevent stale delivery from
overwriting completedGenerationBySession or triggering a recapture, and add
coverage for a newer snapshot completing before an older snapshot is delivered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift`:
- Around line 37-50: Validate the decoded document’s digest against the
requested digest immediately after the document(paths:digest:) result is loaded
and before updating lastKnownRelativePath or events. Reject mismatches by
throwing, leaving the existing metadata file untouched; preserve creation of new
documents and normal updates when the digests match. Add a regression test
covering valid JSON whose embedded digest differs from the filename digest.

---

Duplicate comments:
In `@Sources/Mobile/AgentChat/AgentArtifactCaptureCoordinator.swift`:
- Around line 71-74: Update the final completion-state handling in the capture
coordinator around the inFlightGenerationBySession guard to reject snapshots
whose generation is older than the authoritative completed or current
generation, not merely snapshots matching the in-flight generation. Prevent
stale delivery from overwriting completedGenerationBySession or triggering a
recapture, and add coverage for a newer snapshot completing before an older
snapshot is delivered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5b9c209c-8fb0-451b-a48b-5679e68bf874

📥 Commits

Reviewing files that changed from the base of the PR and between ebb60f1 and 3939d8d.

📒 Files selected for processing (11)
  • CLI/CMUXCLI+Artifacts.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactCaptureService.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactProvenanceRecorder.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactSkipReason.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/ArtifactStoreError.swift
  • Packages/macOS/CmuxArtifacts/Sources/CmuxArtifacts/LocalArtifactRepository.swift
  • Packages/macOS/CmuxArtifacts/Tests/CmuxArtifactsTests/ArtifactRepositorySafetyTests.swift
  • Resources/Localizable.xcstrings
  • Sources/Mobile/AgentChat/AgentArtifactCaptureCoordinator.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/AgentArtifactCaptureCoordinatorTests.swift

@austinywang
austinywang force-pushed the feat-cmux-artifacts branch 2 times, most recently from 6050cb6 to 6edb532 Compare September 9, 2026 01:49

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

Stale Bugbot comment from a previous run.

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 968927a. Configure here.

@teamleaderleo teamleaderleo added area: agents Agent integrations (Claude Code, Codex, ACP), agent chat, hooks, status S3: minor Wrong behavior with a workaround labels Sep 30, 2026

This branch was successfully deployed

2 active deployments
Preview – cmux41 — 6c4bbbce Deployed Sep 9, 2026 by vercel[bot]
Preview – cmux166 — 6c4bbbce Deployed Sep 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: agents Agent integrations (Claude Code, Codex, ACP), agent chat, hooks, status S3: minor Wrong behavior with a workaround

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants