projmux 0.12.2 → 0.13.0

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.
package/docs/cli-guide.md CHANGED
@@ -111,13 +111,16 @@ The contract:
111
111
  --name current` succeeds — so a sentinel token would silently shadow a real
112
112
  resource.
113
113
  - **"No selector at all" means exactly that.** Any positional `<ref>`, any
114
- `--project`/`-p`, `--window`/`-w`, `--pane`, or any `--selector` label keeps the
115
- pre-existing behavior unchanged. The fallback is never blended into a
116
- partially specified selector.
117
- - **Only the singular routes.** The plural reads (`get projects|windows|panes|
118
- agents`) stay 0..N inventories over their whole scope. `delete` and `create`
119
- are unchanged: `create`'s omitted `--pane` selects a split anchor inside an
120
- already-chosen Window, which is not the same question.
114
+ `--project`/`-p`, `--window`/`-w`, `--pane`, or any `--selector` label keeps
115
+ picking the target itself. The active *target* is never blended into a
116
+ partially specified selector. The active *Project* is a separate rule and does
117
+ apply to a reference -- see [Reference scope](#reference-scope-the-active-project-namespace)
118
+ below.
119
+ - **Only the singular routes and `create`.** The plural reads (`get
120
+ projects|windows|panes|agents`) stay 0..N inventories over their whole scope,
121
+ and `delete` is unchanged. `create` has its own spelling of the same rule --
122
+ see [Create scope](#create-scope) -- because a create resolves a scope to put
123
+ something *into* rather than a target to act *on*.
121
124
  - **Inside tmux is decided by `$TMUX_PANE` plus `$TMUX`**, not by whether a tmux
122
125
  server answers. A bare `display-message` from outside a client still succeeds
123
126
  and answers for the most-recently-used session; projmux never uses that.
@@ -136,6 +139,155 @@ and `@projmux_window_uid` on its window — and derives every ancestor from
136
139
  registry `ownerRef`. The session-scoped `@projmux_project_uid` is deliberately
137
140
  not consulted.
138
141
 
142
+ ### Plural read scope: the active managed root
143
+
144
+ Inside tmux, selector-omitted `get windows|panes|agents` derives one exact
145
+ managed root from the active Window's Registry `ownerRef`:
146
+
147
+ - a Project-owned Window lists only that Project's descendants;
148
+ - a ControlSession-owned Window, including Home, lists only that
149
+ ControlSession's descendants;
150
+ - a missing Window mirror, a foreign Window uid, or a Window without one exact
151
+ existing Project or ControlSession owner is exit `2` with zero stdout. It
152
+ never falls through to the whole Registry.
153
+
154
+ The default narrows the Window universe and does not pick a target. Name and
155
+ label selectors keep their existing meaning inside that root. An applicable
156
+ explicit `uid:` occurrence is opaque Registry-global authority: like explicit
157
+ `--project`/`-p` and `--all-projects`/`-A`, it bypasses active-target observation
158
+ entirely, including from a foreign tmux Pane. The two whole-set spellings retain
159
+ their existing meaning and include ControlSession descendants. Outside tmux, an
160
+ omitted root keeps the historical whole-Registry inventory without probing a
161
+ default server.
162
+
163
+ ### Reference scope: the active Project namespace
164
+
165
+ A `metadata.name` is unique inside its owner scope, never across the registry: a
166
+ Window name is unique inside its Project, a Pane name inside its Window or
167
+ Agent, an Agent name inside its Window. So inside a managed Project a reference
168
+ is resolved inside the Project that owns the active Window, which is the same
169
+ universe the plural reads use in that context:
170
+
171
+ ```
172
+ projmux describe window zsh # the Window named zsh in *this* Project
173
+ projmux describe pane log # the Pane named log in this Project
174
+ projmux describe agent codex # the Agent named codex in this Project
175
+ projmux rename window zsh --name review
176
+ projmux rename pane log --name build
177
+ ```
178
+
179
+ The Project-only read matrix is `describe window|pane|agent`. Generic
180
+ `rename window|pane|agent` uses the same narrowing rule but accepts either the
181
+ active Project or ControlSession as the exact owner root. `describe project`
182
+ and `rename project` are not in either matrix — a Project has no enclosing
183
+ root — and `delete`, `rebind`, and `agent resume` keep their selector meaning.
184
+
185
+ The contract:
186
+
187
+ - **It narrows the search, it does not pick the target.** The Project fixes the
188
+ universe and nothing else. The active Window and the active Pane are not used
189
+ to break a tie, so two same-named resources inside the one Project stay the
190
+ ordinary bounded `matched N ..., want exactly one` ambiguity. Resolving that
191
+ is what `--window`/`--pane` and a `uid:` reference are for.
192
+ - **Explicit `--project`/`-p` wins, with zero observations.** Naming a Project
193
+ never costs a tmux round trip and never depends on the pane you are sitting
194
+ in.
195
+ - **A `uid:` reference is scoped too.** A uid that belongs to another Project is
196
+ a no-match, not a cross-Project hit. Pass `--project` to address it.
197
+ - **Outside tmux nothing changes.** The whole registry is searched and the
198
+ previous `matched N ..., want exactly one` ambiguity is unchanged. No default
199
+ tmux server is probed.
200
+ - **Inside tmux a broken owner chain refuses.** A pane carrying no
201
+ `@projmux_window_uid`, a mirrored Window uid the registry does not hold, or a
202
+ Window with no exact existing Project or ControlSession owner is exit `2`
203
+ with zero bytes on stdout and zero mutations for generic rename. It is never
204
+ a silent fallback to the whole registry.
205
+
206
+ ### Create scope
207
+
208
+ `create window|pane|agent|<provider>` is resource-backed on every spelling.
209
+ There is no mode flag and no second parser: the same argv means the same thing
210
+ whether or not `--project` is present, and `-w`, `--create-window`, `--pane`,
211
+ `--selector`, `--placement`, `--name`, `--label`, `--cwd`, `--add-dir`, `-o`,
212
+ and the `--` payload all reach the same parser either way.
213
+
214
+ The scope resolves in two branches:
215
+
216
+ - **Explicit `--project`/`-p` wins**, inside tmux and outside it. The active
217
+ tmux target is not consulted at all.
218
+ - **With no `--project`, the Project comes from the active managed runtime**:
219
+ the `@projmux_window_uid` mirrored on the pane you are in, and that Window's
220
+ registry `ownerRef`. This is the same seam the empty-selector reads use.
221
+
222
+ The Window and the anchor Pane follow the *whole* scope rather than the Project
223
+ flag alone:
224
+
225
+ ```
226
+ projmux create codex # active Project, active Window, split from the active Pane
227
+ projmux create codex -w hi --create-window # active Project, new Window "hi"
228
+ projmux create codex -p beta -w main # everything explicit
229
+ projmux create pane -p alpha # every Window of alpha; a deliberate fan-out
230
+ ```
231
+
232
+ One explicit scope occurrence (`--project`, `--window`, `--pane`, or
233
+ `--selector`) makes the whole scope explicit, so naming a Window never picks up
234
+ an anchor from somewhere you did not address. With a scope but no `--pane`, the
235
+ split anchor is the target Window's role-agnostic `spec.anchorPaneRef`. A
236
+ shell-required offline operation may plan a lazy direct
237
+ `spec.defaultShellPaneRef` without replacing an Agent anchor. A missing or stale
238
+ anchor is exit `2` rather than a silent alternate-Pane repair.
239
+
240
+ Refusals are exit `2` with zero Registry writes and zero tmux mutations, and
241
+ they name `--project` as the fix:
242
+
243
+ - outside tmux with no `--project` — no default server is probed;
244
+ - inside Home, a control session, an unattributed pane, or a foreign pane —
245
+ none of those carry a managed identity, and projmux never invents a Project
246
+ from `$HOME`, a session name, or a cwd;
247
+ - a mirrored uid the Registry does not hold, or a Window whose owning Project is
248
+ gone — a `recoverable` runtime is reported, never adopted.
249
+
250
+ Every create is **detached**: no create moves the client. Use `focus pane` or
251
+ `-o pane-id` when you want to end up in the new pane. Creates run against the
252
+ inherited exact socket inside tmux; outside tmux an explicit `--project` is
253
+ required before anything live is touched.
254
+
255
+ #### Splits started from a popup
256
+
257
+ The split UI's own pickers (`M-7`, `M-4`/`C-r`, the pane context menu, and the
258
+ default split key when the saved mode is `selective` or `resume`) run inside a
259
+ `display-popup`. tmux exports `$TMUX` to a popup job and deliberately exports no
260
+ `$TMUX_PANE`, because a popup is not a pane — so the picker has no inherited
261
+ target of its own while still knowing, from the keypress that opened it, which
262
+ pane the operator was in. That pane travels on the create intent as an explicit
263
+ anchor and resolves Project, Window and split anchor through the same identity
264
+ mirror a pane-hosted invocation reads.
265
+
266
+ The anchor is something the split UI hands to `create`, never something `create`
267
+ reads from the environment. `$TMUX_SPLIT_TARGET_PANE` is not a scope override:
268
+ typing `projmux create pane --placement right` inside a popup is still an
269
+ invocation with no target and still refuses with the `--project` usage error
270
+ above, and no read, rename, or delete verb consults it.
271
+
272
+ #### Managed pane context menu
273
+
274
+ The generated `MouseDown3Pane` menu treats its Horizontal Split, Vertical
275
+ Split, and Kill entries as Projmux resource actions. Both splits pass the exact
276
+ clicked pane through the popup-origin anchor above and reach the same canonical
277
+ `create pane` materializer as the CLI, so the new pane receives a Registry uid.
278
+ Kill resolves that anchor's mirrored uid and reaches canonical `delete pane`,
279
+ including its printed delete result. The menu never falls back to a raw tmux
280
+ mutation when either route refuses. The reason is displayed on the exact client
281
+ that opened the menu instead of being lost as a `run-shell` exit code.
282
+
283
+ tmux Respawn has no equivalent in the current resource model: it preserves the
284
+ same pane handle, layout, Registry uid, and original command, while canonical
285
+ delete plus create removes that identity and creates another one (and may end
286
+ the Window or Project session when it deletes the last pane). The generated
287
+ menu therefore omits Respawn entirely and does not expose a refusal handler or
288
+ invent a replace operation. User-authored tmux bindings remain outside this
289
+ managed-menu contract.
290
+
139
291
  ### Rename and rebind live convergence
140
292
 
141
293
  `rename project|window|pane` commits the selected Registry `metadata.name` and
@@ -166,6 +318,26 @@ duplicate UID claims remain fail-closed.
166
318
 
167
319
  ### Agent topic, interaction, activation, and workspace
168
320
 
321
+ `agent turn start`, `agent turn steer`, `agent turn interrupt`, and
322
+ `agent approval review` use the live Codex app-server connection only when the
323
+ selected Agent, its owned Pane, activation generation, thread, current turn,
324
+ and connection epoch still match exactly. `start` sends only the exact thread
325
+ id and one text input; `steer` supplies the current expected turn id; and
326
+ `interrupt` supplies that exact turn id. These commands never install sticky
327
+ model, effort, cwd, sandbox, permission, or collaboration overrides.
328
+
329
+ Approval review shows only the safe one-shot intersection supplied by the
330
+ exact pending request. Command, file, and network requests are limited to
331
+ `accept`, `decline`, and `cancel`; permission grants echo the received supported
332
+ profile with `scope=turn` and `strictAutoReview=null`. Session grants, policy or
333
+ network amendments, unstable root grants, legacy approval mutations, and
334
+ automatic approval are unavailable. The request envelope and raw JSON-RPC id
335
+ remain only in connection memory; reconnect, resolution, ambiguity, or any
336
+ identity mismatch produces no provider write. Approval queue rows advertise
337
+ `Review pending approval` only while that responder exists, otherwise they
338
+ advertise the exact-Agent `Open Codex` focus fallback; resolution removes the
339
+ row. Neither route stores prompt, command, path, permission, or request content.
340
+
169
341
  `agent topic get|set|clear` and `agent status get|set` resolve exactly one
170
342
  Agent, either from an explicit Agent reference or from the Agent-owned active
171
343
  managed Pane. Topic is a non-identifying Registry annotation. Interaction is a
@@ -193,6 +365,40 @@ and shell Pane manual attention share the existing priority reducer, while
193
365
  `dot`/`emoji`/`off`, glyphs, colors, and the Window aggregate are never stored
194
366
  in resource metadata.
195
367
 
368
+ A clean managed process exit is topology authority only through the exact
369
+ generated `pane-exited` hook. The supervisor must have durably journaled a
370
+ same-generation `normal` receipt, the hook must name the exact `%N` Pane and
371
+ the owner Window must carry its exact last-positive `$N/@N` binding on the same
372
+ socket, and fresh preflight plus locked observations must still resolve the
373
+ same Registry owner chain. A non-last Pane is removed while its directly owning
374
+ Agent is retained Offline with its conversation identity. For a last Pane, that evidence is retained until a
375
+ matching `window-unlinked` hook removes the Window; a final Project Window also
376
+ removes its Window descendants while retaining the exact Project uid, root,
377
+ reservation, pins, snapshots, and external assets as a valid zero-Window
378
+ Project. Managed runtime Stop is different: it stops only the exact runtime and
379
+ keeps the complete desired Project/Window/Pane graph closed for a same-UID
380
+ Continue. Shell and Claude/Codex clean exit have the same result; `/exit`, pane
381
+ content, prompt, history, and transcript are never parsed. `abnormal`,
382
+ `killed`, `unknown`, stale/resumed bindings, empty or unavailable inventory,
383
+ permission failure, and foreign-host/window observations keep their diagnostic
384
+ rows and produce no automatic delete plan.
385
+
386
+ For `pane-exited`, tmux supplies `%N` as `#{hook_pane}`. Its current-context
387
+ session/window formats may already name a surviving client Window, so the owner
388
+ pair comes from the Window's last live Registry observation. The separate
389
+ `window-unlinked` hook supplies exact `#{hook_session}` and `#{hook_window}`;
390
+ only a matching causal pair authorizes last-Pane Window deletion.
391
+
392
+ An offline historical Window with no stored causal last-Pane receipt is not
393
+ absence-only migration authority and is never auto-deleted. Recover it with the
394
+ canonical exact route, `projmux delete window uid:<window-uid> --socket <name>
395
+ --yes` (or the corresponding `--socket-path`/inherited absolute `$TMUX` route).
396
+ Deleting a Project's last exact Window retains the Project with zero Windows;
397
+ within the runtime/startup lifecycle table, explicit `delete project --yes` is
398
+ the unregister operation. Runtime absence, managed Stop, ordinary Window close,
399
+ and Fresh never invoke the separately scoped filesystem-missing `prune project`
400
+ administrative policy.
401
+
196
402
  Resource-backed Agent create accepts provider-neutral `--cwd <absolute>` and
