Repository navigation
cmux NEXT: a one-time sidebar note and an onboarding line for Import from Conductor - #36453
Conversation
…tor on the daemon
conductor_import.plan (read) finds Conductor under the daemon's HOME and
returns {found, plan}: the cmux-conductor-import dry run against the
stored ledger and the workspaces open now (catalog_from_folder when
conductor.db is gone); plan is null when nothing is found, and error says
why when there is no plan (an unreadable conductor.db, or a ledger from a
newer cmux, which is left as it is).
conductor_import.record (mutation, idempotent) sets one workspace's
stage, pending (with an optional half-made cmux workspace) or done, and
run_completed counts a run. A done workspace is named by its key (the key
the app created it with) or its public id; the plan counts it open by either. The ledger is one JSON row in the personal
state store, committed and replayed like every state mutation.
Pages and apps never reach either op; MCP excludes them (the app's
Import from Conductor drives these). Capability conductor-import-v1.
Tests: cmux-tui-core state::conductor_import_tests (4) and cmux-tui
--test conductor_import_daemon over a real headless daemon.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293
Settings › Import gets a Conductor card when this Mac has Conductor's database or ~/conductor/workspaces. Import asks the daemon for its dry-run plan (conductor_import.plan), shows it (projects, the workspaces opened in place with their branches, Claude Code chats found, archived workspaces left out, env var names only, the archive-deletes-folder warning and the Nov 20 deadline), and on confirm adds each repository as a project and opens each live workspace at its own folder, recording each one in the import's ledger before and after (conductor_import.record), so a rerun makes nothing twice and a cut-off run's half-made workspace is replaced. The flow lives in CmuxNextImportCards behind ConductorImportHost; the App adds one-line forwards only. ClassicImportPlan moves to CmuxNextOnboarding so the App does not grow. Strings are in the ImportSuggestions table in every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293
…from Conductor Leo, 2026-11-05: "i meant can you do all of htem" (the ranked list of moves; #3 is Import from Conductor). Conductor Cloud goes read-only on November 13 and shuts down on November 20, and the spec asks for "one onboarding line, Settings → Import next to browser import, and a one-time sidebar note with the Nov 20 deadline". Settings › Import came with the previous change; this adds the other two. The sidebar note is the shared notice card ("Bring your Conductor workspaces", the November 20 deadline, Open Import, which opens Settings › Import). It shows only while the import scan found Conductor on this Mac (agents.conductor), before 2026-11-20 (midnight Pacific), and until the person closes it, starts the import, or hides the Conductor card in Settings › Import (its Done after the import does). Its x closes it for good, kept in the import cards' own saved state (ImportSuggestionState, a new optional field, so older saved state still loads). In the card chooser it comes after the update status and the test-feed notice and waits for the "cmux Updated!" card, like the rest, and comes before announcements and the daily tip: it is the one time-limited message of the three. The first-run Bring your history page gets one line under its sources when this Mac has Conductor, before the deadline: "Conductor workspaces: import them from Settings › Import before Conductor Cloud shuts down on November 20." The page's provider takes the line from the App (read off the main thread) and returns it with its detection. Nothing goes on the New Tab page or the Chief's front page. The decision and the strings live in CmuxNextImportSuggestions, the card in CmuxNextImportCards (now on CmuxNextSidebar for the notice card's type); the App adds the wiring only, and ClassicSnapshotRead moves to CmuxNextOnboarding so the App does not grow. Strings are in the ImportSuggestions table in every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293
teamleaderleo
left a comment
There was a problem hiding this comment.
Review (gpt-6-astra): SHIP at 08b9c45
…tor-import-app # Conflicts: # cmux-tui/bindings/cpp/.cmux-resource-api.json # cmux-tui/bindings/go/.cmux-resource-api.json # cmux-tui/bindings/java/.cmux-resource-api.json # cmux-tui/bindings/python/.cmux-resource-api.json # cmux-tui/bindings/rust/.cmux-resource-api.json # cmux-tui/bindings/typescript/.cmux-resource-api.json # cmux-tui/bindings/zig/.cmux-resource-api.json
…lane/conductor-import-notes
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Gallery before/after for
States
Pick other states with a Shipped in:
|
|
Review (codex, router-review): SHIP at b940ac4 Reviewer: codex gpt-6.1-sol xhigh, fast tier. Follow-ups: 3. They land as checklist items in this PR's one follow-up tracking issue in this repo ( SHIP at b940ac4 Visual verdict: no images; Leo tries the build
Follow-ups:
|
* Import from Conductor: the scan counts workspace folders, not files Review follow-up on #36453 (the Conductor card): `ImportScanner.conductor()` counted every visible entry under `~/conductor/workspaces/<repo>/` as a workspace, so a file left there (a note, an editor's leftovers) raised the card's count, and a file at the repo level was listed as a repo. It now counts folders only, through `subfolders(_:)`, which asks for the directory flag with the one directory read, so the scan stays one listing per folder. It reads through CmuxNextCompat's checked seams (`FileManager.checked`, `blockingResourceValues`), so CmuxNextImportSuggestions now lists CmuxNextCompat, already in its graph through CmuxNextBrowserImport. Leo, 2026-11-05: "i meant can you do all of htem" (Import from Conductor is Test: ImportScannerTests.conductorCountsItsWorkspaceFolders gains a home with a file at each level (counted 2 before, 1 now). Swift tests run on the fleet, not here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293 * Import from Conductor: a ledger that breaks its invariants is unreadable Review follow-up on #36326 / #36389: `Ledger::decode` (and so `usable` in the daemon's router) took any ledger that parsed as usable. A row such as `{"version":1,"runs":0,"workspaces":{"w":{"stage":"done"}}}` has a done entry with no cmux workspace: the plan would read it as closed since and offer it again, so a rerun could make the workspace twice. Decoding now checks what every record keeps (a done entry names its cmux workspace; no entry names an empty one) and returns `Load::Unreadable` with the reason, so `conductor_import.plan` shows no plan and says why, and `conductor_import.record` refuses (`validation.invalid`), as they already did for a ledger that does not parse. Tests (cargo, Linux): ledger::tests::a_done_entry_without_its_cmux_workspace_is_unreadable and conductor_import_tests::conductor_import_refuses_a_damaged_ledger fail with the check taken out and pass with it; cargo test -p cmux-conductor-import, -p cmux-tui-core --lib conductor_import, -p cmux-tui --test conductor_import_daemon and clippy -D warnings pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293 * Import from Conductor: a record never trades an open done workspace for another Review follow-up on #36389: `Record::apply` replaced a workspace's ledger entry unconditionally, so two overlapping runs (different idempotency keys, both planning the workspace as one to adopt) could each make it and the last done won, leaving a duplicate the ledger no longer knew about. `conductor_import.record` now refuses (`validation.invalid`) a pending or done record over an entry that is done while its cmux workspace is still open, unless the record names that same workspace (by key or public id). The check reads the open workspaces from the state `commit_state` hands its apply step and runs inside its transaction, so two runs cannot both pass it. Checking done as well as pending closes the interleaving where both runs record pending before either records done: the second done is refused. Unchanged: a done entry whose workspace was closed is offered again and may be recorded over, pending replaces pending, and recording the same workspace again is fine. Tests (cargo, Linux): conductor_import_tests::conductor_import_record_keeps_an_open_done_workspace fails with the check taken out (the second run's pending is accepted) and passes with it; the crate, core and daemon suites and clippy -D warnings pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293 * Import from Conductor: the note and the onboarding line end on time and stay gone Two review follow-ups on #36453 (the sidebar note and the first-run history page line): Deadline while visible. Both were evaluated only when their inputs changed, so with cmux open across 2026-11-20T08:00Z they stayed up. The center now schedules one DemandTimer deadline from its injected clock (`scheduleConductorDeadline()`, from `start`), which sets an observed `conductorDeadlinePassed` the note reads, so a note on screen is read again and goes; started after the deadline it is gone at once. The history page gets the line's end with it (`noticeUntil`, from `ImportScanner.conductorOnboardingNotice(now:)`), and the page store sets one timer to that moment and drops the line (a far end waits again from setTimeout's limit). No timer that wakes repeatedly. Note came back after pruning. The note's "the import ran" test read the job ledger, which drops finished jobs after 14 days. A finished Conductor import is now kept in `ImportSuggestionState.conductorImportRan`, a new optional field like `conductorNoteClosed`, so older saved state still loads; the note checks it. A done Conductor job already in the ledger sets it when the jobs are restored, before the prune. A declined preview keeps nothing, as before. The App's only change is the one line that hands the page the notice (same line count). CmuxNextImportCards now lists CmuxNextWakeups (for DemandTimer), already in its graph through CmuxNextBrowserImport. Tests: ConductorNoteTests.theNoteStaysGoneAfterTheFinishedJobIsPruned and the older-state test's nil check; ImportSuggestionsCenterConductorTests (the note goes at the deadline by an observation change with the clock frozen before it, gone at once past it, a finished import keeps it gone after its job is removed, a declined one does not); ImportHistoryPageProviderTests.detectionSendsTheNoticeWithItsEnd; and the webview model test "the host's line about Conductor goes at its end while the page stays open" (fake timers), which fails with the timer taken out and passes with it (bun test, 19 pass). The Swift tests run on the fleet, not here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293 * Import from Conductor: the run stops on a failed close, keeps unadded repos importable, and previews whenever there is something to say Three review follow-ups on #36453's `ConductorImport.importing(progress:)`: Failed cleanup. Each workspace a cut-off run half made was closed with `try?`, so a close that failed still went on to make its replacement and left two. A failed close now stops the run (its toast, then the throw, as every other failure); a workspace already gone (no key in the store, or the daemon's `selector.not_found` for it) is no failure. Ignored project adds. `try? projects.add` dropped errors, and that repo's workspaces were then recorded done, so the repo never reached the project list, even after Import Again. Chosen: a repository that cannot be added keeps its workspaces unrecorded (they are skipped, so Import Again offers them with it), the other repositories still come over, the run is not counted complete, and it ends failed (`ConductorImportFailure.projects`). Why not stop the whole run up front: a refusal of a path (the daemon refuses some folders outright) repeats on every run, so one such repo would block every other repo's import for good; skipping its workspaces loses nothing and the failed toast still says something went wrong. Project adds now use a per-run idempotency key, so a project removed since an earlier run is added again rather than replayed as done. Recoverable-only and project-only plans. The run toasted "Every Conductor workspace is already open here." and skipped the preview whenever nothing was adoptable, even with archived recoverable workspaces or repositories not listed yet. It now reads the project list once and shows the preview whenever there is a workspace to open, a repository to add, a recoverable workspace to explain or a cut-off workspace to replace; "all here" stays only for nothing at all. After a run that opened nothing with recoverable workspaces left, the toast is the preview's existing recoverable line. No new strings. To test this without a daemon, the run's daemon calls go through `ConductorImportDaemon` (`LiveConductorImportDaemon` on the App's connection) and the preview through an injected confirm; the public init and the App are unchanged. Tests: ConductorImportRunTests (CmuxNextAppTests) with a fake daemon and host: a failed cleanup close stops the run with nothing opened or recorded; a cleanup workspace already gone is no failure; a failed project add leaves its workspaces unrecorded while the other repo's open; a project-only and a recoverable-only plan show the preview; nothing left shows none. Each but the already-gone case fails against the old flow. The Swift tests run on the fleet, not here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293 * Import from Conductor: a done record follows its own pending record Review of #36784 (F1): two runs that both record pending before either records done still each made a workspace, and only the earlier done was refused after its workspace existed. `conductor_import.record` now refuses a done record that names another workspace than the pending entry, and the app closes the workspace it just made when its done record is refused, so the ledger's workspace is the only one left open. Leo: "i meant can you do all of htem" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293 * Import from Conductor: close a made workspace only on a confirmed conflict Review of #36784 at 512ab19 (F1): the run closed the workspace it made on any failed done record, but a timeout or dropped connection can come after the daemon kept the record, which closed a good import. The conflict refusal now names the field `cmux_workspace`, and only that refusal closes the workspace (and a close that fails then stops the run with its error). Any other failure leaves it open; the ledger's pending entry still names it, so the next run's plan cleans it up if the record was not kept. Leo: "i meant can you do all of htem" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7927be6a5485e9de6293 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Leo, 2026-11-05, on the ranked list of next moves (Import from Conductor is #3): "i meant can you do all of htem".
The spec for this move asks for "one onboarding line, Settings → Import next to browser import, and a one-time sidebar note with the Nov 20 deadline". #36364 adds Settings › Import. This PR adds the other two. It is stacked on #36364, which is stacked on #36389, and it lands after both.
import.open.How it is built:
CmuxNextImportSuggestions, and the card and its buttons inCmuxNextImportCards, which now depends onCmuxNextSidebarfor the card type.ClassicSnapshotReadmoves toCmuxNextOnboarding, so the App shrinks by one line.ImportSuggestionstable, translated into all 20 other locales.Observed / Not run
prepush-gate: no new violations.lint-ios-package-conventions.shis clean.scripts/web-lint.shandbun run checkinwebviews/pass.model.test.tspasses 18 of 18, including the new notice test.ConductorNoteTests(Conductor found, not imported, before the deadline; no Conductor; at and after the deadline with an injected clock; closed, including across save and load; a running or done job; a hidden card; older saved state; the onboarding line with and without Conductor),ConductorImportWireTests,ImportScannerTests,ImportCardDeckTests,BrowserImportEnabledTests,ImportSuggestionRemovedTests,ImportSuggestionsCenterClassicTests,ClassicImportTests.No capture: the note and the line show only on a Mac with Conductor data; a capture follows once the stack is in.
Changelog
Added: A one-time sidebar note and a first-run line point people with Conductor to Settings › Import before Conductor Cloud shuts down on November 20.
🤖 Generated with Claude Code
Agent session: agent session