Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 32 additions & 25 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,31 @@ 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).
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" && \
Expand All @@ -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,
Expand Down Expand Up @@ -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 <branch-slug>` (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 <tag>` (both). Local compile-only checks
must use the tagged DerivedData directory rather than an untagged default.
Expand Down Expand Up @@ -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.
Expand All @@ -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: <short task>" --subtitle "<branch> · <tag>" --body "Was: <prior bad behavior>. Now: <expected behavior>. <concrete check>. PR: <pr-url>"`. Later closeout notifications use `"CI green: <branch>"` or `"CI blocked: <branch>"` 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.
Expand Down Expand Up @@ -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/<locale>.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/<locale>.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}/<pkg>` 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

Expand Down
2 changes: 1 addition & 1 deletion skills/cmux-architecture/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<pkg>` (both apps), `Packages/iOS/<pkg>` (iOS only), or `Packages/macOS/<pkg>` (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 `../../<Group>/<Name>`. Never hand-edit workspace group membership. CI runs `python3 scripts/check-workspace-package-groups.py --check` and fails on drift.

Expand Down
2 changes: 1 addition & 1 deletion skills/cmux-architecture/references/package-boundaries.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion skills/cmux-cua/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/`.
4 changes: 2 additions & 2 deletions skills/cmux-debugging/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion skills/cmux-debugging/references/runtime-pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion skills/cmux-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <agent> --session <session> --name "pre-review-repair"
cmux vault checkpoints --agent <agent> --session <session>
cmux vault fork --agent <agent> --session <session> --checkpoint <id> --open
```
Expand Down
Loading