projmux 0.15.1 → 0.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1008,10 +1008,14 @@ tmux transport mirror:
1008
1008
  recovery route.
1009
1009
  - `rename pane` changes `Pane.metadata.name` and its `@projmux_pane_label`
1010
1010
  mirror only. It never writes the raw tmux `pane_title`.
1011
- - `rename window` is the explicit stable-identity path: it changes only
1012
- `Window.metadata.name`, its root-scoped same-kind name reservation, and the
1013
- exact live `@projmux_window_name` transport mirror. It does not change tmux
1014
- `window_name`.
1011
+ - `rename window` is the explicit stable-identity path: it changes
1012
+ `Window.metadata.name`, its root-scoped same-kind name reservation, the exact
1013
+ live `@projmux_window_name` transport mirror and, when the invocation has a
1014
+ proven runtime route, the tmux tab `window_name` through the canonical
1015
+ `rename-window -t @N -- <name>` -- the same argv shape the rename key and the
1016
+ Window menu Rename item go through. With no route it stays Registry-only and
1017
+ says on stderr that the tab did not converge. It never changes
1018
+ `metadata.displayName`.
1015
1019
  - `rename project` likewise writes only `Project.metadata.name` and the exact
1016
1020
  live session's `@projmux_project_name`; it never renames the tmux session.
1017
1021
  `rebind project` preserves the Project uid and session name while updating
@@ -1906,7 +1910,10 @@ Resource-first create:
1906
1910
  touched.
1907
1911
  - **Everything is detached.** No create path issues `switch-client`,
1908
1912
  `select-window`, `select-pane`, or `attach-session`. `focus pane` and
1909
- `-o pane-id` are how a caller ends up in the new pane.
1913
+ `-o pane-id` are how a caller ends up in the new pane. One exception: the
1914
+ human intent route `internal tmux window-create` (`window.create` key, Window
1915
+ menu New At End) moves exactly the pressing client to the new Window after
1916
+ its create commits; public `create` stays detached.
1910
1917
  - **Focus is navigation-only.** `focus project|window|pane` reads live tmux
1911
1918
  inventory and may move an existing client, but has no Registry store and
1912
1919
  issues no session/Window/Pane creation, identity-marker, rename, respawn, or
@@ -2004,9 +2011,11 @@ Selector and the implicit active target:
2004
2011
 
2005
2012
  Projmux keeps visible naming separate from source metadata:
2006
2013
 
2007
- - **User pane label** is persistent pane-scoped metadata stored in
2008
- `@projmux_pane_label`. The Rename Pane action sets or clears only this field;
2009
- it does not write the AI topic or raw pane title.
2014
+ - **User pane label** is pane-scoped metadata stored in `@projmux_pane_label`,
2015
+ the live mirror of the Pane's Registry `metadata.name`. The Rename Pane action
2016
+ renames the Registry Pane through the same owner as `rename pane`, which
2017
+ writes this mirror; an empty response changes nothing, and the action never
2018
+ writes the AI topic or raw pane title.
2010
2019
  - **Pane border label** is the primary visible pane name. In the app tmux
2011
2020
  config and native previews it resolves to user pane label first, agent AI