197
403
  repeatable `--add-dir <absolute>`. Explicit paths must exist, resolve without a
198
404
  symlink escape, and remain inside a registered Project tree; only Codex and
@@ -210,8 +416,15 @@ owner Project root from `get`/`describe`, and a successful resume persists that
210
416
  normalized effective workspace without changing Window Project ownership.
211
417
 
212
418
  When `create agent -- <initial-prompt>` is used, normal resource creation and
213
- provider activation are distinct. Projmux waits for bounded hook/lifecycle
214
- metadata only; it never captures pane content or stores the prompt. If
419
+ provider activation are distinct. Projmux first waits up to five seconds for an
420
+ exact provider `SessionStart`; that readiness evidence leaves activation
421
+ `pending` and opens an independent five-second initial-task acknowledgement
422
+ window. A `UserPromptSubmit` acknowledgement may also arrive directly before
423
+ the readiness observer sees `SessionStart`. The two stages are independently
424
+ bounded, so provider startup plus an acknowledgement later than two seconds may
425
+ take more than five but never more than ten seconds. Neither stage captures pane
426
+ content or stores the prompt. Acknowledgement returns success and the requested
427
+ exact `%N` output. If
215
428
  activation cannot be confirmed, the command exits nonzero while naming the
216
429
  exact Agent UID and Pane plus safe provider retry and `delete agent ... --yes`
