Skip to content

feat(next): workspace name source and revision for auto-naming (hq#1957) - #20710

Closed
teamleaderleo wants to merge 42 commits into
feat-cmux-nextfrom
cg-lane-adopt/workspace-name-source
Closed

teamleaderleo wants to merge 42 commits into
feat-cmux-nextfrom
cg-lane-adopt/workspace-name-source

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Workspaces now record whether their name came from a person or from auto-naming, the same way tab names already do (NameSource { User, Auto } and name_revision). The daemon is the only writer and decides whether an automatic rename may land. This is the daemon half of the workspace auto-naming plan in plans/cmux-next/workspace-autoname.md (hq#1957, the follow-up to #20397). The app-side namer comes in a separate PR.

  • RegistryWorkspace gains name_source and name_revision. They are stored through an additive SQLite migration. Existing rows read as User, so names people already set stay pinned.
  • rename-workspace / workspace.rename accept source: "auto" with expected_name_revision. An auto rename is refused if:
    • the current name was set by a person (operation.failed, reason_code: name_pinned);
    • the new name is empty (name_empty);
    • the name revision has moved (revision.conflict).
  • The commit runs the same checks again against the stored row inside its transaction. A late auto-namer callback loses, and a rename by a person always wins.
  • source: "auto" with release: true hands a pinned name back to auto.
  • The workspace-name-source-v1 capability gates the new fields. Workspace projections expose name_source and name_revision to every client.
  • New modules (name_flags.rs, cmd_workspaces.rs, workspace_rows_commit.rs, workspace_name_tests.rs) keep the already-large files from growing (check-no-godfiles.sh).

Testing

  • Each behavior commit was preceded by a red test, as listed in the commit log.
  • Hosted cmux-tui verify (--filter name) on the merged head: run 38613476384.
  • Locally: SDK schema, spec inventory and resource-API boundary checks pass; the god-file check passes against the base; rustfmt is clean.

Review

A correctness review (races, migration, protocol, tombstone order) found nothing blocking. Two Low notes:

  • A person's rename to an empty or whitespace name counts as unnamed, so an auto rename may replace it. Tab names behave the same way.
  • When the revision is stale and the name is also pinned, the caller sees revision.conflict first.

Changelog

none

🤖 Generated with Claude Code

teamleaderleo and others added 20 commits October 14, 2026 01:36
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Red tests for workspace name provenance, mirroring the tab name authority:
- resource_name: an automatic rename needs a generation and a name
  revision, never replaces a user name, never empties a name; a stale
  revision is revision.conflict; every rename bumps the revision.
- registry: name_source/name_revision persist across restart, a legacy
  row without the columns reads as user with revision 0, and an older
  daemon's rename claims user ownership.
- workspace.rename (resource API) and raw rename-workspace: an auto rename
  over an auto name succeeds, over a user name fails, a stale revision
  fails; projections carry name_source and name_revision.
- CLI: workspace rename takes the tab rename's --source,
  --expected-generation and --expected-name-revision flags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Workspaces get the name provenance tabs have, so the app's automatic namer
can follow the work without ever overwriting a name a person chose.

- RegistryWorkspace and the mux Workspace carry name_source (user|auto)
  and name_revision. Two additive workspaces columns (default user/0, no
  schema bump) and a trigger that makes an older daemon's rename a user
  rename (workspace_name_store.rs).
- resource_name::NameUpdate is now the one check for tab and workspace
  renames: auto needs expected_generation and expected_name_revision,
  never replaces a non-empty user name, never stores an empty name, and a
  moved revision is revision.conflict. Every workspace rename advances the
  revision; a rename without source stays a user rename.
- rename-workspace gains source and expected_name_revision; workspace.rename
  gains source, expected_generation, expected_name_revision. Both paths
  share workspace_rename_plan / rename_workspace_with_name_authority.
- A workspace the daemon names itself starts auto; a requested name, and
  every legacy row, is user.
- Projections: raw tree, list-workspaces and workspace deltas carry
  name_source and name_revision (string); resource snapshots and journal
  upserts carry them in extra. Capability workspace-name-source-v1.
- CLI: cmux workspace <ws> rename takes the tab rename's --source,
  --expected-generation and --expected-name-revision flags.
- Spec: sdk-schema (NameSource, Workspace fields, rename-workspace fields),
  resource-operations-v2.json, commands.md; bindings, cmux-app.d.ts and
  resource descriptors regenerated. Coordination ledger entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An automatic rename over a user name or to an empty name now has to
return operation.failed with reason_code name_pinned or name_empty, for
tabs and workspaces alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An automatic rename over a user name or to an empty name returned an
anyhow error, so cmux.protocol/2 callers saw no typed code. Both refusals
now return operation.failed with extra.reason_code name_pinned or
name_empty. Tabs and workspaces share the check, so both get them. The
message text is unchanged, so v1 error strings stay the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The auto rename checks name_revision only against the in-memory
projection. A user rename stored by another writer after memory was read
must still win. The registry commit and the resource patch both have to
return revision.conflict for a write that carries an older name revision,
and name_pinned for an auto name over a stored user name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The auto rename checked name_revision only against the in-memory
projection. If another writer stored a user rename after memory was
read, the commit overwrote it. Both commit paths, the legacy registry
apply and the resource patch upsert, now compare the stored name,
source, and revision inside the transaction:

- A write that keeps the stored provenance passes.
- A rename must advance the stored revision by exactly one.
- Anything else is revision.conflict.
- An automatic rename over a stored user name is name_pinned, as it is
  in memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…me authority (hq#1957)

v1 rename-workspace passes expected_generation as the registry guard.
Folding it into the name authority changed a plain user rename's
idempotency fingerprint and its stale-generation error. Only an automatic
rename may carry it as name authority.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…thority (hq#1957)

rename-workspace passed the mutation's expected_generation into the
name authority for every rename. That gave a plain user rename a new
idempotency fingerprint and a different stale-generation error. Only
source auto now uses the generation as name authority. A user rename's
fingerprint and errors are exactly what they were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sets an automatic name through rename-workspace, restarts the registry,
and checks that storage and the restored projection both read auto at
name revision 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A person needs a way to let the automatic namer name a workspace again.
These tests cover the request shape: source auto plus release true, with
no expected revision. The release keeps the current text, bumps
name_revision and sets the source to auto. Only an auto source can be
released to, and tab names have no release.

Covered paths:
- v1 rename-workspace, which takes the current name;
- v2 workspace.rename, where name is omitted;
- the CLI's workspace rename --release-to-auto.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A person can now hand a pinned name back to the automatic namer.
rename-workspace and workspace.rename accept release: true with source
auto. The current text stays, name_source becomes auto and name_revision
advances. Because this is a person's request, it needs no expected
revision. Only an auto source can be released to, and tab names have no
release.

- v1: name is still required and must equal the current name, so the
  request schema keeps its shape and only gains release.
- v2: name may be omitted on a release. The resource API boundary check
  now allows that only when the operation declares release and the
  'name is required unless release is true.' constraint.
- CLI: cmux workspace [<selector>] rename --release-to-auto, which takes
  no --name.

Spec, SDK schema, commands.md, bindings, app-host types and help text
are updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rows (hq#1957)

The plan now records:

- the CLI shape that was built (rename --source auto and
  --release-to-auto);
- the app must gate auto renames on workspace-name-source-v1 or use v2,
  because an older daemon applies v1 source auto as a user rename;
- workspaces that existed before the migration read as user and never
  auto-name until released.

The coordination ledger entry also lists the follow-up fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ard (hq#1957)

commit_resource_patch compares expected_revision with the resource
revision, not the workspace revision. Passing the workspace revision would
have failed the patch for the wrong reason. The test now passes no
revision precondition, so only the stored name row fences the write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(hq#1957)

A rename's patch advances name_revision, as every daemon rename path
does; the stored-row fence refuses one that does not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (hq#1957)

Move the rename name flags to cli/command/name_flags.rs and their tests
to command/tests/name_source_tests.rs; flatten WorkspaceRenameRequest
into rename-workspace and keep its capability in cmd_workspaces; move the
registry row commit to workspace_registry/workspace_rows_commit.rs; and
declare the topology name tests under workspace_mutations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…q#1957)

Tests build rename-workspace through WorkspaceRenameRequest; drop the
qualified workspace_name_store paths, gate the test-only NameUpdate::new
and take clippy's is_none_or.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pt/workspace-name-source

# Conflicts:
#	cmux-tui/bindings/cpp/.cmux-resource-api.json
#	cmux-tui/bindings/cpp/.cmux-sdk-manifest.json
#	cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp
#	cmux-tui/bindings/cpp/src/raw/generated/protocol.cpp
#	cmux-tui/bindings/go/.cmux-resource-api.json
#	cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/go/raw/generated_commands.go
#	cmux-tui/bindings/go/raw/generated_events.go
#	cmux-tui/bindings/go/raw/generated_metadata.go
#	cmux-tui/bindings/go/raw/generated_presence_test.go
#	cmux-tui/bindings/go/raw/generated_types.go
#	cmux-tui/bindings/java/.cmux-resource-api.json
#	cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java
#	cmux-tui/bindings/python/.cmux-resource-api.json
#	cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/python/cmux/raw/_generated/_schema.py
#	cmux-tui/bindings/python/cmux/raw/_generated/metadata.py
#	cmux-tui/bindings/rust/.cmux-resource-api.json
#	cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/rust/src/generated/commands.rs
#	cmux-tui/bindings/rust/src/generated/events.rs
#	cmux-tui/bindings/rust/src/generated/metadata.rs
#	cmux-tui/bindings/rust/src/generated/mod.rs
#	cmux-tui/bindings/rust/src/generated/types.rs
#	cmux-tui/bindings/typescript/.cmux-resource-api.json
#	cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/typescript/src/raw/generated/commands.ts
#	cmux-tui/bindings/typescript/src/raw/generated/events.ts
#	cmux-tui/bindings/typescript/src/raw/generated/index.ts
#	cmux-tui/bindings/typescript/src/raw/generated/metadata.ts
#	cmux-tui/bindings/typescript/src/raw/generated/types.ts
#	cmux-tui/bindings/zig/.cmux-resource-api.json
#	cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/zig/src/raw/generated/protocol.zig
#	cmux-tui/crates/cmux-tui-core/src/workspace_registry.rs
#	cmux-tui/spec/commands.md
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 14, 2026 18:15 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 4fbeb0f0-823d-4c6d-8bd5-01bed8b744d4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-5k1pm created for this PR by the cmux coordinator.

…pt/workspace-name-source

# Conflicts:
#	cmux-tui/spec/commands.md
…pt/workspace-name-source

# Conflicts:
#	cmux-tui/bindings/cpp/.cmux-resource-api.json
#	cmux-tui/bindings/cpp/.cmux-sdk-manifest.json
#	cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp
#	cmux-tui/bindings/cpp/src/raw/generated/protocol.cpp
#	cmux-tui/bindings/go/.cmux-resource-api.json
#	cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/go/raw/generated_commands.go
#	cmux-tui/bindings/go/raw/generated_events.go
#	cmux-tui/bindings/go/raw/generated_metadata.go
#	cmux-tui/bindings/go/raw/generated_presence_test.go
#	cmux-tui/bindings/go/raw/generated_types.go
#	cmux-tui/bindings/java/.cmux-resource-api.json
#	cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java
#	cmux-tui/bindings/python/.cmux-resource-api.json
#	cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/python/cmux/raw/_generated/_schema.py
#	cmux-tui/bindings/python/cmux/raw/_generated/metadata.py
#	cmux-tui/bindings/rust/.cmux-resource-api.json
#	cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/rust/src/generated/commands.rs
#	cmux-tui/bindings/rust/src/generated/events.rs
#	cmux-tui/bindings/rust/src/generated/metadata.rs
#	cmux-tui/bindings/rust/src/generated/mod.rs
#	cmux-tui/bindings/rust/src/generated/types.rs
#	cmux-tui/bindings/typescript/.cmux-resource-api.json
#	cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/typescript/src/raw/generated/commands.ts
#	cmux-tui/bindings/typescript/src/raw/generated/events.ts
#	cmux-tui/bindings/typescript/src/raw/generated/index.ts
#	cmux-tui/bindings/typescript/src/raw/generated/metadata.ts
#	cmux-tui/bindings/typescript/src/raw/generated/types.ts
#	cmux-tui/bindings/zig/.cmux-resource-api.json
#	cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/zig/src/raw/generated/protocol.zig
#	cmux-tui/spec/commands.md
…pt/workspace-name-source

# 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
…pt/workspace-name-source

# 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
#	cmux-tui/spec/commands.md
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Coordinating Leo's local/remote auto-naming follow-up with #20397. This lane will reuse your Rust source/revision protocol and publish a separate Swift client compatibility PR (snapshot/model/event fields, source-aware rename request, capability gating, legacy-safe defaults). No duplicate Rust implementation is planned. Remote automatic writes will require the capability and expected_name_revision; manual rename remains authoritative. I will attach the exact client PR/head and fleet receipts here.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Follow-up from the unified app lane: #21445 now consumes the daemon provenance contract and submits automatic workspace renames with source:auto, expected_name_revision, and the current generation. Exact app head 205fd094d5e348f94a7bba394ba023b952191c3b; focused managed-fleet naming suites pass 23/23 + 4/4. Please keep #20710's manual-name-wins and stale-revision rejection semantics stable; #21441 is the Swift client wire parity and #21445 is the app routing layer.

lawrencecchen added a commit that referenced this pull request Oct 18, 2026
#21445 added workspaceNameSource to DaemonCapabilities.optional, and its
cherry-pick of b4c2c48 also put it in unservedByBundledDaemon. The
capability was in two lists. check-daemon-capabilities.sh requires every
optional entry from the bundled daemon, so every dev build failed with
"the bundled cmux-tui does not serve workspace-name-source-v1".

- The capability is now only in unservedByBundledDaemon (by its constant),
  so isServedByBundledDaemon is false and the auto namer and the
  rename-with-source path fall back (they check DaemonIdentity.supports).
- check-daemon-capabilities.sh fails with a clear message when a
  capability is in required/optional and in the unserved list.
- The unserved ceiling names workspace-name-source-v1 until its daemon
  half (#20710) lands.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@teamleaderleo

teamleaderleo commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator Author

Plan: this PR lands as slices cut from current feat-cmux-next. The Swift half is already there (#21441, #21445).

  1. plan(next): workspace auto-naming that keeps up (hq#1957) #21629: the plan doc, plans/cmux-next/workspace-autoname.md (docs only).
  2. feat(next): workspace name source and revision, like tab names (hq#1957) #21648: the daemon half. That's storage, NameUpdate, the v1/v2 rename fields, projection, spec, regenerated bindings, CLI flags, and workspace-name-source-v1 moved to optional in the app's capability list. It also has two fixes for tests that landed after this PR's base: projected workspace values carry the same name extra as the journal, and identity is checked before the name fence.
  3. Next slice, not started; each item comes from the feat(next): workspace name source and revision, like tab names (hq#1957) #21648 review:
    • Scope the name fence to changed rows. commit_workspace_registry_in_transaction (workspace_registry/workspace_rows_commit.rs, the fence_stored_name loop around lines 103-114) checks every live row on every full commit. One row whose stored name drifted from memory then blocks every create and rename until a restart. Fence only the rows the commit changes.
    • Make v1 and v2 agree on expected_generation. A v1 user rename drops expected_generation from its name authority (NameUpdate::from_request). The v2 path (NameUpdate::parse in resource_name.rs) keeps it for a user rename, so a v2 user rename can fail on a retired generation and fingerprints differently. Apply the same rule in both.
    • Add regression tests for the two cd97e404a19 fixes. One for the dedup: a commit that changes nothing journals no workspace upsert once the name extra is projected. One for the identity check: a create at a key bound to another live workspace fails with is already bound to public id, not revision.conflict. Today only the older feat-cmux-next tests cover them indirectly (batch_close, resource_effects, workspace_materialize).

Recommendation: close this PR once #21629 and #21648 land. The follow-ups in step 3 don't need this branch.

teamleaderleo added a commit that referenced this pull request Oct 18, 2026
… the app half landed (hq#1957)

The CLI flags, RegistryWorkspace provenance and name_pinned refusals exist
only in #20710; the app half (WorkspaceAutoNamer, the
workspaces.autoName.variant tunable, the workspaces.autoName setting) landed
first in #21441 and #21445.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
* plan(next): workspace auto-naming that keeps up (hq#1957)

The design for automatic workspace names: the daemon is the single writer
of a workspace's name source and revision, the app's namer sends automatic
renames fenced by that revision, and a person's rename always wins.

Carved from #20710.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* plan(next): mark the workspace name protocol as proposed, record what the app half landed (hq#1957)

The CLI flags, RegistryWorkspace provenance and name_pinned refusals exist
only in #20710; the app half (WorkspaceAutoNamer, the
workspaces.autoName.variant tunable, the workspaces.autoName setting) landed
first in #21441 and #21445.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Update: the daemon half is now one draft slice on current feat-cmux-next, #21648 (storage, NameUpdate, v1/v2 rename fields, projection, spec, regenerated bindings, CLI flags, and workspace-name-source-v1 moved to optional in the app's capability list). The plan doc is #21629. Once both land, this PR can close.

teamleaderleo added a commit that referenced this pull request Oct 18, 2026
…it (hq#1957)

feat-cmux-next tests that landed after #20710's base failed on this slice:

- batch_close and resource_effects: the journal decorates every stored
  workspace with extra.name_source/name_revision, but the projected
  workspace values had no extra, so the public fold never matched and every
  commit restated every workspace. Every workspace value builder (deltas,
  full and scoped projection, normalized registry deltas, resource
  snapshots) now carries the same extra.
- key_close_cannot_rebind_a_live_durable_workspace_identity: creating a
  workspace at a key bound to another live workspace hit the name fence
  (revision.conflict) before the identity check. The name fence now
  applies only to the same workspace (numeric id), and the resource store
  checks identity before the name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
…57) (#21648)

* feat(next): workspace name source and revision, like tab names (hq#1957)

Workspaces now record whether their name came from a person or from
auto-naming, the way tab names do (NameSource { User, Auto } and a
name_revision), and the daemon decides whether an automatic rename lands.

- RegistryWorkspace and the mux Workspace carry name_source and
  name_revision, stored in two additive workspaces columns (legacy rows
  read user/0). Both commit paths re-check the stored row's name, source
  and revision inside the transaction.
- rename-workspace and workspace.rename accept source, expected_generation,
  expected_name_revision and release. An automatic rename never replaces a
  person's name (operation.failed, reason_code name_pinned), never empties
  one (name_empty), and loses to any rename after the revision it read
  (revision.conflict). source auto with release hands a pinned name back.
- Workspace projections (raw tree, list-workspaces, resource extra) expose
  name_source and name_revision; the daemon advertises
  workspace-name-source-v1.
- CLI: cmux workspace rename --source auto --expected-generation G
  --expected-name-revision N, and --release-to-auto.
- Spec, regenerated SDK bindings, cmux-app.d.ts and resource descriptor
  hashes follow; optchat-chief's RenameWorkspaceRequest literal gains the
  new fields.
- The app's DaemonCapabilities moves workspace-name-source-v1 from
  unservedByBundledDaemon to optional, since the bundled daemon now serves
  it.

Carved from #20710 onto current feat-cmux-next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): project workspace name provenance like the journal states it (hq#1957)

feat-cmux-next tests that landed after #20710's base failed on this slice:

- batch_close and resource_effects: the journal decorates every stored
  workspace with extra.name_source/name_revision, but the projected
  workspace values had no extra, so the public fold never matched and every
  commit restated every workspace. Every workspace value builder (deltas,
  full and scoped projection, normalized registry deltas, resource
  snapshots) now carries the same extra.
- key_close_cannot_rebind_a_live_durable_workspace_identity: creating a
  workspace at a key bound to another live workspace hit the name fence
  (revision.conflict) before the identity check. The name fence now
  applies only to the same workspace (numeric id), and the resource store
  checks identity before the name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(next): workspace_resource_upsert lives with workspace renames (hq#1957)

Its name provenance parameter pushed mux.rs past the 1000-line god-file
budget (check-no-godfiles.sh); the helper moves to mux/workspace_rename.rs
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
* cmux NEXT: a folded long thought reads "Thought for 12s"

A long thought folded once done (#20948, Debug toggle
agentPane.debug.foldLongThoughts) now reads how long the agent thought,
from its first chunk to its latest, clamped at zero for out-of-order
events. An undated thought, or one under a second, reads "Thought".

#20948's "Thought" plus first line stays as the Debug choice
agentPane.debug.thoughtHeader = preview, which the app sets on <html> as
data-thought-header. New string tools.thoughtFor in all 21 pane locales
(split from #20963).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT perf-hitch2: an evicted terminal shows its last frame at once when shown again (GhosttyNextKit a19c884fa, cx-8w027.19.1)

A terminal the warm set evicted showed an empty pane until its new session
attached and drew. Ghostty emptied every released drawable
(kIOSurfacePurgeableEmpty in IOSurface.deinit), and a hidden surface releases
its swap chain, so even the layer's presented frame was blank (ghostty-next
PR 42: a plain release; the last owner frees the pages).

- A paused surface marks its presented frame volatile and makes it
  non-volatile before it shows again; a purged one leaves the layer empty
  until Ghostty's next frame, as before.
- Eviction keeps the presented IOSurface (no copy, volatile), at most the warm
  set terminal capacity, oldest first; a frame dropped by the bound leaves a
  480 px hover picture; memory pressure drops them with no renders.
- The new session shows it over its first surface (TerminalFrameHold
  begin(lastFrame:)) until its first content is parsed and drawn, at most 3 s;
  a purged frame or a hidden mount shows nothing. Hover cards picture it on
  demand.
- debug.surfaces: last_frames outcomes, per pane last_frame_holds, frame_held.
  Proof: scripts/cmux-next/evicted-terminal-frame-live.py.

Pins GhosttyNextKit release xcframework-a19c884fa8d132378ae838dcba95654b177b6723-apple-v6
(checksum 94ff3178... from SHA256SUMS, matching the downloaded zip) and moves the
ghostty-next gitlink, package-notices.json and vt-link-graph.json to it (only
pkg/macos/iosurface/iosurface.zig changed since e6a27a1fb).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* perf(cmux-next): a terminal mounted hidden after an eviction shows its last frame at its reveal (cx-8w027.19.1)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* perf(cmux-next): hover pictures reclaim a paused surface's volatile frame for the read; a frame in flight at the pause is not reclaimed; no last frame at capacity 0 (cx-8w027.19.1, review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* perf(cmux-next): last-frame logic lives in CmuxNextTerminal (TerminalLastFrames evict/show/report); the frame shows at the reveal; CmuxNextApp does not grow (cx-8w027.19.1)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* release-notes(next): fit browser highlights to the What's New limits (#21716)

* release-notes(next): fit browser highlights to the What's New limits

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* release-notes(next): live links, dev-build wording for remote tabs

Replaces cmux.com/whats-new (404) with real try-it actions, says the
remote browser tab entries are development-build features (their action
is DEV/Nightly only), and drops 'instant' from the tab-switch entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* release-notes(next): fit app highlights to the What's New limits (#21717)

* release-notes(next): fit app highlights to the What's New limits

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* release-notes(next): live links and exact profile-menu wording

Replaces two 404 links (cmux.com/whats-new, docs/agent-integrations) with
real actions or docs pages, and says what the profile button opens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(next): workspace name source and revision, like tab names (hq#1957) (#21648)

* feat(next): workspace name source and revision, like tab names (hq#1957)

Workspaces now record whether their name came from a person or from
auto-naming, the way tab names do (NameSource { User, Auto } and a
name_revision), and the daemon decides whether an automatic rename lands.

- RegistryWorkspace and the mux Workspace carry name_source and
  name_revision, stored in two additive workspaces columns (legacy rows
  read user/0). Both commit paths re-check the stored row's name, source
  and revision inside the transaction.
- rename-workspace and workspace.rename accept source, expected_generation,
  expected_name_revision and release. An automatic rename never replaces a
  person's name (operation.failed, reason_code name_pinned), never empties
  one (name_empty), and loses to any rename after the revision it read
  (revision.conflict). source auto with release hands a pinned name back.
- Workspace projections (raw tree, list-workspaces, resource extra) expose
  name_source and name_revision; the daemon advertises
  workspace-name-source-v1.
- CLI: cmux workspace rename --source auto --expected-generation G
  --expected-name-revision N, and --release-to-auto.
- Spec, regenerated SDK bindings, cmux-app.d.ts and resource descriptor
  hashes follow; optchat-chief's RenameWorkspaceRequest literal gains the
  new fields.
- The app's DaemonCapabilities moves workspace-name-source-v1 from
  unservedByBundledDaemon to optional, since the bundled daemon now serves
  it.

Carved from #20710 onto current feat-cmux-next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): project workspace name provenance like the journal states it (hq#1957)

feat-cmux-next tests that landed after #20710's base failed on this slice:

- batch_close and resource_effects: the journal decorates every stored
  workspace with extra.name_source/name_revision, but the projected
  workspace values had no extra, so the public fold never matched and every
  commit restated every workspace. Every workspace value builder (deltas,
  full and scoped projection, normalized registry deltas, resource
  snapshots) now carries the same extra.
- key_close_cannot_rebind_a_live_durable_workspace_identity: creating a
  workspace at a key bound to another live workspace hit the name fence
  (revision.conflict) before the identity check. The name fence now
  applies only to the same workspace (numeric id), and the resource store
  checks identity before the name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(next): workspace_resource_upsert lives with workspace renames (hq#1957)

Its name provenance parameter pushed mux.rs past the 1000-line god-file
budget (check-no-godfiles.sh); the helper moves to mux/workspace_rename.rs
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): restore #21369 and #21384, undone by the Chief Dock squash (#21690)

* test(cmux-next): older history is asked for once per page

Fails on feat-cmux-next: a streaming commit near the top asks for the same
older page again, because 50a0c60 (#21194) restored the effect #21369
had keyed on the first row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(cmux-next): avoid duplicate older-history loads (#21369)

(cherry picked from commit 8f6f9cb)

* perf(cmux-next): gate find highlight repaints by transcript changes (#21384)

* perf(cmux-next): gate find highlight repaints by transcript changes

* fix(cmux-next): preserve find repaint triggers after fold reveals

* style(acpmux): format find highlight effect

* test(next): carry apps empty-state selector through PR 21384

* test(next): carry apps empty-state selector through find highlight PR

(cherry picked from commit afec4de)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: no-ask start modes by inherit or an approved preset (#21666)

* feat(acpmux): start a no-ask mode from a session in it or an approved preset

A `session/new` that asks for a mode that doesn't ask, without the
person, now has two ways through:

- Inherit: a caller that is itself an acpmux session in that exact mode on
  the same harness family. Each harness gets ACPMUX_SESSION_TOKEN, an HMAC
  of its session id under a per-run in-memory secret, and
  ACPMUX_SESSION_TOKEN_SOCKET. The token is read only from a unix socket
  connection's first `initialize`. The caller's mode is read at request
  time.
- Approved preset: `_acpmux/presets {name, approveMode: true}` from the
  person records the sha256 of the preset's contents. The approval counts
  only while the resolve and the spawn both used those exact contents, for
  the preset's own mode, with no request overrides other than a folder.

A start mode never applies to an adopt without the person, and a proven
caller session that asks never gets the full-access default for its
chats. The refusal names where to approve (cmux NEXT on this host, or the
preset to approve there once), with {reason, host, mode, preset} in
error.data. Web and peer clients never get the host name.

person_auto_start.rs runs in the hermetic test environment, and its
person-key pipe is close-on-exec so parallel tests' daemons never hold
each other's key pipe open.

Carved from #20653.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): an approved preset with a route, and env that names the caller token

Red on 116ab01:
- an approved preset plus a request `route` passes the start gate and
  reaches the handler ("no route"), where it should need the person;
- a harness profile env that sets ACPMUX_SESSION_TOKEN reaches the agent
  in place of acpmux's caller token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(acpmux): a route counts as an override, env never sets the caller token

Review fixes for the start gate:
- A request `route` on an approved preset now needs the person, like its
  own model, effort, policy or env: a route swaps the provider base URL
  and keys.
- A profile's, preset's or request's env no longer replaces
  ACPMUX_SESSION_TOKEN or ACPMUX_SESSION_TOKEN_SOCKET. That env is
  layered after `caller_env`, so the spawn drops both keys from it.
- The CLI's daemon readiness pipe is close-on-exec from creation
  (`std::io::pipe`, pipe2(O_CLOEXEC) where the OS has it) instead of a
  `pipe` then `fcntl`, which raced with a fork on another thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: the static status look everywhere; indicators name their state (cx-sya4n) (#21664)

* test(cmux-next): the static look reaches tabs, the attention ring, the agent pane and every label (cx-sya4n)

Red: tabs keep their icon set's marks and colored dots, the attention
ring blinks, the agent pane's theme has no statusIndicators look and
indicators have no tooltip or VoiceOver label (compile failure).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: the static status look everywhere, and every indicator names its state (cx-sya4n)

Tabs draw the static look's still marks (the badge too, never a dot
told apart by color alone); the pane attention ring is the steady ring
under Static; the agent pane's theme sends statusIndicators (static
under Static or Reduce Motion). Every StatusIndicatorView's tooltip and
VoiceOver label is the state's short name (StatusIndicatorState
.shortLabel, nine strings in every catalog locale), and a tab's
needs-input VoiceOver text names the blocked kind. Carved from #20511.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-authored-by: safe-push <safe-push@cmux.local>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
* cmux-next: keep the Mac awake while agents work (sessions.keepAwake)

New Settings > Agents > Agent Sessions row, "Keep Mac Awake While Agents
Work": Off, While Plugged In (default) or Always. While a local agent runs a
turn (an acpmux session, or a tab's agent hooks or OSC 7501 working record),
idle system sleep stays off. The display can still sleep and closing the lid
still sleeps the Mac.

- schemas/settings/source/sessions.json is the row's source; the Swift
  descriptor, parser constants and choice enum are generated.
- KeepAwakeService holds one ProcessInfo activity for every reason, so the
  Keep Mac Awake action and the setting never end each other's hold. The
  action now goes through it and still holds on battery.
- PowerSourceMonitor follows IOKit's power-source notification on the main
  run loop (no polling); unplugging releases a While Plugged In hold.
- Agents may not set the key (userOnly).
- Strings in all 21 locales of the settings catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux NEXT perf-hitch2: an evicted terminal shows its last frame at once when shown again (GhosttyNextKit a19c884fa, cx-8w027.19.1)

A terminal the warm set evicted showed an empty pane until its new session
attached and drew. Ghostty emptied every released drawable
(kIOSurfacePurgeableEmpty in IOSurface.deinit), and a hidden surface releases
its swap chain, so even the layer's presented frame was blank (ghostty-next
PR 42: a plain release; the last owner frees the pages).

- A paused surface marks its presented frame volatile and makes it
  non-volatile before it shows again; a purged one leaves the layer empty
  until Ghostty's next frame, as before.
- Eviction keeps the presented IOSurface (no copy, volatile), at most the warm
  set terminal capacity, oldest first; a frame dropped by the bound leaves a
  480 px hover picture; memory pressure drops them with no renders.
- The new session shows it over its first surface (TerminalFrameHold
  begin(lastFrame:)) until its first content is parsed and drawn, at most 3 s;
  a purged frame or a hidden mount shows nothing. Hover cards picture it on
  demand.
- debug.surfaces: last_frames outcomes, per pane last_frame_holds, frame_held.
  Proof: scripts/cmux-next/evicted-terminal-frame-live.py.

Pins GhosttyNextKit release xcframework-a19c884fa8d132378ae838dcba95654b177b6723-apple-v6
(checksum 94ff3178... from SHA256SUMS, matching the downloaded zip) and moves the
ghostty-next gitlink, package-notices.json and vt-link-graph.json to it (only
pkg/macos/iosurface/iosurface.zig changed since e6a27a1fb).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-next: regenerate settings exports and defaults for sessions.keepAwake

Exports from the fleet export step at 4beacc0 (run-swift-testing-suites.sh,
export-settings); defaults snapshot from defaults_guard.py snapshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(cmux-next): a terminal mounted hidden after an eviction shows its last frame at its reveal (cx-8w027.19.1)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* perf(cmux-next): hover pictures reclaim a paused surface's volatile frame for the read; a frame in flight at the pause is not reclaimed; no last frame at capacity 0 (cx-8w027.19.1, review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-next: regenerate settings exports after merging feat-cmux-next

Exports from the fleet export step at 011a67c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* restore-trace.py: trace a relaunch restore per pane with the snapshot sources that moved (cx-8w027.23)

* restore-trace.py: every pane in short, 50 ms reads (cx-8w027.23)

* perf(cmux-next): last-frame logic lives in CmuxNextTerminal (TerminalLastFrames evict/show/report); the frame shows at the reveal; CmuxNextApp does not grow (cx-8w027.19.1)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux NEXT perf-restore: no pane re-run on connect, room badge from the strip's window, one topology per turn for every pane that changed (cx-8w027.23)

- PaneController.Snapshot.connected is gone: apply never read it, and the
  read re-ran every pane's snapshot when the daemon connected (16 per
  16-pane restore). A reconnect with a new daemon still re-maps through
  generation and surfaces.
- BrowserProfileService.roomDefault takes the room of the window the
  strip or sidebar avatar draws in (WindowState.profileID, observed), so a
  room's profile badge follows that window; the lookup of WindowManager
  state (not observed) stays only for one-time choices (a new tab's profile).
- A pane whose part of the focus topology changed marks the workspace
  (setNeedsTopology); FocusCoordinator builds and sends one topology per
  owner for the whole main-actor turn, at its end or before any state read
  or event (I4), instead of one per pane.

* cmux NEXT: no focus ring on the daemon recovery status label (#21679)

#20765 made the not-connected screen's status label selectable without
focusRingType = .none, so check:text-focus fails every feat-cmux-next PR.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* FocusCoordinator: state is a pure read; key routing and events reduce the pending topology; the turn-end reduction runs detached; what the settled observer queues runs (review of cx-8w027.23 slice 1)

* release-notes(next): fit browser highlights to the What's New limits (#21716)

* release-notes(next): fit browser highlights to the What's New limits

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* release-notes(next): live links, dev-build wording for remote tabs

Replaces cmux.com/whats-new (404) with real try-it actions, says the
remote browser tab entries are development-build features (their action
is DEV/Nightly only), and drops 'instant' from the tab-switch entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* release-notes(next): fit app highlights to the What's New limits (#21717)

* release-notes(next): fit app highlights to the What's New limits

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* release-notes(next): live links and exact profile-menu wording

Replaces two 404 links (cmux.com/whats-new, docs/agent-integrations) with
real actions or docs pages, and says what the profile button opens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(next): workspace name source and revision, like tab names (hq#1957) (#21648)

* feat(next): workspace name source and revision, like tab names (hq#1957)

Workspaces now record whether their name came from a person or from
auto-naming, the way tab names do (NameSource { User, Auto } and a
name_revision), and the daemon decides whether an automatic rename lands.

- RegistryWorkspace and the mux Workspace carry name_source and
  name_revision, stored in two additive workspaces columns (legacy rows
  read user/0). Both commit paths re-check the stored row's name, source
  and revision inside the transaction.
- rename-workspace and workspace.rename accept source, expected_generation,
  expected_name_revision and release. An automatic rename never replaces a
  person's name (operation.failed, reason_code name_pinned), never empties
  one (name_empty), and loses to any rename after the revision it read
  (revision.conflict). source auto with release hands a pinned name back.
- Workspace projections (raw tree, list-workspaces, resource extra) expose
  name_source and name_revision; the daemon advertises
  workspace-name-source-v1.
- CLI: cmux workspace rename --source auto --expected-generation G
  --expected-name-revision N, and --release-to-auto.
- Spec, regenerated SDK bindings, cmux-app.d.ts and resource descriptor
  hashes follow; optchat-chief's RenameWorkspaceRequest literal gains the
  new fields.
- The app's DaemonCapabilities moves workspace-name-source-v1 from
  unservedByBundledDaemon to optional, since the bundled daemon now serves
  it.

Carved from #20710 onto current feat-cmux-next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): project workspace name provenance like the journal states it (hq#1957)

feat-cmux-next tests that landed after #20710's base failed on this slice:

- batch_close and resource_effects: the journal decorates every stored
  workspace with extra.name_source/name_revision, but the projected
  workspace values had no extra, so the public fold never matched and every
  commit restated every workspace. Every workspace value builder (deltas,
  full and scoped projection, normalized registry deltas, resource
  snapshots) now carries the same extra.
- key_close_cannot_rebind_a_live_durable_workspace_identity: creating a
  workspace at a key bound to another live workspace hit the name fence
  (revision.conflict) before the identity check. The name fence now
  applies only to the same workspace (numeric id), and the resource store
  checks identity before the name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(next): workspace_resource_upsert lives with workspace renames (hq#1957)

Its name provenance parameter pushed mux.rs past the 1000-line god-file
budget (check-no-godfiles.sh); the helper moves to mux/workspace_rename.rs
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): restore #21369 and #21384, undone by the Chief Dock squash (#21690)

* test(cmux-next): older history is asked for once per page

Fails on feat-cmux-next: a streaming commit near the top asks for the same
older page again, because 50a0c60 (#21194) restored the effect #21369
had keyed on the first row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(cmux-next): avoid duplicate older-history loads (#21369)

(cherry picked from commit 8f6f9cb)

* perf(cmux-next): gate find highlight repaints by transcript changes (#21384)

* perf(cmux-next): gate find highlight repaints by transcript changes

* fix(cmux-next): preserve find repaint triggers after fold reveals

* style(acpmux): format find highlight effect

* test(next): carry apps empty-state selector through PR 21384

* test(next): carry apps empty-state selector through find highlight PR

(cherry picked from commit afec4de)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: no-ask start modes by inherit or an approved preset (#21666)

* feat(acpmux): start a no-ask mode from a session in it or an approved preset

A `session/new` that asks for a mode that doesn't ask, without the
person, now has two ways through:

- Inherit: a caller that is itself an acpmux session in that exact mode on
  the same harness family. Each harness gets ACPMUX_SESSION_TOKEN, an HMAC
  of its session id under a per-run in-memory secret, and
  ACPMUX_SESSION_TOKEN_SOCKET. The token is read only from a unix socket
  connection's first `initialize`. The caller's mode is read at request
  time.
- Approved preset: `_acpmux/presets {name, approveMode: true}` from the
  person records the sha256 of the preset's contents. The approval counts
  only while the resolve and the spawn both used those exact contents, for
  the preset's own mode, with no request overrides other than a folder.

A start mode never applies to an adopt without the person, and a proven
caller session that asks never gets the full-access default for its
chats. The refusal names where to approve (cmux NEXT on this host, or the
preset to approve there once), with {reason, host, mode, preset} in
error.data. Web and peer clients never get the host name.

person_auto_start.rs runs in the hermetic test environment, and its
person-key pipe is close-on-exec so parallel tests' daemons never hold
each other's key pipe open.

Carved from #20653.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): an approved preset with a route, and env that names the caller token

Red on 116ab01:
- an approved preset plus a request `route` passes the start gate and
  reaches the handler ("no route"), where it should need the person;
- a harness profile env that sets ACPMUX_SESSION_TOKEN reaches the agent
  in place of acpmux's caller token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(acpmux): a route counts as an override, env never sets the caller token

Review fixes for the start gate:
- A request `route` on an approved preset now needs the person, like its
  own model, effort, policy or env: a route swaps the provider base URL
  and keys.
- A profile's, preset's or request's env no longer replaces
  ACPMUX_SESSION_TOKEN or ACPMUX_SESSION_TOKEN_SOCKET. That env is
  layered after `caller_env`, so the spawn drops both keys from it.
- The CLI's daemon readiness pipe is close-on-exec from creation
  (`std::io::pipe`, pipe2(O_CLOEXEC) where the OS has it) instead of a
  `pipe` then `fcntl`, which raced with a fork on another thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: the static status look everywhere; indicators name their state (cx-sya4n) (#21664)

* test(cmux-next): the static look reaches tabs, the attention ring, the agent pane and every label (cx-sya4n)

Red: tabs keep their icon set's marks and colored dots, the attention
ring blinks, the agent pane's theme has no statusIndicators look and
indicators have no tooltip or VoiceOver label (compile failure).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: the static status look everywhere, and every indicator names its state (cx-sya4n)

Tabs draw the static look's still marks (the badge too, never a dot
told apart by color alone); the pane attention ring is the steady ring
under Static; the agent pane's theme sends statusIndicators (static
under Static or Reduce Motion). Every StatusIndicatorView's tooltip and
VoiceOver label is the state's short name (StatusIndicatorState
.shortLabel, nine strings in every catalog locale), and a tab's
needs-input VoiceOver text names the blocked kind. Carved from #20511.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): restore the URL chip hover card styles lost in the Chief Dock squash (#21699)

* test(cmux-next): every URL chip hover card class has a rule

Fails on feat-cmux-next: 50a0c60 (#21194) dropped the .cv-link-preview
rules #21199 added, while LinkChips.tsx still renders those classes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): restore the URL chip hover card styles (#21199)

50a0c60 (#21194, a stale-base squash) removed the nine .cv-link-preview
rules while LinkChips.tsx still renders them, so the card drew unstyled.
Restored byte for byte from 5106cc9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: a saved model or effort pick reaches open New Tab pages (#21677)

* test(acpmux): a saved model or effort pick is announced to watchers

A New Tab prewarmed before a pick draws the old start effort and the chat
then starts on the pick. Fails: note_pick saves the new start default but
sends no _acpmux/harnesses_changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(acpmux): announce a saved model or effort pick to watchers

note_pick makes the pick the harness's start in _acpmux/harnesses, but
told nobody, so a New Tab prewarmed before the pick kept drawing the old
start effort until the chat started on the pick. It now sends
_acpmux/harnesses_changed, as a saved mode pick already announces
start_mode_changed; pages re-read the catalog on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: keep awake lives in its own target (CmuxNextKeepAwake)

Review of #21730: CmuxNextApp only shrinks (check-app-size.sh), so
KeepAwakePolicy, PowerSourceMonitor and KeepAwakeService move to a new
CmuxNextKeepAwake target with their tests. The App keeps a one-line start
and the Keep Mac Awake binding; its source is 10 lines under the base.

The power-source callback now hops to the main actor with a Task instead of
MainActor.assumeIsolated (a banned crash class).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: route test knows the keep-awake tests; one policy write per change

Review of #21730: a CmuxNextSidebar change now also selects
CmuxNextKeepAwakeTests (CmuxNextKeepAwake depends on CmuxNextBridge), so
test_a_shared_module_selects_its_dependents lists it. KeepAwakeService sets
the setting and the agents-working fact in one policy write, so the hold
cannot drop between the two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-authored-by: safe-push <safe-push@cmux.local>
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Already landed as #21648 (workspace name source and revision, hq#1957). Closing.

This branch was previously deployed

2 inactive deployments
artifacts — 861813dd Deployed Oct 15, 2026 by teamleaderleo via publish tree-addressed cmux-tui #18311
cmux-tui-merge-tree — 861813dd Deployed Oct 15, 2026 by teamleaderleo via publish the merge tree #1638
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant