Skip to content

fix(nightly-next): wait up to 40m for notarization in the job - #18736

Merged
teamleaderleo merged 2 commits into
feat-cmux-nextfrom
fix/nightly-next-wait-40m
Oct 9, 2026
Merged

teamleaderleo merged 2 commits into
feat-cmux-nextfrom
fix/nightly-next-wait-40m

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Three nightly-next builds in a row outlasted the 20m in-job notarization wait from #18713: 6d443005's, plus runs 37871970302 and 37872060374, so none published in-job. Published nightly-next now waits up to 40m, the same as main and RC. CMUX_NOTARY_WAIT_TIMEOUT becomes a plain 40m on every track.

The step's 50m and the job's 80m timeouts already cover a 40m wait (tests/test_notarize_nightly_dmg.sh checks the bounds). A wait that still runs out hands off through the recovery artifact as before, and #18722 continues it on a later run.

Testing

  • Red commit aac7acb: tests/test_nightly_universal_build.sh fails with "nightly-next bounded notarization contract is missing: CMUX_NOTARY_WAIT_TIMEOUT: 40m".
  • Green: it passes. The test_notarize_nightly_dmg.sh timeout bounds pass and actionlint is clean. cmux-next-guards runs the release-notary group on this PR.

Changelog

none

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Waits up to 40m for Apple notarization in the nightly-next job, matching main and RC, after three consecutive nightly-next builds outlasted the previous 20m wait and failed to publish in-job.

  • Sets CMUX_NOTARY_WAIT_TIMEOUT to a flat 40m on every track instead of track-specific values.
  • Updates the nightly universal build test to require the 40m contract.
  • The step's 50m and the job's 80m timeouts already cover the longer wait; a wait that still times out hands off through the recovery artifact as before.

Written for commit 5ba51c8. Summary will update on new commits.

View guided diff Turn on auto-fix


Note

Low Risk
CI-only change to notarization wait duration in the nightly workflow; no app runtime or security logic is modified.

Overview
nightly-next no longer uses a shorter 20m in-job Apple notarization wait; CMUX_NOTARY_WAIT_TIMEOUT is now 40m on every track (main, RC, and nightly-next), matching the longer bound main/RC already used.

Workflow comments note that three consecutive nightly-next runs exceeded 20m, so builds were not stapled or published in-job. The existing 50m step timeout and recovery-artifact handoff on timeout are unchanged—only the wait cap increases so more builds can finish notarization in the same run.

tests/test_nightly_universal_build.sh was updated to assert the fixed 40m value instead of the old track-conditional expression.

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