217
430
  cleanup options. The live resources remain explicit and retryable rather than
@@ -219,7 +432,13 @@ being reported as an ordinary success.
219
432
 
220
433
  Activation metadata is bounded to provider-hook provenance and fixed
221
434
  acknowledged/timed-out/failed diagnostics. Provider error strings and initial
222
- prompt text are never stored. Resource-backed Agent create and resume do not
435
+ prompt text are never stored. `pending` can become `unconfirmed` and a later
436
+ exact hook can refine either state to `acknowledged`; acknowledged never moves
437
+ backward. Late refinement must quote the same Agent, Pane uid, activation
438
+ generation, and recorded live `%N`, so a hook from a replaced materialization
439
+ cannot acknowledge its replacement. Codex and Claude initial payloads use the
440
+ same acknowledgement and result contract; provider-specific prompt content is
441
+ not evidence. Resource-backed Agent create and resume do not
223
442
  start the legacy title/content watcher; that watcher remains only for legacy
224
443
  non-resource panes and exits before reading title or capture content if resource
225
444
  identity appears.
@@ -315,14 +534,23 @@ client only after it converges; a refusal, a failed preflight, or a rolled-back
315
534
  partial leaves the client where it was and reports the exact stage. The
316
535
  activation is pinned to the session the open targets, so a Project whose