2012
2021
  topic second, known interactive shell command (`zsh`, `bash`, `fish`, `sh`,
@@ -127,6 +127,22 @@ failure before any byte is a non-ambiguous `provider-write-zero`; partial bytes,
127
127
  a write error after bytes, or a lost helper response is ambiguous and has
128
128
  `autoResend=false`. A complete write plus helper return is only a transport
129
129
  handoff, not proof that Claude parsed, displayed, processed, or answered it.
130
+ Push content has no length cap of its own: only the 8192-byte serialized
131
+ auth+user frame bounds it, and a size excess ends as
132
+ `provider-frame-too-large: frameBytes=N limitBytes=8192` with zero provider
133
+ bytes; the reply tool argv body stays at 4096 bytes.
134
+
135
+ `agent message send` applies the same budget to a Claude target before
136
+ acceptance, for plain sends and `--reply-to` alike. The sender renders the
137
+ content with the helper's renderer, once with its own executable and once with
138
+ the fixed executable phrase, keeps the longer frame, and counts an assumed
139
+ 48-byte auth token; it never reads a messaging token. A frame over 8192 bytes
140
+ exits nonzero with `provider-frame-too-large: frameBytes=N limitBytes=8192`
141
+ before any receipt is stored or the helper is called. Codex targets are not
142
+ pre-checked. A helper started before this rule still caps push content at 4096
143
+ bytes and fails a larger body with `provider-frame-invalid-content`; the
144
+ sender's receipt then names the rendered content bytes and asks to re-activate
145
+ (restart) the target Agent.
130
146
 
131
147
  The frozen frame is closed to Claude Code `2.1.263`. A different provider
132
148
  version, helper replacement, provider restart, registration replacement, or
@@ -208,6 +224,26 @@ real-provider observation is opt-in through
208
224
  `scripts/agent-dialogue-live-canary.sh`; each version-stress row must qualify
209
225
  independently.
210
226
 
227
+ `TestClaudeDialogueStreamReplaysObservedFrameCorpus` pins the reply-only
228
+ validator's verdict on preserved real frame shapes. Neither it nor L20 proves
229
+ installed-provider compatibility, value drift (the corpus keeps top-level key
230
+ shape with placeholder values), or acceptance of unknown frames and fields (the
231
+ closed vocabulary rejects them by design). L20 replays every corpus item whose
232
+ verdict is `drop` through the production reply-only validator: the Claude
233
+ fixture reads the corpus at run time from `PROJMUX_FAKE_CLAUDE_OBSERVED_FRAMES`,
234
+ emits each dropped side frame with its key set unchanged after init and before
235
+ its startup result, and the scenario requires the emitted names to equal the
236
+ corpus drop set. Those frames are key-shape placeholders, not model output. L20
237
+ does not emit the two recorded real shapes that are rejected today:
238
+ `result-success-25-keys` (the result allowlist lacks `origin`) and
239
+ `system-init-allowlist-diff` (the init allowlist lacks `memory_paths` and
240
+ `terminal_slash_commands`). Shapes without preserved evidence are gaps, not
241
+ items, so neither the corpus nor L20 exercises them: `command_lifecycle` keys
242
+ and values, a non-null assistant `context_management`, `system`
243
+ `thinking_tokens`, hook and user `tool_result` frames, the exact real init key
244
+ set, every nested value, `rate_limit_event` envelope keys beyond `type` and
245
+ `rate_limit_info`, and the lost earlier shape inventories.
246
+
211
247
  Provider sources: [SessionStart and Stop hooks](https://code.claude.com/docs/en/hooks)
212
248
  and [cross-session messaging](https://code.claude.com/docs/en/cross-session-messaging).
213
249
 
package/docs/cli-guide.md CHANGED
@@ -252,8 +252,27 @@ The contract:
252
252
  `create window|pane|agent|<provider>` is resource-backed on every spelling.
253
253
  There is no mode flag and no second parser: the same argv means the same thing
254
254
  whether or not `--project` is present, and `-w`, `--create-window`, `--pane`,
255
- `--selector`, `--placement`, `--name`, `--label`, `--cwd`, `--add-dir`, `-o`,
256
- and the `--` payload all reach the same parser either way.
255
+ `--selector`, `--placement`, `--cwd-from`, `--name`, `--label`, `-o`, and the
256
+ `--` payload all reach the same parser either way. `--cwd` and `--add-dir` are
257
+ provider create flags: they are defined on `create agent` and the three provider
258
+ shortcuts, not on `create window` or `create pane`.
259
+
260
+ `--cwd-from project|pane` selects where the new split starts. `project` (the
261
+ default) starts it in the owner Project root. `pane` starts it in the active
262
+ Pane's live directory while that directory is inside the owner Project root, and
263
+ otherwise starts it in the root and says so in one stderr line; it never
264
+ refuses. The flag cannot be combined with `--cwd`, which names an Agent working
265
+ directory outright.
266
+
267
+ On the CLI the flag is the only input: `create pane`, `create agent`, and the
268
+ provider shortcuts never read `[ai] split_cwd_from`, so with no `--cwd-from`
269
+ they always start in the owner Project root, whatever the global or project
270
+ config says. A CLI result is determined by its arguments, and scripts must not
271
+ change behavior because someone changed a Settings value. That config (and the
272
+ `AI > New splits start in` Settings row that writes it) only decides splits
273
+ started from the UI -- keybinding splits, the launcher, the resume picker's new
274
+ row, and the Pane menu -- in the order flag > project config > global config >
275
+ default. See [configuration.md](configuration.md).
257
276
 
258
277
  The scope resolves in two branches:
259
278
 
@@ -339,7 +358,10 @@ they name `--project` as the fix:
339
358
  gone — a `recoverable` runtime is reported, never adopted.
340
359
 
341
360
  Every create is **detached**: no create moves the client. Use `focus pane` or
342
- `-o pane-id` when you want to end up in the new pane. A natural create validates
361
+ `-o pane-id` when you want to end up in the new pane. One exception: the human
362
+ intent route `internal tmux window-create` (the `window.create` key and the
363
+ Window menu New At End) moves the pressing client to the new Window after its
364
+ create commits; public `create` stays detached. A natural create validates
343
365
  the inherited exact route and Pane containment. An explicit resource scope
344
366
  binds the selected app resource route without letting unrelated inherited
345
367
  `TMUX`/`TMUX_PANE` choose or change the resource target; exact Project plus
@@ -376,10 +398,16 @@ The generated `MouseDown3Pane` menu treats its Horizontal Split, Vertical
376
398
  Split, and Kill entries as Projmux resource actions. Both splits pass the exact
377
399
  clicked pane through the popup-origin anchor above and reach the same canonical
378
400
  `create pane` materializer as the CLI, so the new pane receives a Registry uid.
379
- Kill resolves that anchor's mirrored uid and reaches canonical `delete pane`,
380
- including its printed delete result. The menu never falls back to a raw tmux
381
- mutation when either route refuses. The reason is displayed on the exact client
382
- that opened the menu instead of being lost as a `run-shell` exit code.
401
+ Kill on a pane with `@projmux_pane_uid` resolves that anchor's mirrored uid and
402
+ reaches canonical `delete pane`, including its printed delete result; a pane
403
+ without the mirror runs tmux's own `kill-pane` instead of being refused. The
404
+ menu never falls back to a raw tmux mutation when either route refuses. The
405
+ reason is displayed on the exact client that opened the menu instead of being
406
+ lost as a `run-shell` exit code. The app config's `prefix >` and
407
+ `M-MouseDown3Pane` Pane menus follow the same rules, and its `prefix <`,
408
+ `MouseDown3Status`, and `M-MouseDown3Status` Window menus route Kill on a
409
+ mirrored Window to canonical `delete window`; see
410
+ [Managed menus](keybindings.md#managed-menus).
383
411
 
384
412
  tmux Respawn has no equivalent in the current resource model: it preserves the
385
413
  same pane handle, layout, Registry uid, and original command, while canonical
@@ -389,15 +417,40 @@ menu therefore omits Respawn entirely and does not expose a refusal handler or
389
417
  invent a replace operation. User-authored tmux bindings remain outside this
390
418
  managed-menu contract.
391
419
 
420
+ #### Managed close keys
421
+
422
+ The generated app config routes tmux's `prefix x` and `prefix &` through the
423
+ same resource routes whenever the target carries a Registry identity mirror.
424
+ `prefix x` on a Pane with `@projmux_pane_uid` confirms on the exact client, then
425
+ runs the Pane menu Kill route above: canonical `delete pane` with an
426
+ `intentional` termination receipt. The Agent that owned the Pane stays in the
427
+ Registry as Offline with a `deleted` exit, which Continue never replays.
428
+ `prefix &` on a Window with
429
+ `@projmux_window_uid` confirms, then runs `internal tmux window-delete`, which is
430
+ canonical `delete window` on that exact Window; a Project's last Window leaves a
431
+ zero-Window Project. A refusal is shown on that client and never falls back to
432
+ a raw kill. A target without the mirror keeps tmux's stock `confirm-before`
433
+ prompt and kill, with no Registry write. See
434
+ [Managed close keys](keybindings.md#managed-close-keys) for changing or
435
+ disabling the keys.
436
+
392
437
  ### Rename and rebind live convergence
393
438
 
394
439
  `rename project|window|pane` commits the selected Registry `metadata.name` and
395
- then updates only its exact UID-bound live transport field:
440
+ then updates only its exact UID-bound live transport fields:
396
441
 
397
442
  - Project: `@projmux_project_name` (never the tmux session name)
398
- - Window: `@projmux_window_name` (never tmux `window_name`)
443
+ - Window: `@projmux_window_name` and the tmux tab `window_name` (never
444
+ `displayName`)
399
445
  - Pane: `@projmux_pane_label` (never raw `pane_title`)
400
446
 
447
+ The Window tab converges only when the invocation has a proven runtime route,
448
+ which is what running inside the projmux tmux runtime gives it. Outside one the
449
+ rename is Registry-only: it exits zero and prints one line on stderr saying the
450
+ tab still shows the old name until a rename runs inside that runtime. There is
451
+ no flag for either behavior; the next time that Window is materialized from the
452
+ Registry, the tab carries the committed name.
453
+
401
454
  `rename agent` changes only the Agent's stable root-scoped `metadata.name`.
402
455
  It does not change the Agent topic, provider, lifecycle state, or managed Pane
403
456
  name/title. `create agent` derives the managed Pane's name from the Agent's
@@ -537,7 +590,9 @@ generated `pane-exited` hook. The supervisor must have durably journaled a
537
590
  same-generation `normal` receipt, the hook must name the exact `%N` Pane and
538
591
  the owner Window must carry its exact last-positive `$N/@N` binding on the same
539
592
  socket, and fresh preflight plus locked observations must still resolve the
540
- same Registry owner chain. A non-last Pane is removed while its directly owning
593
+ same Registry owner chain. This includes Windows re-materialized by Project
594
+ start/Continue topology replay: the replay records each Window it materializes
595
+ with its live `$N/@N` in the same commit. A non-last Pane is removed while its directly owning
541
596
  Agent is retained Offline with its conversation identity. For a last Pane, that evidence is retained until a
542
597
  matching `window-unlinked` hook removes the Window; a final Project Window also
543
598
  removes its Window descendants while retaining the exact Project uid, root,
@@ -2021,6 +2076,8 @@ projmux internal tmux popup-sessions
2021
2076
  projmux internal tmux popup-preview <session>
2022
2077
  projmux internal tmux rebalance-panes
2023
2078
  projmux internal tmux rename-pane <pane> <label>
2079
+ projmux internal tmux window-rename --client <key> --anchor <%pane> (--name-stdin | -- <name>)
2080
+ projmux internal tmux pane-rename --client <key> --anchor <%pane> (--name-stdin | -- <name>)
2024
2081
  projmux internal tmux print-config [--bin <path>]
2025
2082
  projmux internal tmux print-app-config [--bin <path>]
2026
2083
  projmux internal tmux install [--bin <path>] [--config <path>] [--include <path>]
@@ -2034,10 +2091,18 @@ accepted by `popup-toggle` mirror the historical sessionizer surface:
2034
2091
  `notify-sidebar`, `recent-windows`, `resource-inspector`, `ai-split-picker-right`,
2035
2092
  `ai-split-picker-down`, `ai-split-resume-right`, `ai-split-resume-down`,
2036
2093
  `ai-split-settings`.
2037
- `rename-pane` sets only the pane-scoped user label
2038
- `@projmux_pane_label`; an empty label clears the option. It does not change the
2039
- raw tmux pane title, AI topic, or AI topic manual-ownership flag. The canonical
2040
- keybinding action id is `rename-pane-label`. The retired `rename-pane-topic`
2094
+ `rename-pane` is a label-only helper: it sets only the pane-scoped
2095
+ `@projmux_pane_label`, and an empty label clears the option. It does not change
2096
+ the Registry Pane name, so the next Continue restores that name, and it does not
2097
+ change the raw tmux pane title, AI topic, or AI topic manual-ownership flag. No
2098
+ generated binding uses it.
2099
+ `window-rename` and `pane-rename` are the generated rename routes behind the
2100
+ `rename-window` and `rename-pane-label` keybinding actions and the Window menu
2101
+ Rename item. They resolve the anchor Pane to its exact Registry Window or Pane
2102
+ and rename it through the same owner as `projmux rename window|pane`, then
2103
+ report one line to the client. The generated bindings pass the prompt response
2104
+ with `--name-stdin` inside a quoted here-document; see
2105
+ [keybindings.md](keybindings.md#rename-keys) for the input rules. The retired `rename-pane-topic`
2041
2106
  keymap action is no longer accepted: replace a stale
2042
2107
  `[bindings.rename-pane-topic]` table with `[bindings.rename-pane-label]`.
2043
2108
  The `projmux agent topic set/clear` commands keep
package/docs/cli.md CHANGED
@@ -39,7 +39,7 @@ Root parser bridges outside the route graph are censused from their parser token
39
39
 
40
40
  Every route declares one allowed-effect record over seven independent resource axes. A pipe separates conditional success outcomes; preflight refusal remains zero-effect. `domain-effect=null` means the route has no typed extension beyond this resource tuple.
41
41
 
42
- The machine-readable manifest contains 190 route-effect records, including hidden plumbing that the public route sections omit.
42
+ The machine-readable manifest contains 191 route-effect records, including hidden plumbing that the public route sections omit.
43
43
 
44
44
  | Axis | Closed vocabulary |
45
45
  | --- | --- |
@@ -75,7 +75,7 @@ projmux <command> [args...]
75
75
  | [`projmux notification`](#projmux-notification) | canonical | Manage pending notification workflow state |
76
76
  | [`projmux open`](#projmux-open) | canonical | Open a Project runtime and move the current client to it |
77
77
  | [`projmux pin`](#projmux-pin) | canonical | Manage pinned project directories |
78
- | [`projmux prune`](#projmux-prune) | canonical | Prune stale Projects and snapshots |
78
+ | [`projmux prune`](#projmux-prune) | canonical | Prune stale Projects, Agents, and snapshots |
79
79
  | [`projmux quit`](#projmux-quit) | shortcut | Quit the app-owned projmux tmux runtime |
80
80
  | [`projmux reconcile`](#projmux-reconcile) | canonical | Preview or repair Registry and exact tmux resource drift |
81
81
  | [`projmux rebind`](#projmux-rebind) | canonical | Rebind a Project to a new absolute root without moving files |
@@ -761,7 +761,7 @@ Subcommands:
761
761
 
762
762
  | Route | Summary |
763
763
  | --- | --- |
764
- | [`projmux agent message send`](#projmux-agent-message-send) | Submit bounded peer coordination; --source selects a source Agent anchor, not caller authentication (default: active Pane) |
764
+ | [`projmux agent message send`](#projmux-agent-message-send) | Submit bounded peer coordination; --source selects a source Agent anchor, not caller authentication (default: active Pane); exits nonzero after printing a failed, refused, expired, or stale receipt |
765
765
  | [`projmux agent message status`](#projmux-agent-message-status) | Read a payload-free broker delivery receipt |
766
766
  | [`projmux agent message qualify`](#projmux-agent-message-qualify) | Explicitly qualify one exact Claude target using owned current-version isolation evidence and one marker push |
767
767
 
@@ -769,7 +769,7 @@ Canonical spelling: `projmux agent message send`, `projmux agent message status`
769
769
 
770
770
  #### `projmux agent message send`
771
771
 
772
- Submit bounded peer coordination; --source selects a source Agent anchor, not caller authentication (default: active Pane)
772
+ Submit bounded peer coordination; --source selects a source Agent anchor, not caller authentication (default: active Pane); exits nonzero after printing a failed, refused, expired, or stale receipt
773
773
 
774
774
  Selectorless authority: `explicit-target` — the route or caller must name the exact target.
775
775
 
@@ -1215,10 +1215,10 @@ Allowed effects:
1215
1215
  ```
1216
1216
  projmux create project --root <absolute-path> [--name <name>] [--label key=value]... [-o <mode>]
1217
1217
  projmux create window [--project <ref> | -p <ref>] [--name <name>] [--label key=value]... [-o <mode>] [-- <payload>]
1218
- projmux create pane [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--pane <ref>]... [--create-window] [--all-windows | --primary-window] [--placement right|down] [-o <mode>] [-- <payload>]
1219
- projmux create agent --provider <provider> [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--pane <ref>]... [--create-window] [--all-windows | --primary-window] [--placement right|down] [-o <mode>] [-- <payload>]
1220
- projmux create codex [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--create-window] [--all-windows | --primary-window] [--placement right|down] [-o <mode>] [-- <payload>]
1221
- projmux create claude|antigravity [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--create-window] [--all-windows | --primary-window] [--placement right|down] [-o <mode>] [-- <payload>]
1218
+ projmux create pane [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--pane <ref>]... [--create-window] [--all-windows | --primary-window] [--placement right|down] [--cwd-from project|pane] [-o <mode>] [-- <payload>]
1219
+ projmux create agent --provider <provider> [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--pane <ref>]... [--create-window] [--all-windows | --primary-window] [--placement right|down] [--cwd-from project|pane] [-o <mode>] [-- <payload>]
1220
+ projmux create codex [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--create-window] [--all-windows | --primary-window] [--placement right|down] [--cwd-from project|pane] [-o <mode>] [-- <payload>]
1221
+ projmux create claude|antigravity [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--create-window] [--all-windows | --primary-window] [--placement right|down] [--cwd-from project|pane] [-o <mode>] [-- <payload>]
1222
1222
  projmux create notification --text <s> --target <SESSION[:WINDOW[.PANE]]> [--socket <s>]
1223
1223
  projmux create snapshot
1224
1224
  ```
@@ -1308,7 +1308,7 @@ Allowed effects:
1308
1308
  - `domain-effect=null`
1309
1309
 
1310
1310
  ```
1311
- projmux create pane [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
1311
+ projmux create pane [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [--cwd-from project|pane] [-o <mode>] [-- <payload>]
1312
1312
  ```
1313
1313
 
1314
1314
  Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`, `receipt`
@@ -1331,7 +1331,7 @@ Allowed effects:
1331
1331
  - `domain-effect=null`
1332
1332
 
1333
1333
  ```
1334
- projmux create agent --provider <provider> [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
1334
+ projmux create agent --provider <provider> [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [--cwd-from project|pane] [-o <mode>] [-- <payload>]
1335
1335
  ```
1336
1336
 
1337
1337
  Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`, `receipt`
@@ -1396,7 +1396,7 @@ Allowed effects:
1396
1396
  - `domain-effect=null`
1397
1397
 
1398
1398
  ```
1399
- projmux create codex [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
1399
+ projmux create codex [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [--cwd-from project|pane] [-o <mode>] [-- <payload>]
1400
1400
  ```
1401
1401
 
1402
1402
  Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`, `receipt`
@@ -1419,7 +1419,7 @@ Allowed effects:
1419
1419
  - `domain-effect=null`
1420
1420
 
1421
1421
  ```
1422
- projmux create claude [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
1422
+ projmux create claude [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [--cwd-from project|pane] [-o <mode>] [-- <payload>]
1423
1423
  ```
1424
1424
 
1425
1425
  Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`, `receipt`
@@ -1442,7 +1442,7 @@ Allowed effects:
1442
1442
  - `domain-effect=null`
1443
1443
 
1444
1444
  ```
1445
- projmux create antigravity [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
1445
+ projmux create antigravity [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--all-windows | --primary-window] [--name <name>] [--label key=value]... [--placement right|down] [--cwd-from project|pane] [-o <mode>] [-- <payload>]
1446
1446
  ```
1447
1447
 
1448
1448
  Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`, `receipt`
@@ -2600,7 +2600,7 @@ projmux pin project list|add|remove|toggle|clear
2600
2600
 
2601
2601
  ## `projmux prune`
2602
2602
 
2603
- Prune stale Projects and snapshots
2603
+ Prune stale Projects, Agents, and snapshots
2604
2604
 
2605
2605
  Selectorless authority: `refusal` — there is no safe selectorless action; refuse before output or mutation.
2606
2606
 
@@ -2618,16 +2618,39 @@ Allowed effects:
2618
2618
  ```
2619
2619
  projmux prune snapshot [--older-than <duration>]
2620
2620
  projmux prune project --missing --older-than <duration> [--yes]
2621
+ projmux prune agent --older-than <duration> [--no-session-ref] [--no-pane] [--exclude <agent-ref>]... [--yes]
2621
2622
  ```
2622
2623
 
2623
2624
  Subcommands:
2624
2625
 
2625
2626
  | Route | Summary |
2626
2627
  | --- | --- |
2628
+ | [`projmux prune agent`](#projmux-prune-agent) | Delete Offline or Failed Agents past a bounded age whose session ref or managed Panes are gone; live Panes and Running Agents are never selected |
2627
2629
  | [`projmux prune project`](#projmux-prune-project) | Delete Projects whose spec.root has been missing for a bounded age |
2628
2630
  | [`projmux prune snapshot`](#projmux-prune-snapshot) | Inspect or delete preserved session snapshots (canonical spelling) |
2629
2631
 
2630
- Canonical spelling: `projmux prune project`, `projmux prune snapshot`
2632
+ Canonical spelling: `projmux prune agent`, `projmux prune project`, `projmux prune snapshot`
2633
+
2634
+ ### `projmux prune agent`
2635
+
2636
+ Delete Offline or Failed Agents past a bounded age whose session ref or managed Panes are gone; live Panes and Running Agents are never selected
2637
+
2638
+ Selectorless authority: `explicit-fan-out` — the route spelling is an intentional global or whole-set opt-in.
2639
+
2640
+ Allowed effects:
2641
+
2642
+ - `identity=removed`
2643
+ - `address=released`
2644
+ - `topology=removed`
2645
+ - `desired-state=removed`
2646
+ - `runtime=unchanged`
2647
+ - `focus=unchanged`
2648
+ - `cardinality=zero-or-more`
2649
+ - `domain-effect=null`
2650
+
2651
+ ```
2652
+ projmux prune agent --older-than <duration> [--no-session-ref] [--no-pane] [--exclude <agent-ref>]... [--yes]
2653
+ ```
2631
2654
 
2632
2655
  ### `projmux prune project`
2633
2656
 
@@ -2847,7 +2870,7 @@ Subcommands:
2847
2870
  | Route | Summary |
2848
2871
  | --- | --- |
2849
2872
  | [`projmux rename project`](#projmux-rename-project) | Rename a Projmux Project resource; with no selector inside tmux, the active Project |
2850
- | [`projmux rename window`](#projmux-rename-window) | Rename a Projmux Window resource; inside tmux a reference resolves within the active Project or ControlSession and no selector means the active Window |
2873
+ | [`projmux rename window`](#projmux-rename-window) | Rename a Projmux Window resource; inside tmux a reference resolves within the active Project or ControlSession, no selector means the active Window, and the tmux tab is renamed with it |
2851
2874
  | [`projmux rename pane`](#projmux-rename-pane) | Rename a Projmux Pane resource; inside tmux a reference resolves within the active Project or ControlSession and no selector means the active Pane; does not change tmux pane_title |
2852
2875
  | [`projmux rename agent`](#projmux-rename-agent) | Rename an Agent stable resource name within the active Project or ControlSession without changing its topic, provider, or managed Pane |
2853
2876
 
@@ -2880,7 +2903,7 @@ Output modes (`-o`): `receipt`, `none`
2880
2903
 
2881
2904
  ### `projmux rename window`
2882
2905
 
2883
- Rename a Projmux Window resource; inside tmux a reference resolves within the active Project or ControlSession and no selector means the active Window
2906
+ Rename a Projmux Window resource; inside tmux a reference resolves within the active Project or ControlSession, no selector means the active Window, and the tmux tab is renamed with it
2884
2907
 
2885
2908
  Selectorless authority: `natural-omitted` — omission resolves one predictable current resource or documented contextual read/scope; any selector replaces it.
2886
2909
 
@@ -342,6 +342,7 @@ success = "#5faf87"
342
342
  action_required = "#ffaf00"
343
343
  pane_active_bg = "default"
344
344
  focus = "#00ffff"
345
+ provenance = "#ff8700"
345
346
  ```
346
347
 
347
348
  `text_primary` controls primary content text in native terminal-rendered UI.
@@ -360,6 +361,13 @@ active-pane background tint, and `focus` is the active-pane border color. Each
360
361
  of these is a public token: leave it unset to keep the historical built-in
361
362
  color, or set it to repaint the matching chrome.
362
363
 
364
+ `provenance` is the compact usage label color for a provider row whose numbers
365
+ came from a fallback data source rather than its authoritative one (today: a
366
+ Codex row outside a healthy app-server). It is independent of `warning` and
367
+ `critical`, which stay reserved for usage thresholds, and of the ordinary AI
368
+ label color; the fallback value is orange. See `docs/usage-tracking.md` for what
369
+ the label looks like and `docs/theme-palette.md` for the preset contract.
370
+
363
371
  Supported presets are `projmux`, `high-contrast`, `blue-hour`, `carbon-violet`,
364
372
  `daylight`, `ember`, `forest`, and `rose`. `daylight` is the fully-light
365
373
  preset; the others are dark. Preset colors paint projmux chrome only (status
@@ -517,6 +525,63 @@ desktop intent for both app-server and hook-fallback authority. Existing
517
525
  runtime override exists, take precedence only while hook fallback is current;
518
526
  they are never inferred or copied into the semantic policy store.
519
527
 
528
+ ## Split Start Directory
529
+
530
+ New splits start in the owner Project root. `[ai] split_cwd_from = "pane"`
531
+ starts UI splits in the active Pane's live directory instead, while that
532
+ directory is inside the owner Project root. Settings exposes the same value as
533
+ `AI > New splits start in` (`Project root` / `Current Pane directory`); choosing
534
+ one writes only the global `[ai] split_cwd_from`.
535
+
536
+ Config paths (global and project both honored):
537
+
538
+ ```text
539
+ ~/.config/projmux/config.toml # global
540
+ <project>/.projmux/config.toml # project
541
+ ```
542
+
543
+ Schema:
544
+
545
+ ```toml
546
+ [ai]
547
+ split_cwd_from = "project" # project|pane; where a new shell or Agent split starts
548
+ ```
549
+
550
+ Resolution depends on who starts the split.
551
+
552
+ **CLI** (`create pane`, `create agent`, and the provider shortcuts) — the flag
553
+ only:
554
+
555
+ 1. `--cwd-from project|pane`
556
+ 2. otherwise `project`
557
+
558
+ The CLI opens no config file for this decision. A CLI result is determined by
559
+ its arguments, so scripts and automation keep starting in the Project root when
560
+ someone changes this setting.
561
+
562
+ **UI** (keybinding splits, the launcher, the resume picker's new row, and the
563
+ Pane menu) — the config tiers:
564
+
565
+ 1. an explicit per-call value (flag)
566
+ 2. project `[ai] split_cwd_from`, read from the owner Project root
567
+ 3. global/user `[ai] split_cwd_from`
568
+ 4. built-in default (`project`)
569
+
570
+ There is no environment override, and an unknown value skips its own tier. The
571
+ project tier is always read from the owner Project root, so where a Pane sits
572
+ never changes which config decides. The Settings row shows the UI result and the
573
+ tier that decided it (`project`, `global`, or `default`).
574
+
575
+ With `pane` selected, the active Pane is the origin of the split: the Pane a
576
+ keybinding ran in, the Pane a right-click menu was opened on, or the anchor Pane
577
+ a CLI create resolved. Its live directory is used only when it is inside the
578
+ owner Project root. `$HOME`, another registered Project tree, a directory that no
579
+ longer exists, and an unreadable directory all start in the Project root and
580
+ report one line naming the reason and that root — on stderr for a CLI create
581
+ with `--cwd-from pane`, and as one client message for a split started from the
582
+ UI. A split is never refused for this reason. `--cwd` on `create agent` still
583
+ names the Agent working directory outright and ignores this setting.
584
+
520
585
  ## AI Resume Picker
521
586
 
522
587
  The Agent resume picker lists the most recent
@@ -337,3 +337,31 @@ The selectorless E2E remains the single deterministic L20 scenario. Actual
337
337
  active-tool/human overlap, multiple ordinary requests, same-UID recovery and
338
338
  installed smoke remain separate unverified evidence. They are not run by this
339
339
  qualification-plus-one-idle transaction and are not inferred from its result.
340
+
341
+ `TestClaudeDialogueStreamReplaysObservedFrameCorpus` replays the preserved
342
+ Claude Code 2.1.263 frame shapes through the reply-only validator and fails when
343
+ a recorded verdict moves. Neither it nor L20 proves:
344
+
345
+ - compatibility with the installed provider, which only this canary observes;
346
+ - value drift, because the corpus keeps top-level key shape and fills values
347
+ with placeholders;
348
+ - acceptance of unknown frames or fields, which the closed vocabulary rejects
349
+ by design.
350
+
351
+ L20 replays every corpus item whose verdict is `drop` through the production
352
+ reply-only validator. Its Claude fixture reads the corpus at run time, emits each
353
+ dropped side frame with its key set unchanged after init and before its startup
354
+ result, and the scenario requires the emitted names to equal the corpus drop
355
+ set. Those frames are key-shape placeholders, not model output. L20 does not
356
+ emit the recorded real shapes the validator rejects today:
357
+
358
+ - `result-success-25-keys`: the result allowlist lacks `origin`;
359
+ - `system-init-allowlist-diff`: the init allowlist lacks `memory_paths` and
360
+ `terminal_slash_commands`.
361
+
362
+ Shapes without preserved evidence are gaps, not corpus items, so neither the
363
+ corpus nor L20 exercises them: `command_lifecycle`
364
+ keys and values; a non-null assistant `context_management` value; `system`
365
+ `thinking_tokens` frames; hook and user `tool_result` frames; the exact real init
366
+ key set; nested values of every frame; `rate_limit_event` envelope keys beyond
367
+ `type` and `rate_limit_info`; and earlier shape inventories, which were lost.