diff --git a/CLAUDE.md b/CLAUDE.md index 73e2e6f330c5..d8cd49696024 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,13 +8,12 @@ Before committing, setup or a native build, [choose verification for the changed ## Dev builds on the Mac mini fleet -For team dev builds, use the controller client `~/.local/bin/cmux-ci`. The Mac -mini fleet is **dev-build-only**. Owned minis will take pull request jobs -through the pool picker (`scripts/ci/pr_runner_pool.py`, `POOLS`); the earlier -persistent compile pilot is retired. Release, signing, notarization, nightly, -TestFlight, merge-queue policy, generic agent execution, and every GUI or -runtime test remain on their existing lanes. A successful dev build never -replaces a required check. +For team dev builds, use the controller client `~/.local/bin/cmux-ci`. Owned +minis also take pull request CI jobs (compile admission, app-host shards, side +lanes) through the pool picker (`scripts/ci/pr_runner_pool.py`), with Blacksmith +as overflow; see [CI runners](docs/ci-runners.md). Release, signing, +notarization, nightly and TestFlight stay on Blacksmith. A successful dev build +never replaces a required check. Before submitting, read the current [HQ AGENTS.md](https://github.com/manaflow-ai/cmuxterm-hq/blob/main/AGENTS.md) and [agent build contract](https://github.com/manaflow-ai/cmuxterm-hq/blob/main/build-fleet/AGENT-BUILDS.md). @@ -22,17 +21,18 @@ These are the authoritative fleet instructions even when an old PR worktree has copied instructions. `AGENTS.md` in this repository is a symlink to this file. Commit and push the intended edits first. This builds the exact pushed SHA; -it does not upload dirty local edits. Use the PR owner's GitHub login for -`SUBMITTER` (for example `lawrencecchen` or `austinywang`), the full PR URL for -`PR_URL`, and preserve both receipts: +it does not upload dirty local edits. `--tag` is required: pick a descriptive +tag and a new iteration suffix per submission. Put the full PR URL in +`--workspace`. The controller records the submitter from your personal client +token, so `--submitter` is not needed. Preserve both receipts: ```bash SHA=$(git rev-parse HEAD) PR_URL=https://github.com/manaflow-ai/cmux/pull/123 -SUBMITTER=lawrencecchen +TAG=pr-123-sidebar-star-align-v1 mkdir -p artifacts/fleet -JOB_JSON=$(~/.local/bin/cmux-ci build cmux --ref "$SHA" \ - --workspace "$PR_URL" --submitter "$SUBMITTER" \ +JOB_JSON=$(~/.local/bin/cmux-ci build cmux --ref "$SHA" --tag "$TAG" \ + --workspace "$PR_URL" \ --receipt "artifacts/fleet/$SHA-submit.json") JOB_ID=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"$JOB_JSON") ~/.local/bin/cmux-ci wait "$JOB_ID" --receipt "artifacts/fleet/$SHA-terminal.json" && \ @@ -45,11 +45,11 @@ requesting agent, then the receipts and HQ download link when complete. If continues if the submitting laptop disconnects. The installed client loads a private credential file; never print it or copy secrets into PR evidence. -Use the client's workload defaults: **120 GiB for CMUX**, **250 GiB for a cold -Chromium build**. The former blanket 250 GiB CMUX requirement is obsolete. -Do not copy it into new requests or bypass a rejection with an arbitrary lower -floor. A validated Chromium warm profile may use 200 GiB through the runbook's -compatibility-receipt workflow. Report a controller/worker policy mismatch; +Use the client's workload defaults: **80 GiB for CMUX**, **180 GiB for a cold +Chromium build**. The former 120 GiB and blanket 250 GiB CMUX requirements are +obsolete. Do not copy them into new requests or bypass a rejection with an +arbitrary lower floor. A validated Chromium warm profile may use 120 GiB through +the runbook's compatibility-receipt workflow. Report a controller/worker policy mismatch; queueing is not permission to build over SSH. The disk daemon owns cleanup under the host lock. Do not remove shared caches, @@ -100,7 +100,9 @@ substitute a raw `.app` path or a `file://` URL. For standalone contributors without the team controller, the local workflow is `./scripts/reload.sh --tag ` (build without launch) or the same -command with `--launch`. This is not a queue-bypass fallback for team agents. +command with `--launch`. In a checkout not created through +cmuxterm-hq, set `CMUX_DEV_BACKEND_MODE=local`; the default shared dev backend +refuses otherwise. This is not a queue-bypass fallback for team agents. Other local variants remain `reloadp.sh` (Release), `reloads.sh` (isolated Release staging), and `reload2.sh --tag ` (both). Local compile-only checks must use the tagged DerivedData directory rather than an untagged default. @@ -211,9 +213,12 @@ the broad suite; state which additional lanes are needed and why. Normal PR CI can already run routed tests, including Swift package and CLI wrapper checks, without `full-ci`. A `cmuxTests/` diff runs the suites it -declares or extends on one app-host worker, with no label. `unit-ci` runs every -app-host suite across all seven workers; `full-ci` adds the other lanes on top. -Neither is needed to test the suites you edited. The label permits eligible app-host shards, +declares or extends, and an app-source diff runs the suites whose tests mention +what it changed (`reverse_test_impact.py`, #14418), in one changed-suites batch +with no label (edited suites over its budget take all seven shards). `unit-ci` runs +every app-host suite across all seven workers; `full-ci` adds the other lanes on +top. Neither is needed to test the suites you edited. No PR job runs +`cmuxUITests/`; `no-full-ci` records a deliberate skip for `suite-coverage`. The label permits eligible app-host shards, lag builds, and other full-suite lanes; path routing, release routing, and job dependencies still apply. It does not request every repository test. Inspect actual executed tests on the current SHA: a green skipped job is not coverage. @@ -236,6 +241,8 @@ A first pass ends when the change is implemented, [scoped verification](skills/c Do not launch a background review agent (`$autoreview`, `codex review`, `claude review`, or a judge loop) by default. Second-model review is explicit user opt-in in the current conversation; an implementation request, open PR, CI failure, closeout, or handoff is not that opt-in. Let required GitHub checks and review bots run asynchronously, then return to address only concrete check failures and actionable findings before merge. +**Merge fast, not blind.** `main` is our nightly: stack fixes, do not revert. Before merging, wait for the checks that judge the change (macOS compile admission plus the app-host suites CI selected for it) and skip slow unrelated lanes. If you merge without them, say on the PR what was not verified; the merge receipt (`merge_receipt.py`) records it and labels the PR `merged-unverified`. A main-regression comment on your PR (`main_regression_attribution.py`) is a fix-forward ask. + The main agent owns dogfood, approval, mergeability, and every pushed fix. Merging app/runtime/UI changes requires the user's explicit approval after dogfood; if a fix changes runtime behavior mid-dogfood, rebuild the tag and re-notify, since the earlier verdict covers only the build the user tested. Notify through `cmux notify` so the user can leave and return. Handoff: `--title "Dogfood ready: " --subtitle " · " --body "Was: . Now: . . PR: "`. Later closeout notifications use `"CI green: "` or `"CI blocked: "` with a one-line cause and the next decision. Titles carry outcome and branch, bodies carry the single next action. Skip notify if there is no cmux socket. @@ -273,19 +280,19 @@ reasoning about cache warmth. Each of these has full detail in the skill named in parentheses. -- **Typing-latency-sensitive paths** (`cmux-debugging`): `WindowTerminalHostView.hitTest()` in `TerminalWindowPortal.swift`, `TabItemView` in `ContentView.swift`, and `TerminalSurface.forceRefresh()` in `GhosttyTerminalView.swift` run on every keystroke. Read the skill before touching them. +- **Typing-latency-sensitive paths** (`cmux-debugging`): `WindowTerminalHostView.hitTest()` in `TerminalWindowPortal.swift`, `TabItemView` in `ContentView.swift`, and `TerminalSurface.forceRefresh()` in `Packages/macOS/CmuxTerminal` run on every keystroke. Read the skill before touching them. - **SwiftUI list boundaries** (`cmux-debugging`): no view below a `LazyVStack`/`LazyHStack`/`List`/`ForEach` boundary may hold an observable store reference, and no function called from `body` may write state. Violating either reintroduces the 100% CPU spin loop from https://github.com/manaflow-ai/cmux/issues/2586. Reference pattern: `IndexSectionActions` / `SectionGapActions` / `SessionSearchFn` in `Sources/SessionIndexView.swift`. - **Do not add an app-level display link or manual `ghostty_surface_draw` loop.** Rely on Ghostty wakeups and its renderer, or typing lags. - **Terminal find layering** (`cmux-debugging`): `SurfaceSearchOverlay` mounts from `GhosttySurfaceScrollView` in `Sources/GhosttyTerminalView.swift` (AppKit portal layer), never from SwiftUI panel containers such as `Sources/Panels/TerminalPanelView.swift`. Portal-hosted terminal views can sit above SwiftUI during split/workspace churn. - **Custom UTTypes** for drag-and-drop must be declared in `Resources/Info.plist` under `UTExportedTypeDeclarations` (e.g. `com.splittabbar.tabtransfer`, `com.cmux.sidebar-tab-reorder`). - **Submodule safety** (`cmux-ghostty`): push the submodule commit to its remote `main` before committing the pointer in the parent repo. Never commit on a detached HEAD. Verify with `git merge-base --is-ancestor HEAD origin/main`. -- **Localize every user-facing string** (`cmux-localization`): `String(localized:)` with keys in `Resources/Localizable.xcstrings`, plus every web locale declared by `web/i18n/routing.ts` with a matching `web/messages/.json` entry. The supported macOS app locales are English, German, French, Arabic, Spanish, Traditional Chinese, Simplified Chinese, Korean, and Japanese (`en`, `de`, `fr`, `ar`, `es`, `zh-Hant`, `zh-Hans`, `ko`, `ja`). A localization audit is required for any UI, Settings, menu, schema, docs, or help-text change, and the handoff must state what was audited. +- **Localize every user-facing string** (`cmux-localization`): `String(localized:)` with keys in `Resources/Localizable.xcstrings`, plus every web locale declared by `web/i18n/routing.ts` with a matching `web/messages/.json` entry. New macOS strings need the nine locales `scripts/localization_catalog.py` requires: English, German, French, Arabic, Spanish, Traditional Chinese, Simplified Chinese, Korean, and Japanese (`en`, `de`, `fr`, `ar`, `es`, `zh-Hant`, `zh-Hans`, `ko`, `ja`); the catalog also carries partial translations for other languages. A localization audit is required for any UI, Settings, menu, schema, docs, or help-text change, and the handoff must state what was audited. - **Shortcut policy** (`cmux-keyboard-shortcuts`): every new cmux-owned shortcut goes in `KeyboardShortcutSettings`, is editable in Settings, is supported in `~/.config/cmux/cmux.json`, and is documented. - **Test wiring** (`cmux-testing`): a `.swift` file in `cmuxTests/` without a `PBXFileReference` + `PBXSourcesBuildPhase` entry is silently skipped, and both `xcodebuild test` and bot reviews pass with "Executed 0 tests". Run `./scripts/sync-test-wiring` after adding, renaming, or deleting a direct test file; `--check` is read-only. `workflow-guard-tests` keeps `./scripts/lint-pbxproj-test-wiring.sh` as the defensive guard. - **SPM package groups** (`cmux-architecture`): packages live under `Packages/{Shared,iOS,macOS}/` and the workspace mirrors that folder shape. To move one, `git mv` the directory then `python3 scripts/check-workspace-package-groups.py --write`. Never hand-edit workspace group membership. - **Do not gitignore cmux-owned `Package.resolved`.** SwiftPM resolution changes must show in PR diffs; package-local lockfiles are not replaced by the root one. `python3 scripts/check-package-resolved-policy.py` fails on drift. - **"Feature flag" means a remote PostHog runtime flag.** Implement through `CmuxFeatureFlags` with a PostHog key, explicit unavailable fallback, registry metadata, live update behavior, and focused tests. A local override may support dogfood but must not be the production control plane. -- **Foundation, SwiftUI, AttributeGraph, and WebKit semantics change between macOS major versions.** `URL(fileURLWithPath: "/").deletingLastPathComponent().path` returns `"/.."` on macOS 14 and 15 but `"/"` on macOS 26 (https://github.com/manaflow-ai/cmux/issues/4529); CI and maintainer machines were all on the fixed side while every reporter was on the broken side. Test on the reporter's macOS before declaring a repro disproven. AWS M4 Pro builders (`aws-m4pro-1..6`) run macOS 15.7.4. +- **Foundation, SwiftUI, AttributeGraph, and WebKit semantics change between macOS major versions.** `URL(fileURLWithPath: "/").deletingLastPathComponent().path` returns `"/.."` on macOS 14 and 15 but `"/"` on macOS 26 (https://github.com/manaflow-ai/cmux/issues/4529); CI and maintainer machines were all on the fixed side while every reporter was on the broken side. Test on the reporter's macOS before declaring a repro disproven. CI's `blacksmith-6vcpu-macos-15` pool runs macOS 15; the AWS M4 Pro Tart hosts were retired in #14427. ## Shared behavior policy diff --git a/skills/cmux-architecture/SKILL.md b/skills/cmux-architecture/SKILL.md index 1f0c6f9fb321..8bb11ae93cd8 100644 --- a/skills/cmux-architecture/SKILL.md +++ b/skills/cmux-architecture/SKILL.md @@ -15,7 +15,7 @@ cmux is migrating from a single app target into Swift Packages under `Packages/` When in doubt, extract leaf-first: the package with no internal dependencies. Existing packages under `Packages/` predate this policy; do not use them as design references. -Wiring a new package into `cmux.xcodeproj` needs explicit pbxproj entries in **both** the `cmux` and `cmux-unit` targets. See [references/package-boundaries.md](references/package-boundaries.md). +Wiring a new package into `cmux.xcodeproj` needs explicit pbxproj entries in **both** the `cmux` and `cmuxTests` targets (`cmuxTests` is what the `cmux-unit` scheme runs). See [references/package-boundaries.md](references/package-boundaries.md). **Group folders.** Every package lives physically under exactly one group directory: `Packages/Shared/` (both apps), `Packages/iOS/` (iOS only), or `Packages/macOS/` (macOS only). `cmux.xcworkspace/contents.xcworkspacedata` mirrors that folder shape, with three groups whose container locations are those folders and every package directory as a FileRef under its folder's group. The folder is the source of truth: to move a package, `git mv` the directory then run `python3 scripts/check-workspace-package-groups.py --write`. Cross-group `.package(path:)` deps use `../..//`. Never hand-edit workspace group membership. CI runs `python3 scripts/check-workspace-package-groups.py --check` and fails on drift. diff --git a/skills/cmux-architecture/references/package-boundaries.md b/skills/cmux-architecture/references/package-boundaries.md index 13ab716418ed..1191c37e9c4c 100644 --- a/skills/cmux-architecture/references/package-boundaries.md +++ b/skills/cmux-architecture/references/package-boundaries.md @@ -28,7 +28,7 @@ A lower package cannot extend a higher-owned type without inverting the dependen - one `XCSwiftPackageProductDependency` - one `PBXBuildFile` linked in the Frameworks phase of every target that imports it -App-target packages link into **both** `cmux` and `cmux-unit` so tests can import and inject them. A package linked by the app but not `cmux-unit` compiles the app and fails the test target. Copy a recent leaf package for the exact shape, then run: +App-target packages link into **both** `cmux` and `cmuxTests` (the target the `cmux-unit` scheme runs) so tests can import and inject them. A package linked by the app but not `cmuxTests` compiles the app and fails the test target. Copy a recent leaf package for the exact shape, then run: ```bash scripts/normalize-pbxproj.py diff --git a/skills/cmux-cua/SKILL.md b/skills/cmux-cua/SKILL.md index ae6d05b0ceb7..bdc6fa801082 100644 --- a/skills/cmux-cua/SKILL.md +++ b/skills/cmux-cua/SKILL.md @@ -292,6 +292,7 @@ Settings → cmux Computer Use. MCP tool after the helper runtime is healthy. - Never hand-edit `docs/.../cmux-cua/mcp-tools.mdx` in the fork — it is generated from the Rust tool descriptions. -- cmux-side UX lives in `Sources/App/ComputerUse*.swift`, +- The runtime service, helper lifecycle, capture and daemon admission live in + `Packages/macOS/CmuxComputerUse/`. cmux-side UX lives in `Sources/App/ComputerUse*.swift`, `Packages/macOS/CmuxSettingsUI/.../Sections/ComputerUseSection.swift`, and the two wrappers under `Resources/bin/`. diff --git a/skills/cmux-debugging/SKILL.md b/skills/cmux-debugging/SKILL.md index 2061bfbdceb8..935576c2b149 100644 --- a/skills/cmux-debugging/SKILL.md +++ b/skills/cmux-debugging/SKILL.md @@ -30,9 +30,9 @@ DEBUG builds get a **Debug** menu in the macOS menu bar. When the user says "deb - Custom drag-and-drop UTTypes must be declared in `Resources/Info.plist` under `UTExportedTypeDeclarations`. - Do not add an app-level display link or manual `ghostty_surface_draw` loop; rely on Ghostty wakeups/renderer to avoid typing lag. -- `WindowTerminalHostView.hitTest()` in `Sources/TerminalWindowPortal.swift` runs on every event including keyboard. Add no work outside the `isPointerEvent` guard. +- `WindowTerminalHostView.hitTest()` in `Sources/TerminalWindowPortal.swift` runs on every event including keyboard. Add no work outside the `allowsPortalPointerHitTesting` guard in `performHitTest`. - `TabItemView` in `Sources/ContentView.swift` uses `Equatable` plus `.equatable()` to skip body re-evaluation during typing. Do not add environment/store/binding reads without updating `==` and keeping `.equatable()` at the call site. -- `TerminalSurface.forceRefresh()` in `Sources/GhosttyTerminalView.swift` runs on every keystroke. No allocations, file I/O, or formatting. +- `TerminalSurface.forceRefresh()` in `Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+ForceRefresh.swift` runs on every keystroke. No allocations, file I/O, or formatting. - `SurfaceSearchOverlay` must be mounted from `GhosttySurfaceScrollView` in `Sources/GhosttyTerminalView.swift`, not from SwiftUI panel containers. - Views below a `LazyVStack` / `LazyHStack` / `List` / `ForEach` boundary receive immutable snapshots plus closures, never an observable store. - Functions called from SwiftUI `body` must not mutate state or schedule store writes. diff --git a/skills/cmux-debugging/references/runtime-pitfalls.md b/skills/cmux-debugging/references/runtime-pitfalls.md index 7935d1ad43ea..d1ad465748d2 100644 --- a/skills/cmux-debugging/references/runtime-pitfalls.md +++ b/skills/cmux-debugging/references/runtime-pitfalls.md @@ -34,4 +34,4 @@ A function called from `body`, directly or through a helper, must not write obse Foundation, SwiftUI, AttributeGraph, and WebKit behavior changes silently between macOS majors. From https://github.com/manaflow-ai/cmux/issues/4529: `URL(fileURLWithPath: "/").deletingLastPathComponent().path` returns `"/.."` on macOS 14 and 15 but `"/"` on macOS 26, because Apple fixed CFURL normalization. The repo's `macos-26` CI and every maintainer's machine were on the fixed side; every reporter was on the broken side. -Test on the reporter's macOS before declaring a repro disproven. AWS M4 Pro builders (`cmux-aws-mac`, `cmux-aws-m4pro`, `aws-m4pro-1..6`) are pre-provisioned on macOS 15.7.4 and are the preferred empirical repro path. +Test on the reporter's macOS before declaring a repro disproven. CI's `blacksmith-6vcpu-macos-15` pool runs macOS 15; the AWS M4 Pro Tart hosts were retired in #14427. diff --git a/skills/cmux-review/SKILL.md b/skills/cmux-review/SKILL.md index e4cedc026beb..65f1aef22c2e 100644 --- a/skills/cmux-review/SKILL.md +++ b/skills/cmux-review/SKILL.md @@ -209,7 +209,7 @@ When the user requested repair, or the workflow explicitly allows it: Useful cmux primitives: ```bash -cmux vault checkpoint --name "pre-review-repair" +cmux vault checkpoint --agent --session --name "pre-review-repair" cmux vault checkpoints --agent --session cmux vault fork --agent --session --checkpoint --open ```