317
536
  Registry projects a different session name is refused instead of populating a
318
- session the open never reaches. Choosing `Latest snapshot` or `Named snapshot`
319
- instead stays entirely on the Session State snapshot engine. Reading whether a
320
- Project declares topology is a zero-write snapshot read, so opening a directory
321
- that was never registered still creates no Registry state.
322
-
323
- Materialization never starts or resumes an Agent, creates an Agent-owned Pane,
324
- or executes `Pane.spec.command`; that field remains a one-time name seed. A
325
- new Window binds only its own tmux-created primary Pane. On an existing Window,
537
+ session the open never reaches. The closed-Project startup screen has exactly
538
+ two neutral actions. `Continue project` materializes current Registry desired
539
+ state with the same Project UID. A retained graph keeps descendant UIDs; a
540
+ zero-Window Project atomically receives a new canonical Window/shell UID chain.
541
+ A deleted Project may use only the exact usable snapshot compatibility path;
542
+ an unavailable Continue is an explicit zero-write refusal with no Fresh
543
+ fallback. `Open fresh` is one step with no danger styling, confirmation, or
544
+ delete counts: it atomically replaces the same-root graph with a new Project
545
+ UID and new canonical Window/shell UIDs, then hands off only after ordinary
546
+ materialization. A repeat replaces identity again. The root, git/worktrees,
547
+ trust decision, unrelated roots, and snapshot bytes remain unchanged.
548
+
549
+ Materialization launches or resumes declared Agents through the canonical
550
+ provider/trust path and creates their managed Agent-owned Panes. An individual
551
+ Agent refusal preserves the committed desired Registry and emits an item notice
552
+ for retry. It never directly executes `Pane.spec.command`; that field remains a
553
+ one-time name seed. A new Window binds only its own tmux-created primary Pane. On an existing Window,
326
554
  every pre-existing uid-less Pane is refused rather than adopted; foreign,
327
555
  duplicate, wrong-owner, or otherwise ambiguous UID state is refused before the
328
556
  first create. Layout uses the existing deterministic right-axis equalizer and does
@@ -334,6 +562,193 @@ Registry-only: its complete Agent/Pane cascade is shown under `--dry-run`, no
334
562
  tmux object is killed, and unrelated live objects and sockets are untouched. A
335
563
  unique live mirror keeps the exact-kill path, while duplicate, foreign,
336
564
  stale-owner, inventory-failure, and revalidation-race states are refused.