teamleaderleo and others added 2 commits October 8, 2026 19:44
Three nightly-next builds in a row outlasted the 20m in-job wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runs 37871970302 and 37872060374 (and 6d443005's) outlasted the 20m wait,
so nothing published in-job. Wait 40m on every track, as main already does;
the step's 50m and job's 80m timeouts already cover it.

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

coderabbitai Bot commented Oct 9, 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: 13f5c741-e24e-4bdf-9ac5-b145cc8146d9

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
  • Autopilot · 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
teamleaderleo merged commit cd3ee29 into feat-cmux-next Oct 9, 2026
48 checks passed
@teamleaderleo
teamleaderleo deleted the fix/nightly-next-wait-40m branch October 9, 2026 02:51
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/37876991913 at 3cc0805 failed: cmux-next generated files.
Those jobs last passed at ef8692e. This pull request is one of 1 merged since: #18736.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/37877263866 at 9ca25d8 failed: cmux-next generated files.
Those jobs last passed at ef8692e. This pull request is one of 1 merged since: #18736.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/37878426726 at 818b7f9 failed: cmux-next generated files.
Those jobs last passed at ef8692e. This pull request is one of 2 merged since: #18736, #18727.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/37879215803 at 2dca7c1 failed: cmux-next checks (god files, concurrency, crash safety, l10n), cmux-next generated files.
Those jobs last passed at ef8692e. This pull request is one of 2 merged since: #18736, #18727.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/37880226198 at 82f52c7 failed: cmux-next checks (god files, concurrency, crash safety, l10n), cmux-next generated files.
Those jobs last passed at ef8692e. This pull request is one of 3 merged since: #18736, #18727, #18485.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

teamleaderleo added a commit that referenced this pull request Oct 9, 2026
…der… (#18715)

* fix(backend): retire again every retired team VM paused before the fence (cx-009a)

Review finding: a row that reached paused before retireVm existed never
had its run budget spent. Retired rows now carry fenced, set by
team_vm.retired_paused; the alarm retires every row that is pausing or
paused without it (PATCH budget, then pause or already paused). The
events use new idempotency keys (retired-fenced, retired-fence-failed)
because a pre-fence row already spent retired-paused:<vm>, and a
replayed key would not reduce again. The backfill test now models the
pre-fence row exactly (old reducer state, no provider budget, no
spent new key).

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

* crash (assume_isolated): the off-main post in VideoMainDeliveryTests goes through a GCD queue (Swift 6: no Thread.isMainThread in async code)

* cmux-next sidebar: red test, a pin dropped on Home goes after the built-in rows (cx-odqn)

* cmux-next sidebar: a pin dropped in the top rows goes after the built-in rows (cx-odqn)

* test(acpmux): a fork holds its turns' ends (red, cx-f4sk)

Dogfood of Fork from Here on dog77fk-v1: the forked chat showed its last reply
streaming with a caret and the Stop button, because hub fork copied
user_message and agent chunks but not turn_started/turn_result.

* test(optchat, acpmux): turns and compactor load no user settings, MCP servers or skills (red)

The reference runs its Claude Code turns with no user setting source and
no slash commands. Failing tests:
- acpmux accepts the preset args --setting-sources project and --disable-slash-commands; it refuses
  user/local sources, an empty list (it would drop the project denies)
  and = forms.
- An isolated Claude turn preset and every Claude compactor slot preset
  pass --setting-sources project --disable-slash-commands; a subagent
  preset passes none (subagents keep the user's environment).
- '[claude-code:unrecognized_model]' (Claude Code 2.1.287 on
  claude-haiku-5-5) counts as an unavailable model.
host::subagent_preset is the subagent preset closure moved out, unchanged.

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

* test(cmux-next): status icon sets and OSC 7501 blocked kind (red)

Behavior tests for cx-kxa2: the default icon set keeps today's plans,
eight or more candidate sets draw every state and mark permission,
question and auth apart, the OSC 7501 kind reaches the row, tab and
plan, the Debug menu writes the tunable, and a contact sheet of every
set x state x size x theme.

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

* fix: the folder picker's separator is an hr

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

* feat(cmux-next): status icon candidate sets with a live Debug switcher

cx-kxa2. StatusIconSet (Debug Settings status.iconSet, Debug menu >
Status Icons) restyles working, blocked, done and error marks in every
indicator live through the shared StatusIndicatorConfig: sidebar rows,
group headers, tab icon slots, tab badges and the OSC 7501 notification
badge. Ten sets; the default current set keeps today's plans exactly.
Marks are vector alpha masks (StatusMarkArt) tinted with theme roles;
SF Symbol sets draw as templates. The OSC 7501 blocked kind (permission,
question, auth) rides StatusIndicatorState.waiting(kind:) from
ProgramStatusRecord through StatusMapping to the plan and the tab item.
StatusIconSet.image draws a still mark from the same geometry; the
notification badge uses it with the reason's kind.

The candidate contact sheet (StatusIconSetSheetTests) renders every set
x state x size x theme with CARenderer; both review sheets now host their
sidebar strip in an offscreen window so the rows draw.

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

* fix(optchat, acpmux): turns and compactor run with no user settings, MCP servers or skills

As the reference's Claude Code path, Chief turns and compactor nodes now
load none of the user's settings, MCP servers, plugins, skills or slash
commands, and still sign in with the user's login (credentials are not a
setting source). Subagents keep the user's environment.

- acpmux: the preset args allowlist takes --setting-sources project and
  --disable-slash-commands (both take capabilities away). An empty source
  list stays refused: it would drop the project's denied tools.
- optchat-chief: an isolated Claude turn preset passes both (the session
  directory's settings and .mcp.json still load: the Chief's MCP server,
  denied tools, hooks off); every Claude compactor slot preset adds them
  to its args. Checked on Claude Code 2.1.287 with a scratch HOME: a
  user-scope MCP server and a user skill leave the session's init, the
  project server and denied tools stay.
- An older acpmux that refuses the new words gets the preset with the
  args it knows (--tools "" and the rest), logged, not with no args.
- '[claude-code:unrecognized_model]' counts as an unavailable compactor
  model (Claude Code 2.1.287 does not list claude-haiku-5-5), so the
  compactor falls back to the turn model.

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

* feat(cmux-next): workspace groups look and work like Chrome tab groups (cx-rcby)

Lawrence 2026-10-08: "new group is very ugly, the creation animation is
very bad too. make groups like this", then "pixel match" the Chrome tab
group sidebar "with our smaller height".

- Header: one full-width rounded bar filled with the group's color (the
  active theme's ANSI palette, lightened; light gray for none), the name in
  dark regular type, a more (⋮) button on hover and the collapse chevron at
  the right edge. Radius, padding, type, glyphs, the member indent and the
  member bar scale with the sidebar row height (the density setting).
  No member counts. Members carry a thin bar in the group's color.
- One editor bubble (name field focused and selected, color dots for none
  and the palette, New Workspace in Group, Move Group to New Window, Close
  Group, Ungroup, Delete Group, More Group Actions) opens from the name,
  the more button, a right-click, Return on a focused header, Rename, and
  every group a person makes. Rows run through the action registry.
- Creation moves once: the new group is a pending edit until the store
  holds it (cx-odqn rows), the header view carries on under the daemon's id,
  the bar fades and scales in once (Reduce Motion: fade only). Name and
  color edits made while the daemon creates the group wait for its id.
- A click on the open group's name or more button toggles the editor
  closed; a click in the list closes it; it follows only a group that just
  appeared and closes when its group goes or scrolls away.
- Strings in en and ja (and the other catalog languages, needs review).

* fix(acpmux): a fork copies its turns' starts and ends (cx-f4sk)

hub fork copied prompts, replies and tools but not turn_started/turn_result,
so a pane attached to the fork saw its last reply still streaming and the chat
working (caret + Stop) until a new turn ended. Both kinds are now copied, so
the fork's turns end and its latest turn can be forked again.

* fix(cmux-next): restore baseline checks and app FFI pin (#18563)

* fix(cmux-next): restore baseline routing checks

* style(webviews): format Chief inspector fixture

* ci(cmux-next): repin app FFI to published source

The f1a32f1499bc0d6fcaf8175732f2123d4e14f225 build passed the app FFI release checks. Pin its immutable release and checksum so the target push verifies the current sources.

* fix(gallery): wait for page readiness instead of network idle

(cherry picked from commit 88096251c91499b5b2ef72e08b2dcc686947e169)

* fix(gallery): recover from renderer target crashes

(cherry picked from commit cf9444f021a720a4aa29626c15091252fcd9d75e)

* cmux-agent-pane-policy: answers on _acpmux/permission_respond, only for a pending question and bounded

Fixes the base-red AgentPanePolicyParityTests on feat-cmux-next (fleet step
c670e2482c527e22bc5ee65a on 51731962c82c):

  Test thePolicyFileListsAreThisHostsLists() recorded an issue at
  AgentPanePolicyParityTests.swift:79:13: Expectation failed:
  (Set(known[method]?["params"] ?? []) → ["permissionId", "optionId", "sessionId"]) ==
  (entry.params → ["optionId", "sessionId", "answers", "permissionId"])
  ↳ _acpmux/permission_respond params

Cause: d2d1fe161dfa ("fix(cmux-next): the agent pane bridge passes question
answers", ask-gallery lane) added "answers" to the Swift known params of
_acpmux/permission_respond; policy.json did not have it.

The coordinator's security decision (2026-10-07): mirror "answers", plus two
crate limits the Swift host does not have yet (its half goes to the
ask-gallery lane):
- answers only when the pending permission is a question
  (request.toolCall._meta.acpmux.question an object, the acpmux hub's mark,
  plans/cmux-next/agent-questions.md section 3); a tool permission, or one the
  host never saw, is refused (transport.intent_invalid). PermissionOptions
  records it from the frames it already observes (is_question); a permission
  seen once without the mark is not a question.
- answers an object of 1 to 64 item ids, each id and each value at most 4096
  UTF-8 bytes, each value a string or a list of strings (a list counted
  together). policy.json question_answers holds the numbers.
The frame limit, the gesture rule (an allow still needs a fresh gesture) and
the refusal of other extra keys (updatedInput) do not change.

Cases: check.json 13 new (41 now; the Swift and Rust count asserts follow),
the refused ones with swift_gap / swift_expect, so the Swift suite stays
green and fails, naming the gap, once Swift adds the rule; params.json
(answers known, updatedInput still breaks); gestures.json (an answer with an
allow needs a gesture); parity.rs question_permissions. Red check: with the
rule off, full_check_order fails.

* fix(cmux-next): keep new column before new row

* test(cmux-next): close parity fixture gaps

* style(gallery): format chief inspector fixture

* style(gallery): match vp formatter output

* fix(cmux-next): enforce aggregate answer bounds

* test(cmux-next): match redacted compatibility detail

* fix(cmux-next): admit macOS helper inputs and annotate reap

* test(cmux-next): align answer parity bounds

* docs(policy): refresh answer parity description

* test(ci): track current cmux-tui trigger inputs

* test(cmux-next): seed parity question items

* ci(cmux-next): route resource binaries through workflow

* test(cmux-next): model combined question permission state

---------

Co-authored-by: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com>

* test: Choose folder… survives WebKit's press blurring the folder field

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

* fix: Choose folder… keeps the folder field's focus on press

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

* crash: fonts on render threads are held per size and nil-checked; ratchet render_font (red, cx-qpqs class)

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

* crash: fonts on render threads are held per size and nil-checked; ratchet class render_font (cx-qpqs class)

cx-qpqs: NSFont factories annotated nonnull return nil when threads make and
drop the last instance at once. The audit found the same make-per-draw
pattern on the render threads beyond the code font:
- RowDrawing (RowBitmaps, 3 concurrent renders): 17 fonts made per draw
  (captions, receipts, link cards, names, reaction glyph, tapbacks) now come
  from HomeFonts.system / monospacedDigit.
- MarkdownLayout (measure, tail, tile queues): heading and table header fonts
  from HomeFonts.system; code fonts from HomeFonts.monospaced.
- Layout.attributed and MarkdownLayout.attributed: bold/italic faces from
  HomeFonts.font(_:adding:), held per base, size and traits; no font
  attribute when the face does not exist (before: NSFont(descriptor:size:)
  returned nil, stored as NSNull).
- SidebarDrawing (concurrentPerform): monogram fonts held per half point; the
  six CTFontCreateUIFontForLanguage(...)! force unwraps fall back to Helvetica.
HomeFonts holds every font under one lock for the process and reads the
factory's result as an optional (the annotation is not trusted), falling
back through NSFont(descriptor:size:). Patches regenerated; sync --check ok.
Ratchet: new class render_font (fonts made in place outside a static let in
MessagesLabHome, MessagesLabSidebar, CmuxHomeRender): 31, mostly main-thread
UI and the caches; it goes to BAN once the UI sites use HomeFonts.
force_unwrap 210 -> 203. crash-elimination.md: rule for class b.
Not changed: CmuxHomeRender (hq-6d's lane; TextDraw already falls back).

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

* next: compact model picker, starred models on top, one harness on the chip (#18719)

* test: model picker stars on top, one-line rows, one harness on the chip

Leo's dogfood (2026-10-08, A1): the favourites star filtered every
harness to "No matching models", rows were two lines, and the chip drew
the Codex mark beside "Claude Code". Red: 4 fail, 6 pass.

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

* fix: compact model picker with starred models on top and one harness on the chip

- The favourites-only star toggle is gone: starred models sit in a
  Starred section above the others, and nothing hides a harness's models.
- Rows are one line (no harness subtitle) and the menu is 400px wide,
  the harness column 136px and top-aligned.
- The chip draws one harness: the one a switch to another harness is
  starting, else the running one; browsing a harness in the open menu
  no longer renames the chip.
- New string picker.starred in all 21 locales (non-English needs_review).
- Gallery: composer model-menu-starred and model-switching.

The red test expected data-agent "codex" for the Codex mark; the mark
reports its brand, "openai", so the fix commit corrects that line.
Green: modelPicker 10/10, composerPickers 33/33.

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

---------

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

* crash ratchet: an enum case or member named unowned is not an unowned reference (H1 false positives)

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

* crash: ban MainActor.assumeIsolated without a main-proof (123 -> 0; H2, hq-6d for CmuxHomeRender)

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

* test(optchat): sessions without the user setting source keep the user's settings env (red)

Review finding on 4aa642cd045d: --setting-sources project also drops the
env block of ~/.claude/settings.json, where a Mac may keep its Claude API
route (ANTHROPIC_BASE_URL and the rest for the subrouter). Adds
session_dir::user_settings_env and the SessionSetup/CompactorSpec
user_env fields (read, not yet applied) and failing tests: the turn's
project settings and a compactor slot's settings carry that env, with the
session's own env winning.

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

* fix(optchat): sessions without the user setting source keep the user's settings env

A turn or compactor slot that loads no user setting source lost the env
block of ~/.claude/settings.json, where a Mac may keep its Claude API
route. The host reads that block at start; the session directory's and
each compactor slot's project settings carry it, under their own env
(PATH and the cmux variables of the session, FORCE_PROMPT_CACHING_5M of
a 5-minute slot). Only env comes over: no hooks, permissions, MCP
servers or plugins.

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

* style(optchat): rustfmt

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

* fix(cmux-next): group editor landing-review fixes (pending edits while the daemon creates a group, action rows target the daemon id, no focus steal on close, narrow-width clamp) (cx-rcby)

* test(optchat, acpmux): a turn offers an allowlist of built-in tools (red)

Turns deny a list of Claude Code built-ins, so a built-in a later
Claude Code adds is offered until someone denies it. Failing tests:
acpmux accepts --tools with a comma list of built-in names (and still
refuses rules, MCP tool names, spaces, empty names and wildcards); an
isolated Claude turn preset passes --tools
Bash,Read,Edit,Write,WebFetch,WebSearch,ToolSearch.

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

* plans(cmux-next): team-vm lane note for the retired VM fence (cx-009a)

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

* Home text layout: property tests, random Markdown, runs that do not fit, stale lines (red, crash program phase 3)

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

* Home text layout: a run or line that does not fit its text is skipped, never raised (crash program phase 3)

TextLayout.attributed checked only NSMaxRange(range) <= length: a negative
start passed and addAttribute raised NSRangeException, and Int.max made
NSMaxRange overflow (a Swift trap). link(at:) read lines measured for another
text past its end. One overflow-free check, TextLayout.range(_:_:in:), now
guards both. Found by TextLayoutFuzzTests. Patches regenerated.

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

* optchat-chief: red test, the last turn's cache mark is lost across a host restart

* fix(optchat, acpmux): a turn offers only an allowlist of built-in tools

- acpmux: a preset may pass --tools with a comma list of built-in names
  (letters and digits; no rules, MCP tool names or wildcards), which only
  narrows what the session offers.
- optchat-chief: an isolated Claude turn preset passes --tools
  Bash,Read,Edit,Write,WebFetch,WebSearch,ToolSearch (host::TURN_TOOLS).
  Checked on Claude Code 2.1.287: the session's init lists exactly those
  and the Chief's MCP tools, so a built-in a later Claude Code adds is
  not offered.
- TURN_DENIED_TOOLS stays as the second guard: on an acpmux older than
  these preset args the preset is installed without them (now also
  without the --tools list; --tools "" of the compactor stays).

Kept --setting-sources project, not an empty list plus an explicit
--mcp-config: project keeps the session directory's .mcp.json and
settings, while a preset --mcp-config would let a preset add MCP servers
(commands), which acpmux's allowlist does not allow.

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

* test(CmuxNextCloud): the Mac app registers a mac install at sign-in and revokes it at sign-out (red, cx-wb5.64)

CmuxNextCloud depends on the shared CmuxInstallAuthCore package.

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

* feat(cmux-next): the Mac app's Cloud install principal: registered at sign-in, revoked at sign-out (cx-wb5.64)

MacInstallIdentity (CmuxNextCloud) wraps the shared InstallAuthClient with
the .mac profile: a signed build keeps its install key in the Secure Enclave
(blob in the Keychain); a development build keeps a software key and the
records in 0600 files (no Keychain prompt per rebuild). CloudService registers
(or reuses) the install and mints one token when the user signs in, and
revokes it (install.revoke, key rotated, record forgotten) at sign-out while
the Stack session still exists. InstallHTTPTransport refuses redirects.

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

* test(CmuxNextCloud): Mac install review findings: API Worker origin, sign-out during register, per-user keys, store choice (red, cx-wb5.64)

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

* fix(cmux-next): Mac install review findings (cx-wb5.64)

- Install calls go to the API Worker (CloudConfiguration.ownerAPIBaseURL,
  now shared with FeedService), never the web origin; the debug override
  stays debug-only.
- One key per Stack user; a sign-out always rotates the key and forgets the
  record, and a registration still in flight never writes its record
  (generation per bind); a session that ends elsewhere unbinds.
- Only a debug build without a Team ID uses files; anything else (also a
  failed Team ID read) uses the Secure Enclave.
- Key rotation reports Keychain and file errors; owner-only files are written
  through an exclusive, no-follow temporary file, synced, then renamed;
  cookies are off on the install transport.
- Registration runs after the machine refresh, not before it.

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

* test(CmuxNextCloud): sign-out revokes a stored install with nothing bound, and blocks registration until the session ends (red, cx-wb5.64)

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

* optchat-chief: the last turn's cache mark is saved with the turn's messages

The marked view prefix (size and hash) goes into the host state in the same
transaction as the turn's messages and pending turn, so the first turn
after a restart still keeps its mark within the API's lookback of the last
one. The turn's engine, preset and mark are now decided before the
messages are logged; the engine note still follows them.

* fix(cmux-next): Mac install re-review findings (cx-wb5.64)

Sign-out takes the Stack user and session and revokes the stored install even
when this launch never bound it, then refuses registration until the session
ends; unbind resets the client so a mint in flight yields nothing; no
registration while Cloud is turned off, the managed team is missing or the
backend is local-only, and one after the organization lifts the block.

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

* fix(cmux-next): the API Worker URL literal is a reviewed crash-allow; crash baseline lowered (cx-wb5.64)

* fix(cmux-next): make trust refusals one clear flow (#18731)

* test(cmux-next): keep trust refusals out of switch failures

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

* fix(cmux-next): route trust refusals through the trust prompt

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

---------

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

* CMUXMobileCore: protocol fuzz tests, input frames, sync frames, render grid, attach tickets (crash program phase 3)

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

* test(optchat, acpmux): --tools default is refused; an older acpmux drops only the tools list (red)

Review findings on 92a3cfe729e3: the built-in list check accepts
'default', which Claude Code reads as every built-in (a preset could
widen a profile's narrower --tools). And an acpmux that knows the
setting source but not a --tools list loses all three isolation words.
Failing tests: 'default' is refused in any case; without_tools_list
(seam, no behavior yet) drops only the list.

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

* Toast host: move toasts after an avoidance change on a main-actor task, not MainActor.assumeIsolated (crash ratchet)

* fix(nightly-next): wait up to 40m for notarization in the job (#18736)

* test(nightly-next): require the 40m notarization wait

Three nightly-next builds in a row outlasted the 20m in-job wait.

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

* fix(nightly-next): wait up to 40m for notarization in the job

Runs 37871970302 and 37872060374 (and 6d443005's) outlasted the 20m wait,
so nothing published in-job. Wait 40m on every track, as main already does;
the step's 50m and job's 80m timeouts already cover it.

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

---------

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

* test(cmux-tasks): a log write failure and a repeated positional flag must not end the process (red)

serve() must return the log write error instead of process::exit(70) in
the writer thread, and the CLI parser must refuse a repeated flag after
its positional value instead of panicking on expect("array").

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

* fix(cmux-tui): zero production panics in cmux-unix-socket, cmux-tasks, cmux-pane-protocol, cmux-remote-protocol

Each crate root now denies clippy unwrap_used, expect_used, panic,
unreachable, todo, unimplemented and exit outside cfg(test), so the
count stays at zero.

- cmux-tasks: the writer thread returned process::exit(70) on a failed
  log write. serve() now returns ServeError::LogWrite, the accept loop
  runs on its own thread and serve() waits for the first of the two to
  stop, so the process still exits at once with code 70 (from the CLI's
  ExitCode) and the supervisor restarts it from disk. The CLI parser
  refused nothing when a repeated param took the positional word and
  then its flag: it panicked on expect("array"); it now returns
  "--<flag> given twice".
- cmux-pane-protocol: SigningKey::from_seed returns anyhow::Result
  (ring rejects only a seed of the wrong length); the vector generator
  and emit-ir propagate it. The non-unix example provider returns
  ExitCode 2 instead of process::exit(2). Risk::default_for keeps its
  panic under crash-allow: pane_op! evaluates it only for the associated
  const Op::RISK, so it is a compile error, never a runtime panic.
- cmux-remote-protocol: frame decode reads the header through
  first_chunk (no try_into().unwrap()); SessionId::from_hex parses with
  str::get instead of from_utf8().expect().
- cmux-unix-socket: short_test_dir keeps its expect under crash-allow:
  the test-support feature is enabled only from dev-dependencies.

Ratchet baseline lowered (8 counts).

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

* test(crash-ratchet): Rust cfg(all(test, ...)) modules are test code (red)

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

* fix(crash-ratchet): cut Rust inline modules under cfg(all(..., test, ...))

INLINE_TESTS matched only #[cfg(test)], so test modules under
#[cfg(all(test, unix))] counted as production (all cmux-link, cmux-wg
and cmux-pty hits, most cmux-remote unwraps). The regex now also takes
cfg(all(...)) with test as one top-level predicate; any(test, ...) and
all(not(test), ...) still count. Counts only go down.

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

* fix(optchat, acpmux): refuse --tools default; an older acpmux drops only the tools list

- acpmux: the built-in list refuses 'default' (any case), Claude Code's
  word for every built-in, so a preset list can only narrow. The hub
  integration test now refuses --tools default and a rule, not a list.
- optchat-chief: on an 'args' refusal the preset is first installed
  without its --tools list (without_tools_list; --tools "" stays), and
  only then without the setting source and slash-command words.

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

* fix(cmux-tui): zero production panics in ghostty-vt, cmux-relay, cmux-pty

Each crate root now denies clippy unwrap_used, expect_used, panic,
unreachable, todo, unimplemented and exit outside cfg(test).

- ghostty-vt: key encoding falls back to the plain encoding when the key
  field has no separator (guaranteed above, was unwrap); the PNG header
  check returns false instead of try_into().unwrap(); a failed cursor
  visual read reports none instead of expect; the kitty replay base64
  buffer size is a compile-time assert and an encode error falls back to
  an allocating encode; an OSC 10-12 code outside 10..=12 is ignored
  (was unreachable!); the Kitty palette value is filtered once instead of
  expect after is_some_and.
- cmux-relay: the default bind is a const SocketAddr (test: it equals
  127.0.0.1:8787); TicketAuthority::open builds its value directly (test:
  the default issuer is valid); the CLI subcommand uses next_if; a Join
  ticket from the CLI is an error, not unreachable!; a missing SIGTERM
  handler stops on Ctrl-C only; a closed admission semaphore (private,
  never closed) stops admitting instead of panicking.
- cmux-pty: its only hits were in cfg(all(test, unix)) test code.

Ratchet baseline lowered (includes the regex fix in the previous commit).

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

* CMUXMobileCore: a render grid frame past a 16-bit terminal size or 1M scrollback rows is malformed; span width checked without overflow (crash program phase 3)

MobileProtocolFuzzTests hung for 180 s on a mutated frame: rows or
scrollback_rows near Int.max passed validation, and the phone's replay
(fullSnapshotBytes -> appendFlowLines) looped to build an unbounded byte
stream; cell_width Int.max made span.column + width overflow (a Swift trap).
Now columns and rows must be 1...65535 (Ghostty's 16-bit size) and
scrollback_rows at most 1_000_000 (the phone keeps 20000), else
invalidDimensions; the width check is width <= columns - column.

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

* cmux-next: red tests for the cmux Updated! card and Share cmux (cx-7py7)

Behavior tests: the card shows once after a version change, never on a
first install; its x is per version and shares What's New's seen state;
it wins the bottom slot over the tip (a staged update wins over it); its
rows run updates.whatsNew and app.shareCmux; Copy Link copies the shown
message (with the plain download link) and turns into Copied.

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

* optchat-chief: test a marked subagent session passes acpmux's env allowlist and takes its TTL from its directory (red)

The live proof on cmux-lawrence-2 (tag subp2) showed every Claude subagent
refused: session/new: env key CLAUDE_CODE_PROMPT_CACHE_TTL is not one a
session may set. The fake now refuses what acpmux refuses.

* optchat-chief: test the subagent TTL comes from its directory's settings, and a user directory gets no mark (red)

* crash P1b red: MessagesLab pane controller read before install (traps today)

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

* crash P1b red: an unusable auth callback scheme traps at sign-in (CmuxNextCloud)

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

* test(optchat): compactor asks Claude Code for the haiku alias; user env files private and gated (red)

Claude Code 2.1.287 reports claude-haiku-5-5 as unrecognized, so the
compactor fell back to the turn model (and lost its cost parity); it
takes the 'haiku' alias. The copied user settings env can hold secrets.
Adds compactor_model_for and session_dir::copy_user_env_allowed (seams,
today's behavior) and failing tests: a Claude Code compactor resolves
to 'haiku', the API route keeps claude-haiku-5-5; the session and
compactor slot settings files are 0600; the copy happens only on macOS
and OPTCHAT_COPY_USER_ENV=0 turns it off.

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

* plans: crash program status after phase 2 and the first phase 3 fuzz landings

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

* CMUXMobileCore: render grid limits match what Macs send; scrolled rows and the replay cell budget bounded (review fixes, crash program phase 3)

Review subagent findings on d2ae62f2ea87:
- scrolled_rows had no upper bound: String(repeating:count:) in the replay
  trapped or exhausted memory. Now scrolled_rows <= rows + scrollback_rows
  (what every producer sends).
- 65535 x (1M + 65535) cells still meant ~70 GB of replay. Limits now match
  the producers: columns and rows <= 4096 (DaemonRenderGridState.maxDimension),
  scrollback_rows <= MobileTerminalScrollbackPreference.maximumRows (20000),
  and columns * (rows + scrollback_rows) <= 32 Mi cells.
- New error case invalidSize(columns:rows:scrollbackRows:scrolledRows:).
- Fuzz test: the base frames must decode, at least 100 mutated frames must
  decode (no vacuous pass), a second base (screen-anchored delta with
  scrolled_rows, scrollback, cursor, cleared rows) reaches the scroll fields,
  and deterministic limit tests check the maximum and one past each limit.

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

* crash P1b: no force unwraps or IUOs in CmuxMessagesLab and the CmuxNext modules outside CmuxNextApp (cx-6so)

force_unwrap (new ratchet counting) and iuo to 0 outside the deferred and excluded sites.
MessagesLab: MessagesLabHome 62 -> 6 left (RowDrawing WideBitmap/RowBitmaps path: deferred
until the TextLayout P0 fix cx-qpqs lands, chief's decision), MessagesLabSidebar 18 -> 2 left
(SidebarDraw.bitmap, same CGImage? design, done with the WideBitmap cascade), iuo 4 -> 0.
CmuxNext modules (Actions, AgentActivity, AgentPane, Apps, Bookmarks, Browser,
BrowserAutomation, BrowserImport, Cloud, CodeRouter, Design, Feed, History, Layout,
Onboarding, Pages, Remote, RemoteView, Resources, Tabs, Terminal; CmuxNextSettings is frozen and lands separately): force_unwrap
-> 0, iuo (Browser 8, Onboarding 17) -> 0. CmuxNextApp and CmuxNextSidebar untouched.

Per site (behavior unchanged except "no trap"):
- MessagesLab ChatController.store/demo: plain optionals (most readers already read them
  optional-style); install() works on locals and returns the store. Red: 6204a39af730.
- HeaderBar pill constraint, AddressBarView controller and trailing constraints,
  BrowserChromeView.toolbarHeight, Onboarding buttons, gallery bar: lazy vars built at the
  same line of init as before (`_ = x` where the next line did not read it).
- LoadErrorView buttons: `let` with targets set in init. SidebarPalette.resolve: local
  builder; the synchronous appearance block sets it.
- Literal URLs, color spaces, UI fonts, function-key scalars: non-trapping construction
  with a stated fallback (/dev/null URL, device RGB, system font, NUL) and a test per type
  that every literal resolves (CrashLiteralTests in 10 CmuxNext test targets,
  CrashSafeGraphicsTests, SidebarCrashSafeTests). BrowserProfileID.default from bytes.
- CloudConfiguration: an environment callback scheme that cannot form a URL is ignored
  (it trapped at sign-in; red 091671413c3b); signInURL builds with optional components.
- ImportedExtension.webStoreURL returns URL? (an imported id may not form a URL).
- Dictionary re-reads -> key/value iteration; optional chains on spring dictionaries;
  baseAddress guards (PTY write, LZ4 decode, CSV read, CEF switches, LongText); try? for
  NSDataDetector/NSRegularExpression; optional CGGradient draws nothing; WKNavigation! ->
  WKNavigation? in delegate signatures (same type).
Vendor patches regenerated in this commit (sync-messageslab.sh --check passes).

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

* cmux-next: cmux Updated! card and Share cmux modal (cx-7py7)

After the app updates to a new version, the sidebar's bottom card slot
shows "cmux Updated!" with an x and two rows on the tip card's glass:
"See What's New" runs updates.whatsNew (the in-app What's New page) and
"Share cmux" runs the new app.shareCmux. The card reads What's New's one
seen state (WhatsNewCenter): it shows when this launch's version is newer
than the last seen one, never on a first install; opening What's New from
any entrypoint or the card's x marks the version seen, so the card and the
sidebar item's dot clear together. updates.showWhatsNew off hides it. A
staged update card keeps the slot first; the tip waits for the card.

app.shareCmux (palette, Help menu, the card row) opens a centered modal in
the cmux dialog style: title, one line, an editable message with the plain
https://cmux.com/download link, Copy Link (copies the shown text, then
reads Copied until the text changes), an x, and Escape. No referral or
tracking. CmuxDialogButtonView gains a public init and retitle(_:) so the
modal reuses the dialog button.

debug.updater gains updated {previous}, share and share-copy for DEV and
NIGHTLY proofs. Strings: en + ja reviewed, other languages needs_review.

Keyboard: Copy Link takes the keyboard when the modal opens and Return
copies; Tab leaves the message box; the x draws no system focus ring.
Card rows announce as buttons, show a pressed fill and follow the
writing direction.

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

* optchat-chief: a marked subagent takes its TTL from its directory's settings, never from session env

Fixes the L4 regression the live proof found: acpmux allows only
CMUX_WORKSPACE_ID in a session's env, so every Claude subagent with a cache
mark was refused at session/new. The TTL now goes to the subagent
directory's .claude settings (session_dir::set_prompt_cache_ttl, as the
turns' session directory), and a spawn in a user's directory gets no mark.

* fixup: fuzz mutates one or two fields per frame so most frames reach the replay

* fix(optchat): Claude Code compactor uses the haiku alias; user env files private and gated

- A Claude Code compactor now asks for 'haiku' (compactor_model_for),
  which Claude Code maps to its current Haiku. Claude Code 2.1.287 does
  not know 'claude-haiku-5-5', so the compactor fell back to the turn
  model and lost its cost parity. The Messages API route keeps
  claude-haiku-5-5; another harness keeps its own default. The host
  log's compactor line names the resolved model once at start.
- The session's settings.json and settings.local.json and each compactor
  slot's settings are written 0600 from the first byte (write_private):
  they may hold the user's settings env. Its values are never logged.
- The user's settings env is copied only on a macOS host (the user's own
  Mac, never a Linux VM or dev backend image), only from a settings file
  this user owns, and OPTCHAT_COPY_USER_ENV=0 turns it off. The subagent
  directory gets no copy: subagents load the user's settings themselves.

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

* fixup: decode floor 100 (about 210 of 6000 mutated inputs decode per base)

* test(acpmux): red: ACP Registry agents as harnesses, cmux harness registry and add --registry (cx-1785)

* feat(acpmux): ACP Registry agents: installed ones are harnesses, cmux harness registry, add --registry (cx-1785)

t3code supports every agent of the ACP Registry (41 today: GitHub Copilot,
Qwen Code, goose, Kimi, Amp, Cline, Auggie, Factory Droid, Junie, Kilo,
Mistral Vibe, ...); acpmux knew none of them. registry.rs reads the
registry (daemon fetch at start, HTTPS only, no redirects, 1 MiB cap,
cached in ~/.acpmux/acp-registry) and checks every field: exact npx/uvx
versions, https archives, relative archive commands without .., no loader
or runner env keys, no ${} expansions; a bad agent is dropped alone.
Nothing runs at discovery: an agent whose program is on PATH (its binary
command, or the known bin of its npm package) becomes a harness with the
registry's arguments, after built-in harnesses and after the default
choice. cmux harness registry lists every agent and how it starts here;
cmux harness add ID --registry writes a profile at the pinned version
(installed program, else npx, else uvx). Archives are never downloaded.
The design follows t3code's ACP Registry driver (MIT, credited).

* test(agent-brands): red: ACP Registry agent ids resolve to their brand marks (cx-1785)

* fix(agent-brands): github-copilot-cli (the ACP Registry id) shows the Copilot mark (cx-1785)

* test(acpmux): red: cmux harness login signs in through ACP authMethods (cx-1785)

* feat(acpmux): cmux harness login: sign in to any ACP harness with its own methods (cx-1785)

acpmux never called ACP authenticate and showed only a login text, so a
harness that needs a sign-in (Cursor, Gemini, Copilot, Antigravity, most
registry agents) could not be signed in from cmux. cmux harness login ID
starts the harness in a private temp folder, reads its authMethods and
runs one: agent methods through authenticate (the agent opens the
browser), terminal methods by running the harness's command line plus the
method's arguments in this terminal, env_var methods by printing the
Keychain step. --status checks with session/new (-32000 = sign-in
required); --list shows the methods. Claude Code runs claude auth login.
Method types follow t3code's ACP Registry auth (MIT, credited).

* test(acpmux): red: an installed Antigravity ACP server (agy_acp_server.par) is the antigravity harness (cx-1785)

* feat(acpmux): Antigravity harness: an installed agy_acp_server.par (cx-1785)

t3code supports Google Antigravity through its ACP server
(agy_acp_server.par). The ACP Registry lists it, but its program name is
not its id, so registry discovery skipped it. The pinned name makes an
installed server the antigravity harness; cmux harness login antigravity
runs its Google sign-in (oauth-personal and the other ACP methods it
offers). acpmux still downloads nothing: the registry entry has no
sha256, so the server must be installed from its page.

* release-notes: remote tab datalist suggestions and extension popups (CEF cmux.21)

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

* cmux-next: red test, the Updated card skips nightly updates (cx-7py7)

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

* release notes: plain cmux is this app's CLI in every terminal (cx-c59m.1)

* cmux-next: the Updated card shows only for stable and RC updates (cx-7py7)

Nightly builds update several times a day, so a nightly version no longer
shows the cmux Updated! card (chief decision 2026-10-08). The What's New
item keeps its own rule. debug.updater updated still forces the card.

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

* test(backend): Mac grants from before the mac grant cap lose execute once, on bind and in the per-request grant check (red, cx-wb5.64)

* fix(backend): drop execute once from Mac install grants made before the mac grant cap (install.mac_execute_narrow on UserDO bind, cx-wb5.64)

* indicators: red tests, Symbols is the default status icon set (Lawrence 2026-10-09) (cx-kxa2)

* indicators: Symbols is the default status icon set; Dots stays as the original drawing in the switcher (cx-kxa2)

* agent-screen-detection: red tests for herdr 0.9.3 parity and the herdr pin check

Behavior tests only. They fail on this tree:
- manifests: agy dialogs, thought-summary spinner and esc footer; agy and grok
  background work at the prompt is idle; codex Folder access trust dialog;
  pi spinner Working line (herdr 2563803d manifests).
- process identity: Hermes through its Python installer (herdr e35f3937,
  reporter capture copied under Apache-2.0), non-interactive Letta processes
  (fc1cb77f), Cline .cline launcher and argument mentions (f3cbe03f), Kimi and
  omp package launchers (63ea2314, cd8306d7).
- scripts/cmux-next/tests/test_herdr_sync.py: sync/check/drift for the herdr
  pin, including check failing on a mismatched pin (herdr-sync.py does not
  exist yet).

* agent-screen-detection: sync herdr 0.9.3 (2563803d) and keep it current

Pin and tooling:
- HERDR_UPSTREAM.toml pins herdr 2563803d (0.9.3, engine 3) with the upstream
  sha256 of each vendored manifest and of the tracked engine sources
  (src/detect/{manifest,mod,manifest_update}.rs, src/pane/{agent_detection,osc}.rs).
  HERDR_PATCHES.toml documents local manifest edits; there are none now.
- scripts/cmux-next/herdr-sync.py (stdlib, python3 -I): sync (git show, never
  checkout; reapplies documented patches or fails), check (offline), drift
  (nonzero on drift, markdown issue body).
- cmux-next checks: herdr-sync.py check and its unit tests in the Script tests
  step on every push and PR.
- .github/workflows/herdr-upstream-drift.yml: daily + dispatch, checks out
  feat-cmux-next, shallow-clones the herdr tip, opens or updates one
  "herdr upstream drift" issue. No PRs.

Port:
- manifests antigravity 2026.10.05.1, codex 2026.10.01.1, grok 2026.10.05.1,
  pi 2026.10.01.1, byte-identical to upstream. The cmux Grok precedence patch
  is dropped: upstream 2026.10.05.1 covers it.
- process identity: Hermes Python installer (e35f3937), Letta interactivity
  (fc1cb77f), Cline .cline launcher and no identity from another program's
  arguments (f3cbe03f), Kimi and omp package launchers (63ea2314, cd8306d7),
  in the new src/process/launchers.rs (package launchers moved there).
- background_agent.rs + scanner: a suspended or backgrounded agent keeps its
  identity while that exact process lives in the terminal session, and its
  screen is not read (950d012c).
- ATTRIBUTIONS (plugin and cmux-tui), manifests/README, README and the
  hand-written notice (THIRD_PARTY_LICENSES.md section) name the new pin and
  the reviewed commits; the Apache text is unchanged.

* cmux-tui-core: red test, an OSC 7501 record that finishes posts one done notification (cx-kxa2)

* cmux-tui-core: an OSC 7501 record that finishes posts one done notification (info), shown by clients only for a terminal the user cannot see (cx-kxa2)

* herdr-sync: red tests, whole-token patch edits, changed-file notice, removed tracked files and odd upstream text (cx-kxa2 review)

* herdr-sync: patch edits match whole tokens and stamp a Manaflow change notice; drift tracks the ported process files, reports removed files, exits 2 on tool failures, keeps upstream text inert; the drift job clones history for the commit list (cx-kxa2 review)

* cmux-next: menus and composer pickers pick by press-drag-release (#18727)

* test: composer popovers pick by press-drag-release

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

* test: rows are option buttons, as the pickers build them

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

* fix: menus and composer popovers pick by press-drag-release

One shared press session (ui/pressRelease.ts) for Menu and every
composer popover trigger: a press opens, a release over a row picks it,
a release on the trigger within 150 ms or without moving is a click.

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

* test: a release over a row's icon picks the row

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

* fix: a release over a row's icon picks the row

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

---------

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

* test(spaces): scripted space.new gives the new space its own workspace without moving the view (cx-f8aw, red)

* package-test-lane: an unknown CMUX_SWIFT_SANITIZE is refused before any build (red, crash program phase 3)

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

* package-test-lane: CMUX_SWIFT_SANITIZE=address|thread|undefined runs a lane under that sanitizer (crash program phase 3)

Adds --sanitize and a separate --scratch-path (.build-sanitize-<name>) to
every swift build/test of the lane, so a sanitizer pass is one ci-step:
cmux-ci run --class light --script scripts/ci/package-test-lane.sh ... --env CMUX_SWIFT_SANITIZE=thread

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

* test(cmux-server-core): an identifier that does not quote must not panic the SQL builder (red)

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

* fix(cmux-tui): zero production panics in cmux-server-core, cmux-layout-reducer, cmux-app-manifest, cmux-tasks-core

Each crate root now denies clippy unwrap_used, expect_used, panic,
unreachable, todo, unimplemented and exit outside cfg(test).

- cmux-server-core: SQL identifiers and literals that do not quote
  (empty or NUL; impossible for constants and app_<AppId>) become "", a
  zero-length identifier Postgres refuses, instead of a panic; nothing
  unquoted reaches the SQL. SCRAM hmac/hi return Option into the
  existing Option of scram_verifier. Literal absolute Unix paths use
  HostPath::unix_absolute (debug_assert that HostPath::new accepts them
  unchanged). PairingCode::as_str/display and the fingerprint read do not
  unwrap; the word list fills a fixed array (the existing test checks
  2,048 distinct words). An unparsable min_cmux_version after validation
  is ManifestError::Invalid.
- cmux-layout-reducer: take_tab/place_tab/move_tab return Reject
  (UnknownPane/UnknownTab/UnknownWorkspace/RowSetMismatch) instead of
  expect; apply works on a copy, so a reject still changes nothing. No new
  Reject variant (no FFI change). The cfg(kani) proof harness keeps its
  expects under crash-allow (a panic is the proof failure).
- cmux-app-manifest: every embedded schema and table loads once into a
  Result; a load failure refuses (an Issue, a reserved CLI word, an
  unknown scope), fail closed. Unit tests load every embedded asset and
  drive the broken-table paths.
- cmux-tasks-core: handler lookups of validated entities reject with
  not_found before the first write (task_delegate now looks the task up
  before it inserts the session); cascade loops and void helpers skip a
  missing entity; place() plans the rebalance and checks the gap before
  any write and refuses with conflict when it has no room (state
  unchanged); sort_key::sequence stops instead of panicking.

Ratchet baseline lowered.

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

* test(backend): team_vm.retired.export downloads /srv/team of a fenced retired VM (cx-lyvg, red)

Behavior tests for the missing export path: an owner or admin exports the
retired, fenced team VM's /srv/team as a single-use tar download; the VM
stays paused and fenced; members, agents and installs are refused; a
VM not fenced yet, more than 2 GiB or 5000 entries, and a VM without
/srv/team answer declared errors; a broken provider stream resumes with
Range. Dashboard: a Download button on paused retired rows for owners and
admins, and the Rebuild and Delete confirms say to download first.

* feat(backend): team_vm.retired.export streams /srv/team of a fenced retired VM (cx-lyvg)

A rebuild retires the old team VM with its team files on /srv/team, and the
fence (cx-009a) means it never runs again, so the copy-off step had no path.

team_vm.retired.export {vm} (owner/admin, person session only; TeamDO role
check and team_vm.taint_audit retired_exported, like retired.delete) walks
/srv/team through Freestyle's file API (fs/dir, fs/stat; the disk of a
paused, budget-spent VM stays readable), refuses a VM that is not paused
and fenced (team_vm.retired_not_fenced), over 2 GiB of files or 5000
entries (team_vm.export_too_large), without /srv/team
(team_vm.export_no_files) or a walk over 60 s (team_vm.export_timeout), and
stores the manifest under a single-use 5-minute ticket (SHA-256 only) in
TeamVmDO. GET /v1/team-vm/export/<team>/<ticket> streams a pax/ustar tar
from that manifest: one fs/read per file, chunk by chunk, so the Worker
never holds the archive; the exact Content-Length (FixedLengthStream) turns
a cut stream into a failed download; a broken read resumes with Range; 30
minute stream limit. Symlinks and special files are skipped and listed.
Nothing starts or unfences the VM. No R2: the only bucket belongs to Home
attachments, and a frozen VM is already the source of truth.

Dashboard: Download files on each paused retired row (owners and admins);
the Delete confirm says to download first and has the button; the Rebuild
confirm says the same. The files-copied checkbox stays manual. en + ja.

Generated: catalog, TS client, cmux-app-host files (the op is in `never`:
gen-cmux-global NEVER names it; full regen also picks up the conversation.*
ops that 7e10e236714b left ungenerated).

* test(backend): a retired team VM's disk holds its newest writes; paused-disk stat metadata (cx-lyvg, red 2)

Live check 2026-10-09 (dev account): Freestyle's file API reads a paused
VM's disk image, not the guest page cache. A file written seconds before
the pause read back as 0 bytes (stat size 0); after a guest sync it read
back whole (300000 bytes, Range 206). retireVm must sync a running VM
before the budget and pause, never wake a paused VM for it, and never let
a failed sync hold the fence. stat answers modified as epoch seconds
(running) or empty (paused) and numeric owners when paused.

* fix(backend): retireVm syncs a running team VM before the fence; read paused-disk stat metadata (cx-lyvg)

The provider's file API reads a paused VM's disk image, so writes still in
the guest page cache at the pause were invisible to team_vm.retired.export
(measured: 0 bytes). retireVm now reads the state and, only on a running
VM, runs `sync` as root through exec-await (15 s), then spends the budget
and pauses as before; a failed or slow sync is logged and never holds the
fence, and a paused VM is never woken for it. fs/stat `modified` is epoch
seconds (running) or empty (paused). Plan and coordination notes.

* fix(backend): export download sends referrer-policy no-referrer; rebuild copy names the retired VM row (cx-lyvg review)

Review by the coordinator: the ticket is in the URL path, so the download
answer sets referrer-policy: no-referrer. The Rebuild confirm says to
download the team files from the retired VM row after the rebuild. The
full app-host regen keeps the conversation.* output that 7e10e236714b
left ungenerated. Coordination note names the pre-change fence limit.

* test(app): every static URL literal and function-key character parses (red: helpers missing) (cx-6so P1b, helper H4)

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

* fix(app): no force unwrap on static literals in CmuxNextApp (force_unwrap CmuxNextApp 67 -> 48) (cx-6so P1b, helper H4)

Per site (no behavior change except "no trap"):
- Static URLs (feedback, docs, about:blank):
  StaticURL enum; `url` never traps (a literal that did not parse would be
  a file URL of the same text); StaticURLTests checks every case parses.
- NS*FunctionKey characters (BrowserChordTable, DebugKey): arrows use the
  existing Shortcut.*ArrowKey constants; page up/down, home, end, forward
  delete use FunctionKeyCharacter.string, which gives "" for a non-scalar
  code; FunctionKeyCharacterTests checks every code the app uses.
- CGColorSpace(name: sRGB)! (DebugPaletteCapture, ScreenEmojiIcon):
  `?? CGColorSpaceCreateDeviceRGB()`, a real color space.
- AppResourceSource: the daemon index is bound once instead of re-read
  with `!`.
- HistoryService: an empty or missing visit title falls back to the URL
  with flatMap instead of `title!`.

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

* docs(cmux-next): OSC 7501 matrix records landed SHAs and live evidence

Bead: cx-6so.36.5

* cmux-next composer: the folder popover's field reads as a menu row

The folder popover (LocationPicker allowPath) gave its search field no styles, so WebKit drew a
native text box with the system blue focus ring, and the placeholder repeated the button label
(Choose folder). The field is now a borderless menu row on the menu fill with a hairline over the
results, and the placeholder says what it does (Search folders or type a path, all 21 locales).

The gallery's agent-home variant was not a fresh chat (snapshot without sessionId, summary with
""), so it showed this fallback field instead of the new chat's folder menu (Choose folder...).
Its snapshot now names the prewarmed session, as the app's does; a new agent-home-path variant
keeps the field in the gallery.

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

* test(home): my own send shows as typed at the press, after the echo and after a reopen (red: the reducer's local message was never marked plain and took the Markdown engine)

* optchat-chief: red tests, no request exceeds the API's 4 cache marks (turns, compactor nodes, subagents)

Measured on Claude Code 2.1.287 on the subscription login and the subrouter:
it marks its two system blocks and the last message of a session's first
request, and the last two messages of every later request (tool step,
follow-up prompt, steered message), so our view mark in the first message
makes 5 on a session's second request.

* fix(home): a message is plain unless HomeMapping marks it Markdown (an agent's text), so my own send shows as typed at the press, after the echo and after a reopen (green). The reducer's local message was measured before HomeMapping saw it and kept its Markdown layout under the same id

* fix(spaces): space.new shares one path that gives the new space its own workspace and shows it only when the run may change the view (cx-f8aw)

* fix(cmux-next): group color dots are named by their theme slot, never a fixed color word (cx-rcby)

* crash: escaping tasks no longer outlive the owner of an unowned back-reference (cx-6so P1b, helper H1)

Step B of the unowned plan (chief-approved A/B table): an unowned back-reference
traps only when the child outlives its owner, and the escapes that could make
it do so are tasks that keep the child and reach the owner after a suspension.
Each site now either does not keep the child, or pins the owner until the task
ends:
- weak child: AgentHistory/CommandHistory refresh (the owner's daemons are read
  before the first suspension), ThemeCoordinator's launch prune loop.
- owner values read before the task: AppOnboardingServices openProjects /
  resumeChats (folderFailed takes the logger) and importClassicSessions
  (machines + logger), so nothing after a suspension goes through the
  onboarding service.
- owner pinned for the task's duration (`[services]` capture, released when
  the one-shot task ends; no stored task, so no cycle): TabDragSession commit
  (4), QuitCoordinator quit/complete, PaneController new tab / close / pin,
  SidebarBridge reorder + command re-sync (bridge weak), WindowManager
  newWorkspace / newWindow / incognito window / profile switch,
  WorkspaceContentController focus intents ([services, state]), CEFTab
  exitContentFullscreen ([runtime]).
No reachable path changes while the owner lives (the app's services live for
the process); the change matters only where the owner can be freed (tests,
window teardown). Not driven by a test: freeing AppServices mid-task needs the
whole service graph torn down, which the lifetime tests of step A cover.
The unowned count is unchanged here (the references stay; step A allowlists
them with lifetime tests).

* notifications: red test, an OSC 7501 alert is recognized before its record arrives (cx-kxa2 live race)

* notifications: an OSC 7501 alert waits up to 1.5 s for its record (observed, no poll) so its banner gets the status badge (cx-kxa2 live race)

* notifications: red test, debug.notifications reports a banner's status badge attachment (cx-kxa2)

* notifications: debug.notifications reports each banner's status badge attachment (cx-kxa2)

* scripts: status-indicators-live.py, OSC 7501 and ACP status indicators on a fleet build per icon set and theme (cx-kxa2)

* plans: mobile Rust core proposal (uniffi, cmux-mobile-core/net/ffi, slice order)

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

* optchat-chief: no request exceeds the API's 4 cache marks

Claude Code 2.1.287 marks its two system blocks plus the last message of a
session's first request, and the last two messages of every later request
(tool step, size-loop follow-up, steered message), on the subscription login
and through sr alike. Our view mark stays in the first message's history,
so a session's second request had 5 and the API refused it (compactor nodes
0+8 and 8+4 in subp2).

A turn or compactor node that carries our mark now runs Claude Code with
DISABLE_PROMPT_CACHING=1 in its directory's settings env (Claude Code places
none; every request reads up to our mark, a turn's tool steps send their
tail uncached). One too small for a mark keeps Claude Code's marks. The pool
hint writes the next turn's choice, a turn drops its mark when its pooled
session started with Claude Code's marks on, and warm compactor sessions are
keyed by it. Subagents carry no mark of ours (long sessions: Claude Code's
own marks cache them step by step), so the spawn warm-up is idle.

* test(cmux-tui-cdp): a poisoned lock must not panic the CDP client (red)

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

* fix(cmux-tui-cdp): zero production panics; poisoned locks recover, rebuild or close

The crate root now denies clippy unwrap_used, expect_used, panic,
unreachable, todo, unimplemented and exit outside cfg(test). Per lock:

- FrameEpoch.wait_lock guards (): recover (PoisonError::into_inner),
  also for the condvar wait.
- Inner.pending is one map; every critical section is one insert,
  remove or drain: recover.
- EventQueue.state keeps retained_bytes equal to the sum of the queued
  sizes: on poison, rebuild the sum from the events and clear the poison.
- Inner.frame_epochs holds multi-field per-session navigation and
  screencast state: a poisoned lock is never read. The connection closes
  (pending calls fail, the event stream ends with Closed) and the caller
  gets an error, false or None.

The three Page.* handlers take the session id with let-else instead of
expect("guarded above"). The lock helpers, close_inner and the new tests
live in client/locks.rs and client/lock_tests.rs (client.rs stays under
its godfile baseline).

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

* cmux-tui: bundled-CLI daemon test is its own test target

tests/cli.rs is at its god-file limit, so the real-daemon bundled-CLI test
moves to tests/bundled_cli.rs with a small harness of its own.

* test(home): the send-as-typed test reads the controller's optional store and window view (base removed the IUOs)

* cmux-next: improve composer control hierarchy (#18485)

* refine agent composer context surface

* show composer controls in the toolbar

* update composer layout assertion

* cover visible access mode label

* test: cover model picker Escape away from search

* fix: close model picker on Escape from any control

* fix: keep unified composer radius tokenized

* chore(gallery): format chief inspector fixture

* chore(gallery): match web formatter output

* fix(composer): preserve full access warning color

* fix(composer): keep warning color while access menu is active

* test: locate model filter by accessible state

* cmux-next: red tests for the cmux Updated! card and Share cmux (cx-7py7)

Behavior tests: the card shows once after a version change, never on a
first install; its x is per version and shares What's New's seen state;
it wins the bottom slot over the tip (a staged update wins over it); its
rows run updates.whatsNew and app.shareCmux; Copy Link copies the shown
message (with the plain download link) and turns into Copied.

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

* optchat-chief: test a marked subagent session passes acpmux's env allowlist and takes its TTL from its directory (red)

The live proof on cmux-lawrence-2 (tag subp2) showed every Claude subagent
refused: session/new: env key CLAUDE_CODE_PROMPT_CACHE_TTL is not one a
session may set. The fake now refuses what acpmux refuses.

* optchat-chief: test the subagent TTL comes from its directory's settings, and a user directory gets no mark (red)

* test(optchat): compactor asks Claude Code for the haiku alias; user env files private and gated (red)

Claude Code 2.1.287 reports claude-haiku-5-5 as unrecognized, so the
compactor fell back to the turn model (and lost its cost parity); it
takes the 'haiku' alias. The copied user settings env can hold secrets.
Adds compactor_model_for and session_dir::copy_user_env_allowed (seams,
today's behavior) and failing tests: a Claude Code compactor resolves
to 'haiku', the API route keeps claude-haiku-5-5; the session and
compactor slot settings files are 0600; the copy happens only on macOS
and OPTCHAT_COPY_USER_ENV=0 turns it off.

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

* cmux-next: cmux Updated! card and Share cmux modal (cx-7py7)

After the app updates to a new version, the sidebar's bottom card slot
shows "cmux Updated!" with an x and two rows on the tip card's glass:
"See What's New" runs updates.whatsNew (the in-app What's New page) and
"Share cmux" runs the new app.shareCmux. The card reads What's New's one
seen state (WhatsNewCenter): it shows when this launch's version is newer
than the last seen one, never on a first install; opening What's New from
any entrypoint or the card's x marks the version seen, so the card and the
sidebar item's dot clear together. updates.showWhatsNew off hides it. A
staged update card keeps the slot first; the tip waits for the card.

app.shareCmux (palette, Help menu, the card row) opens a centered modal in
the cmux dialog style: title, one line, an editable message with the plain
https://cmux.com/download link, Copy Link (copies the shown text, then
reads Copied until the text changes), an x, and Escape. No referral or
tracking. CmuxDialogButtonView gains a public init and retitle(_:) so the
modal reuses the dialog button.

debug.updater gains updated {previous}, share and share-copy for DEV and
NIGHTLY proofs. Strings: en + ja reviewed, other languages needs_review.

Keyboard: Copy Link takes the keyboard when the modal opens and Return
copies; Tab leaves the message box; the x draws no system focus ring.
Card rows announce as buttons, show a pressed fill and follow the
writing direction.

Co-Authored-By: Claude Opus 5.5 (1M …
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