565
+ The selected Window's Registry `ownerRef` is authoritative in that preflight:
566
+ a Project uses its `status.session` plus the optional matching Project uid
567
+ mirror, while a ControlSession uses its exact `spec.session` and requires that
568
+ no Project uid mirror contaminate the control session. Window, Pane, and Agent
569
+ deletes preserve that `(root kind, root uid)` chain in the signed live plan and
570
+ report a final-window cascade with its actual root kind.
571
+
572
+ Pane and Agent Registry-only deletion is deliberately narrower. It accepts
573
+ only an explicit exact `uid:` selector: a Pane must carry durable
574
+ `MissingRuntime=True/RuntimeUnbound` evidence, and an Agent must be `Offline`
575
+ with no `paneRef` (with every retained descendant Pane also marked
576
+ `MissingRuntime`). The exact routed server must answer with a non-empty socket
577
+ identity and a non-empty Pane inventory that proves the target has zero mirrors.
578
+ A missing server, empty or failed inventory, unavailable or permission-denied
579
+ transport, implicit/name/scope/`--all` selection, and duplicate or foreign
580
+ mirrors are not absence authority and make zero writes. Dry-run and apply sign
581
+ the same socket, owner/root chain, lifecycle evidence, Pane activation
582
+ generation, and Agent binding; locked revalidation refuses zero-to-live,
583
+ live-to-zero, owner, generation, duplicate, or foreign changes. A successful
584
+ Registry-only result reports that no tmux Pane was killed, preserves the owning
585
+ Window/root/socket and all siblings, and repeating the exact apply returns the
586
+ ordinary no-match result.
587
+
588
+ `delete window|pane|agent` names the server its live half addresses the same
589
+ way `reconcile resources` does: `--socket <name>`, `--socket-path <absolute>`,
590
+ or the inherited absolute `$TMUX`. Outside tmux with neither flag it refuses
591
+ rather than reaching for the app's own socket, so a delete issued against an
592
+ isolated server can never inventory one host and kill objects on another.
593
+
594
+ Before it kills anything, a delete commits an intentional termination receipt
595
+ against every Pane whose process it is about to end, in its own Registry
596
+ transaction. If that write fails, nothing live is touched; if the delete then
597
+ refuses for any other reason, the receipt is withdrawn again. The receipt is
598
+ what tells a later reader that a process disappeared because someone asked for
599
+ it, rather than because it crashed.
600
+
601
+ ## get runtime
602
+
603
+ ```text
604
+ projmux get runtime sessions|windows|panes [--socket <name> | --socket-path <absolute>] [-o json|none]
605
+ ```
606
+
607
+ `get runtime` is the read-only escape hatch onto one exact tmux server. The
608
+ resource reads (`get projects|windows|panes|agents`) enumerate the Registry;
609
+ this one enumerates the machine, including everything projmux does not own, and
610
+ it accepts no selector because most of what it reports has no name to resolve.
611
+ Its kinds are tmux object kinds, not resource kinds, and they have no singular
612
+ spelling for the same reason.
613
+
614
+ Every row carries the attribution the resolved resource graph decided from exact
615
+ evidence -- `managed`, `recoverable`, `control`, `ephemeral`, `unattributed`,
616
+ `foreign`, `conflict` -- the reason for it, the stable tmux id, the fully
617
+ qualified coordinate (`<session>`, `<session>:@N`, `<session>:@N.%N`), and, for a
618
+ managed object, the Registry resource it is bound to. A refused object is named
619
+ and explained and is never handed a resource identity.
620
+
621
+ Socket selection is the same fail-closed rule `reconcile resources` uses, with
622
+ one difference at the end:
623
+
624
+ - `--socket <name>` means exactly `tmux -L <name>`.
625
+ - `--socket-path <absolute>` means exactly `tmux -S <absolute>`.
626
+ - The two flags are mutually exclusive.
627
+ - With neither flag, an invocation inside tmux inherits only the absolute socket
628
+ path from `$TMUX` and uses `-S`.
629
+ - With neither flag outside tmux the read still succeeds. It returns the
630
+ unavailable projection: no items, every scope reported unobservable with a
631
+ reason, and zero tmux calls. There is no default-socket guess, and a sibling
632
+ socket is never read.
633
+
634
+ The default projection is a table preceded by a header line naming the host mode
635
+ and the exact transport, plus one line per scope that could not be observed. The
636
+ header is always printed, even when the table is empty: "no sessions" is only
637
+ trustworthy next to which server was asked and whether the answer could be taken
638
+ at all. `-o json` emits the same data as a stable `Runtime{Session,Window,Pane}List`
639
+ envelope; `-o none` prints nothing. The Registry projections (`uid`, `name`,
640
+ `ref`, `metadata`) are deliberately not offered, because most of what this route
641
+ returns has none of them.
642
+
643
+ The read writes nothing: the Registry is opened without being created, the
644
+ observation issues one option probe and three list queries whatever the size of
645
+ the server, and no write verb is ever sent.
646
+
647
+ ## runtime diagnostics
648
+
649
+ ```text
650
+ projmux runtime diagnostics [--socket <name> | --socket-path <absolute>] [--ui=popup|sidebar]
651
+ ```
652
+
653
+ The interactive half of the same read. It lists every tmux object on the exact
654
+ server in containment order with an attribution tally, and it is deliberately
655
+ separate from `projmux runtime sessions`: that picker lists recent sessions so
656
+ you can open one, this one lists everything so you can understand it.
657
+
658
+ Selecting a row opens its action menu, which offers only routes that already
659
+ exist:
660
+
661
+ - **Focus** hands `projmux focus` the row's exact coordinate and the server's own
662
+ `#{socket_path}`. It moves a client and never materializes anything.
663
+ - **Attach** forwards to `projmux attach project uid:<uid>`, and is offered only
664
+ for a session bound to a Registry Project while you are outside tmux.
665
+ - **Open Resource Inspector** opens `projmux resources` unchanged.
666
+
667
+ An action that does not apply is listed with the reason instead of being hidden:
668
+ "no Registry Project claims this session; diagnostics never adopts one" is the
669
+ diagnostic. There is no adopt, import, rename, or kill action, and opening the
670
+ surface writes nothing.
671
+
672
+ ## reconcile registry
673
+
674
+ ```text
675
+ projmux reconcile registry [--dry-run] [--source <name|absolute-path>] [--expect-source-checksum <sha256:hex>] [--expect-current-checksum <sha256:hex>] [--socket <name> | --socket-path <absolute>] [-o json]
676
+ ```
677
+
678
+ `reconcile registry` is the recovery boundary for the Registry itself. It is a
679
+ sibling of `reconcile resources`, not a stronger version of it: `reconcile
680
+ resources` converges a Registry that loads, and this route runs when the
681
+ Registry is the thing that is wrong.
682
+
683
+ Planning writes nothing. With no `--source` — and with `--dry-run` at any time —
684
+ the command reads the current `registry.json`, the `registry.initialized`
685
+ marker, and the bounded copies under `recovery/`, then reports:
686
+
687
+ - the current state as `valid`, `first-use`, `missing`, `empty`, `malformed`,
688
+ `schema-too-new`, `invalid`, or `unreadable`, with a `sha256:` digest of the
689
+ exact bytes;
690
+ - every candidate newest first, each marked `eligible` or `rejected` with the
691
+ reason, its digest, size, mtime, schema version, and the
692
+ projects/windows/panes/agents/reservations it holds;
693
+ - the exact guarded command that would restore the candidate it suggests.
694
+
695
+ No lock is taken, no permission is repaired, and `<state>/projmux/metadata/` is
696
+ not created — a preview is safe against a first-use state directory and against
697
+ one nobody should be writing to yet.
698
+
699
+ Restoring requires `--source`. There is no "restore the newest" mode: which copy
700
+ is the truth is a judgment about which mutations were wanted. A source is an
701
+ exact copy name, a unique fragment of one, or an absolute path to a copy carried
702
+ from elsewhere; a fragment matching several copies is refused rather than ranked.
703
+ An explicit path gets exactly the same verification as a bounded copy.
704
+
705
+ Verification is fail closed. Malformed JSON, an empty file, an envelope newer
706
+ than this build, and a graph with a duplicate uid, a dangling `ownerRef`, or a
707
+ broken name reservation are all refused with the current Registry byte-identical.
708
+ The verified bytes are then published **verbatim**, so uids, owner relations, and
709
+ name reservations are preserved exactly rather than re-encoded, and a
710
+ known-older-but-valid envelope stays readable through the normal safe read and
711
+ migrates on the next semantic write.
712
+
713
+ The bytes being replaced are kept first, at
714
+ `recovery/replaced-<stamp>-<seq>.json`. Unlike the write-side copies this keeps
715
+ content that does not verify: a damaged Registry is the only remaining evidence
716
+ if the restore turns out to be the wrong call, and the preserved copy is offered
717
+ back as a candidate. Replaced copies are their own bounded family, so a restore
718
+ never consumes the automatic write history.
719
+
720
+ Race guards are the operator's tie to the plan they read:
721
+
722
+ - `--expect-source-checksum <sha256:hex>` refuses unless the source still hashes
723
+ to that digest.
724
+ - `--expect-current-checksum <sha256:hex>` refuses unless the current Registry
725
+ still does.
726
+ - Both are what the printed `next:` command already carries, so copy-pasting the
727
+ preview's suggestion is guarded by construction.
728
+
729
+ Underneath, the source is re-read and re-verified under the store lock, the
730
+ staged copy is re-validated, and both inputs are re-hashed immediately before the
731
+ single atomic rename. Anything that moved refuses with nothing published, no
732
+ preserved copy, and no staged file left behind, and says to re-run the preview. A
733
+ repeat restore is a byte no-op: no rename, no preserved copy, no marker write.
734
+ Restoring into a state directory with no marker publishes one, so a later loss on
735
+ that machine reads as state loss rather than as a fresh first use.
736
+
737
+ When recovery is needed and no verified copy exists, the report adds a mirror
738
+ diagnostic — and it is **only** a diagnostic. It reports the Projmux identity the
739
+ one exact tmux server still carries (Project/Window/Pane uids, mirrored names,
740
+ the Project root, and containment resolved from stable tmux ids) beside a fixed
741
+ statement of what no mirror can return: offline resources, every Agent (no tmux
742
+ option carries an Agent uid), an Agent-owned Pane's `ownerRef`, the name
743
+ reservation table, `spec.anchorPaneRef`, `spec.defaultShellPaneRef`, and labels/annotations/timestamps/
744
+ status. Panes carrying a provider option are counted as proof that Agents existed
745
+ whose uids are nowhere on the server. Nothing is imported and no Registry is
746
+ generated from fragments. Socket selection follows the same `--socket` /
747
+ `--socket-path` / inherited-`$TMUX` rule as `reconcile resources`, except that
748
+ having no exact target is reported as a reason rather than being a usage error:
749
+ a restore is a filesystem operation, so recovery must work on a machine with no
750
+ tmux server. The diagnostic is skipped entirely when a verified copy exists or
751
+ the Registry is healthy.
337
752
 
338
753
  ## Internal plumbing (`projmux internal ...`)
339
754
 
@@ -865,8 +1280,8 @@ supplied window.
865
1280
  ## Agent creation and hook ingress
866
1281
 
867
1282
  ```
868
- projmux create agent --provider <claude|codex|antigravity> [--placement right|down] ...
869
- projmux create pane [--placement right|down] ...
1283
+ projmux create agent --provider <claude|codex|antigravity> [--project <ref>] [--window <ref>]... [--create-window] [--placement right|down] ...
1284
+ projmux create pane [--project <ref>] [--window <ref>]... [--create-window] [--placement right|down] ...
870
1285
  projmux config edit [--get|--set <mode>]
871
1286
  projmux agent status set <thinking|waiting|idle> [pane]
872
1287
  projmux agent topic ...
@@ -897,34 +1312,60 @@ yellow respectively. That palette is independent from notify queue
897
1312
  can still render a non-red action-required status badge.
898
1313
 
899
1314
  `create agent --provider ...` selects a provider without changing the saved
900
- default. Concrete provider invocations create a new managed
901
- agent pane every time; existing managed AI panes in the same project/session are
902
- not selected or reused.
903
- The provider picker and plain Pane creation remain available through the
904
- canonical create workflow. Arguments after `--` are extra arguments appended to
1315
+ default. Concrete provider invocations create a new Agent and a new managed
1316
+ Pane every time; existing managed AI panes in the same project/session are
1317
+ not selected or reused, and rebinding an existing conversation is `agent
1318
+ resume`, a different verb. The scope of the new resources follows
1319
+ [Create scope](#create-scope). The provider picker remains available through
1320
+ `internal agent-pane picker`. Arguments after `--` are extra arguments appended to
905
1321
  the resolved `claude`, `codex`, or `agy` executable inside the managed wrapper;
906
- projmux still sets the context directory, tmux title, AI pane metadata, title
907
- watcher, and split layout.
908
-
909
- Automation callers can add `--print-pane-id` to an explicit direct
910
- `--agent claude|codex|antigravity|shell` launch. On success, stdout contains
911
- exactly the new `%N` pane id followed by one newline. The value comes directly
912
- from tmux's existing
913
- `split-window -P -F '#{pane_id}'` result. If tmux returns no valid pane id, the
914
- command fails non-zero with tmux-specific guidance and writes no
915
- success value. Without `--print-pane-id`, successful split invocations keep the
916
- existing empty-stdout behavior.
917
-
918
- `--print-pane-id` is not available for the saved default mode or for
919
- `--agent selective|resume`, because those paths may open a picker and launch
920
- only after a later user selection. Those combinations fail before opening a
921
- picker or creating a pane. Arguments after `--` keep their existing argv-tail
922
- meaning when the flag is used with a concrete AI agent.
1322
+ projmux still sets the context directory, tmux title, AI pane metadata, and
1323
+ split layout.
1324
+
1325
+ Automation callers get the new pane's handle from `-o pane-id` on the canonical
1326
+ create routes: `projmux create agent --provider <p> --placement right -o pane-id`
1327
+ and `projmux create pane --placement right -o pane-id` each print exactly the
1328
+ managed Pane's `%N` followed by one newline. See
1329
+ [AI Agent Shortcuts](ai-agent-shortcuts.md) for the shortcut spellings.
1330
+
1331
+ Every Projmux split surface produces the same canonical create intent. The
1332
+ default `ai-split-right/down` binding reads the saved split mode and turns it
1333
+ into one intent -- a provider Agent, a shell Pane, or one of the two pickers --
1334
+ and the `Alt-7` picker and the resume picker do the same with what the operator
1335
+ selected. Only the create route's materializer runs tmux's `split-window`, so a
1336
+ pane opened from the UI is a Registry resource on the same terms as one asked for
1337
+ by name, and a failed launch leaves zero Registry and zero tmux mutations. A raw
1338
+ unmanaged split exists only where you make one yourself.
1339
+
923
1340
  The resume picker lists the newest deduplicated Claude, Codex, and Antigravity
924
1341
  resume sessions for the current project, with `[+ New Session]` pinned first.
925
1342
  If there are no resume sessions it goes straight to the existing selective
926
- picker. Selecting a row directly starts `claude --resume <id>`,
927
- `codex resume <id>`, or `agy --conversation <uuid>`.
1343
+ picker. Selecting a row creates a managed Agent whose pane joins that
1344
+ conversation -- `claude --resume <id>`, `codex resume <id>`, or
1345
+ `agy --conversation <uuid>`. Rebinding an Agent the Registry already has is
1346
+ `projmux agent resume`, a different verb that never falls back to a fresh
1347
+ conversation.
1348
+
1349
+ Codex rows come from one source per picker invocation. A healthy app-server is
1350
+ primary: `thread/list` is paged with opaque cursors, non-archived and explicit
1351
+ `cli`/`vscode`/`appServer` source filters, provider recency ordering, and exact
1352
+ cwd/depth filtering. Rows preserve the exact thread id plus provider name,
1353
+ branch, and runtime status; an unnamed thread uses only its short id, never its
1354
+ prompt preview. The row suffix displays native or rollout source, confidence,
1355
+ status, and any closed fallback reason. Unsupported, unavailable, protocol, or
1356
+ malformed-pagination results discard the native partial result and run the
1357
+ existing rollout scan once. Selecting a native row resumes that same thread id
1358
+ through the native lane; selecting a fallback row keeps the current CLI lane.
1359
+ Claude and Antigravity discovery and launch semantics are unchanged.
1360
+
1361
+ `projmux agent review [<agent-ref>]` starts a native Codex review for
1362
+ uncommitted changes by default. Use exactly one of `--base <branch>`, `--commit
1363
+ <sha>`, or `--instructions <text>` to choose another review target. The action
1364
+ is available only when the selected Running Codex Agent still has an exact live
1365
+ Pane/thread binding and the current app-server supports `review/start`; every
1366
+ other case reports review as unavailable without changing the Agent. This route
1367
+ projects only the initial response into interaction status. It does not claim
1368
+ the later notification-driven completion lifecycle.
928
1369
 
929
1370
  Live Antigravity hook/session-state resume metadata remains a separate,
930
1371
  high-confidence lane; it is not enumerated from disk by the picker. Within the
@@ -944,10 +1385,8 @@ Settings > AI Settings > Enabled agents controls Claude/Codex/Antigravity launch
944
1385
  visibility. Disabled agents are hidden from the selective picker and from the
945
1386
  default-mode picker. A saved default that later becomes disabled fails clearly
946
1387
  instead of falling back to another agent. Direct
947
- `--agent claude|codex|antigravity` launches also fail when disabled, including
948
- shortcuts that call the same command. For a deliberate one-shot direct CLI
949
- launch, pass `--force-agent`; picker and saved default paths do not use this
950
- override. If all AI agents are disabled, the selective picker still offers the
1388
+ Canonical `create agent --provider <p>` launches and the provider shortcuts also
1389
+ fail when disabled. If all AI agents are disabled, the selective picker still offers the
951
1390
  plain `shell` split and shows guidance to re-enable Claude/Codex/Antigravity.
952
1391
  For user-level skill, slash-command, editor, or launcher registrations that
953
1392
  call this contract, see [AI Agent Shortcuts](ai-agent-shortcuts.md).
@@ -1403,7 +1842,8 @@ returns a usage error.
1403
1842
 
1404
1843
  The live tmux inventory is under `runtime`: `runtime sessions`, `runtime
1405
1844
  attach`, `runtime stop`, `runtime tag`, and `runtime prune`. Project pins use
1406
- `pin project list|add|remove|toggle|clear`. Resource retention uses `prune
1845
+ `pin project list|add|remove|toggle|clear|migrate`; `list` takes `--kind
1846
+ project|candidate` and `migrate` takes `--dry-run`. Resource retention uses `prune
1407
1847
  project|snapshot`, while explicit snapshot deletion uses `delete snapshot`.
1408
1848
 
1409
1849
  Popup-marker, preview, status, and tmux configuration plumbing is hidden under
@@ -1417,14 +1857,16 @@ human configuration work should prefer `config render` and `config apply`.
1417
1857
  generated config. The generated app config uses absolute `$SHELL` as the
1418
1858
  tmux default shell when set, otherwise `/bin/sh`. `shell` starts or attaches
1419
1859
  the app session directly after resolving the target app session name and
1420
- startup directory. Alt-1 sidebar project open defaults to `Project topology`,
1421
- which materializes the Project's Registry Windows and Window-owned shell Panes
1422
- before the client moves; the Session State `Sidebar startup picker` opt-in
1423
- shows `Latest snapshot`, `Named snapshot`, and `Project topology` before
1424
- starting a closed project session. `Latest snapshot` is auto-saved; named
1425
- snapshots are fixed until the user saves or replaces them. A directory with no
1426
- Registry Project, and a Project with no Registry Window, still start as a
1427
- single default session.
1860
+ startup directory. Alt-1 sidebar project open defaults to `Continue project`,
1861
+ which materializes the Project's Registry Windows, shell Panes, and Agents
1862
+ before the client moves. When the startup picker is enabled it contains exactly
1863
+ `Continue project` and `Open fresh`; Esc returns to Projects. `Continue
1864
+ project` restores a deleted Project only from its usable exact snapshot and
1865
+ otherwise refuses with zero Registry writes. `Open fresh` is a neutral,
1866
+ confirmation-free one-step action that atomically replaces the Project with
1867
+ a new Project/Window/shell UID chain and one same-root claimant. Repeating it
1868
+ allocates another new identity. Neither action modifies snapshot
1869
+ bytes, the project directory, git/worktrees, unrelated roots, or trust state.
1428
1870
  - `quit` — open an action picker with `Quit projmux` and `Cancel`. Selecting
1429
1871
  `Quit projmux` terminates only a `tmux -L projmux` runtime whose global
1430
1872
  `@projmux_app` option is set by the generated app config. Missing servers,