projmux 0.12.1 → 0.12.2

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.
@@ -38,10 +38,13 @@
38
38
  - `make test`: CLI information architecture v2 Phase 1 resource metadata foundation: `internal/core/metadata` unit and table tests cover the closed `Project`/`Window`/`Pane`/`Agent` kind set under `apiVersion: projmux.io/v1alpha1`, the one-time name-base seed priorities for every kind (Agent topics and raw pane titles are excluded everywhere), name-key validation, the persisted `nameReservations` suffix allocator proving the lowest free suffix is scan-order independent and that an explicit collision fails with `ErrNameConflict` and zero reservations written, offline Project bootstrap topology with a resolvable `spec.primaryPaneRef`, uid immutability across runtime creation, teardown, and rebind, zero heuristic uid merges (same-basename roots stay distinct; only an exact saved root reuses its uid), rebind and root-missing/root-returning `MissingRoot` condition lifecycle with preserved first-observed timestamps and name reservations, reverse-order operation rollback restoring a byte-identical registry, the closed Agent phase set with its exit classification and resumable Offline/Failed transitions, `schemaVersion` classification that fails closed on any newer envelope and on an unversioned document (production registers no migration step, since v1 is the first shipped envelope), a `testdata/registry-v0-source.json` → `testdata/registry-v1-migrated.golden` migration pair driven by a test-injected older step with reservation backfill that never renumbers existing names, full registry structural validation, and Session State round-tripping through additive `omitempty` `metadata` blocks at the unchanged snapshot `version: 1` including deterministic positional reconciliation of pre-metadata snapshots. `internal/integrations/metadata` covers the locked `<state>/projmux/metadata/registry.json` store: a newer `schemaVersion`, malformed JSON, and a content-bearing document with no `schemaVersion` are all refused byte-for-byte with no write, no temp file, no backup, and no quarantine on `Load`/`Update`/`Migrate`, while an absent, empty, or whitespace-only file still yields a fresh empty registry; migration atomicity is proven against a test-registered older step by injected failures after the backup, after the staged temp write, and just before the atomic replace, each leaving the original bytes and no leaked temp; a successful migration keeps a verifiable pre-migration backup and is idempotent; a failing operation writes nothing; a bootstrap registry golden freezes the camelCase file format and 0600 mode; concurrent registrations serialize through the lock with unique allocated names; and an end-to-end test round-trips an offline snapshot through both the registry store and the session-state store, reconciling every Window and Pane back by uid. The tmux mirror tests pin the exact session/window/pane option writes, the new window-scoped options, `automatic-rename off` on managed windows, `rename pane` touching only `@projmux_pane_label` and never `pane_title`, uid ↔ target resolution in both directions, and write-free legacy observation. `internal/app` and `cmd/projmux` prove the metadata usage-error seam: explicit name and rebind-root collisions reach exit code 2 through the existing `UsageError` path with zero mutations, while a newer registry schema stays a runtime error at exit code 1.
39
39
  - `make test`: CLI information architecture v2 Phase 2 selector and read-only resolution: `internal/core/selector` unit and table tests cover the pure selector grammar and the read-only resolution engine over a structurally validated registry fixture that carries duplicate `displayName`s, an offline Project, a `MissingRoot` Project, and Window/Pane names repeated across owner scopes. An aggregate negative table proves the excluded forms resolve **0 occurrences** — an implicit comma split of two real names, a duplicate `displayName`, a real `spec.root`/`spec.cwd` path, a tmux `%N`/`@N`/`$N` handle, and a bare uid without the `uid:` prefix — while `displayName` is still reported as ambiguity context and the `uid:` form does resolve. A recorded per-stage trace pins the fixed name/uid-union -> AND label-filter -> uid-dedupe order with a 3/2/1 shape no other ordering can produce; repeated singular `--window`/`--pane` occurrences union in argv order and collapse by uid; the Pane universe is owner-scoped across Projects, Windows, and Agent-managed Panes; Window/Pane status is inherited from the owning Project as live/offline/`missing-root` with `MissingRoot` outranking a stale live session; and the resolver never mutates the registry it is handed. The `<verb, kind>` cardinality matrix pins the contract cells (`get windows` 0..N, `rename window` exact-one, repeated `create --window` 1..N) and a violation returns a usage error whose ambiguity listing is bounded to 5 candidate rows carrying name/`displayName`/owner context plus an omitted count. `internal/cli` proves `cwd` is accepted by exactly one canonical route: `ResolveOutputToken` grants it only on `get pane` and rejects it on every other read kind and on every named mutation route, the shared `-o` catalog still excludes it, and the new `get` node owns only the `pane` kind while the `current` compatibility route is left untouched. `internal/app` covers the route: `get pane --current -o cwd` emits a single trailing-newline path scalar from one tmux `display-message` query; `-o cwd` without `--current` and `--current` with any other projection or with selectors are usage errors with 0 bytes on stdout and 0 tmux calls; ambiguity, no-match, an emptied label filter, and an unresolvable `--project` scope are exact-one violations with 0 bytes on stdout; the output table pins default/uid/name/ref/metadata/json/none for a resolved read; the read loads the registry through a store path that creates no directory and no lock file when no registry exists; and `get` is excluded from the pre-dispatch legacy-hook filesystem migration. `internal/integrations/metadata` pins the zero-side-effect `LoadReadOnly` read, and `cmd/projmux` proves a bounded cardinality failure reaches exit code 2 with the listing on stderr and 0 bytes on stdout.
40
40
  - `make test`: Implicit active target selector Phase 3 managed runtime binding convergence: a normal live-reload `config apply` invokes the existing registry matcher/reconciler/mirror only after successful `source-file` and routes every inventory and mirror call through the exact supplied `-L` socket; `--no-reload` and config/keymap preflight failures make zero live calls. Generated app config installs synchronous `after-new-window` and `after-split-window` boundaries that pass the expanded absolute `#{socket_path}` to hidden plumbing and use exact `-S` routing with no default socket or inherited `$TMUX` fallback. Unit/integration tables cover blank bindable Window/Pane repair to the original registry uids, ambiguous and foreign refusal, identity/owner/name-reservation preservation, two-socket isolation, and a repeat pass with zero `set-option`/`rename-window` calls and zero registry byte write. `get pane`, `describe pane`, and active-target resolution open zero write transactions and issue only the required option read. The CLI catalog, public option/environment surface, registry schema, Project scope, and heuristic/name/cwd matching remain unchanged.
41
- - `make test` / `make test-integration` / `make test-e2e`: public resource reconciliation adds `projmux reconcile resources [--dry-run] [--socket <name> | --socket-path <absolute>] [-o json]` to the CLI manifest/help/reference. One planner runs the production Registry reconciler, mutator, matcher, and tmux Mirror over a cloned Registry and a zero-write tmux overlay; normalized allocation display values keep stable item keys/order without making opaque UIDs a matching key. Tests pin missing/stale/foreign/orphan Registry and tmux items, human/JSON target plus changed/no-op/failed counts, exact `-L`, absolute/inherited `-S`, no-flag outside-tmux pre-mutation usage refusal, two-socket isolation, dry-run byte stability, repeat no-op, and whole-session containment for unknown/duplicate/wrong-owner bindings, including a rootless UID-less session whose name is claimed by multiple Registry Projects. Execute scopes observation to Registry graphs safely attributable to the selected socket, replans under the Registry lock, commits Registry authority before non-transactional mirror writes, guards every planned live write through its current Project/Window/Pane UID binding before the first mutation, and reports completed stages, each current remaining drift item in human and JSON output, and the exact retry. Injected Registry commit failure publishes zero planned UID and changes neither Registry nor tmux; injected post-commit tmux failure leaves one authoritative identity graph that the exact retry converges without duplicates. A second socket and its pre-existing Registry graph remain byte/model identical. `doctor`, `get`, and `describe` remain read-only, the route skips unrelated legacy/config migration, and no config apply, default-socket fallback, state-loss recovery, heuristic merge, rename/rebind, or Agent semantic-status work is introduced.
41
+ - `make test` / `make test-integration` / `make test-e2e`: public resource reconciliation adds `projmux reconcile resources [--dry-run] [--materialize-project <name|uid:uid>] [--socket <name> | --socket-path <absolute>] [-o json]` to the CLI manifest/help/reference. One planner runs the production Registry reconciler, mutator, matcher, and tmux Mirror over a cloned Registry and a zero-write tmux overlay; normalized allocation display values keep stable item keys/order without making opaque UIDs a matching key. Tests pin missing/stale/foreign/orphan Registry and tmux items, human/JSON target plus changed/no-op/failed counts, exact `-L`, absolute/inherited `-S`, no-flag outside-tmux pre-mutation usage refusal, two-socket isolation, dry-run byte stability, repeat no-op, and whole-session containment for unknown/duplicate/wrong-owner bindings, including a rootless UID-less session whose name is claimed by multiple Registry Projects. Execute scopes observation to Registry graphs safely attributable to the selected socket, replans under the Registry lock, commits Registry authority before non-transactional mirror writes, guards every planned live write through its current Project/Window/Pane UID binding before the first mutation, and reports completed stages, each current remaining drift item in human and JSON output, and the exact retry. Injected Registry commit failure publishes zero planned UID and changes neither Registry nor tmux; injected post-commit tmux failure leaves one authoritative identity graph that the exact retry converges without duplicates. A second socket and its pre-existing Registry graph remain byte/model identical. `doctor`, `get`, and `describe` remain read-only, the route skips unrelated legacy/config migration, and no config apply, default-socket fallback, state-loss recovery, heuristic merge, rename/rebind, or Agent semantic-status work is introduced.
42
+ - `make test` / `make test-integration` / `make test-e2e`: explicit Registry desired-topology materialization adds the exact-one `--materialize-project <name|uid:uid>` opt-in to `reconcile resources` without changing the default plan or strict reads. A separate pure Project graph plan and ledger executor cover deterministic zero-write human/JSON preview, offline `-L` full and live `-L`/`-S` partial creation in Registry insertion order, fresh session/Window primary binding, primary reconstruction from an exact-bound shell anchor, additional Window/shell Pane creation, exact uid/name/owner/CWD mirrors, stable retries that preserve selector and socket, second-run no-op with no ensure/lease/write, raw-kill recreation versus canonical-delete exclusion, and two-socket isolation. Negative tables reject duplicate selectors, a present-but-blank selector that must not degrade into the broad default reconcile (zero Registry read, transaction, write, tmux call, and output), invalid root/CWD, zero Windows, non-shell or indirect primary refs, every pre-existing uid-less Pane, unknown/duplicate/wrong-owner claims, name collisions, races, and injected failures before mutation or with ownership-checked reverse rollback and explicit residual drift. Pre-mutation owner-set proofs reject a Window relinked to a foreign session, a Pane join-paned out of its planned Window before planning, and the same move injected at the plan-to-execute boundary. A server-wide uid preflight runs before the selected Project session is created, so an offline Project whose desired Window or Pane uid is already claimed by a foreign session is refused with zero sessions created, zero pre/post-create hook runs, and zero runtime mutation; the inventory is refreshed after the session exists to cover its new tuple and any race since. a `new-window` or `split-window` that tmux mutated before reporting a synchronous hook failure has its exact attributed handle claimed and ledgered so rollback removes it, leaving byte-identical runtime and Registry. Call logs prove stored commands, Offline/Failed Agents, Agent-owned Panes, snapshots, notifications, ephemeral sessions, and historic geometry are never replayed. Initial Pane cwd is separate from the unchanged Project hook/root cwd. The isolated real-tmux smoke uses inherited-environment removal plus a unique `TMUX_TMPDIR` and two exact `-L` sockets, and observes zero-write preview, live partial Window/Pane repair back to the original uids, a zero-write repeat no-op, an empty start command on every recreated Pane, canonical delete that is not replayed, offline full topology rebuild under the original Project/Window/Pane uids, no Agent left Running, a byte-identical second socket, and cleanup of only the sockets whose queried `#{socket_path}` is under the smoke root. Offline arbitrary `-S` session creation is an explicit safety refusal because the public name-only `PROJMUX_SOCKET` cannot encode an exact socket path; live partial `-S` remains supported, and the refusal can be lifted only with a future versioned hook socket-path contract.
43
+ - `make test` / `make test-e2e`: closed Project topology startup parity routes the non-snapshot closed-Project open through the same explicit Registry topology engine, so `Use Project topology` materializes every Registry Window and Window-owned shell Pane before the client moves instead of creating one default Session. The Registry lock, prevalidation order, tmux write pass, and ownership-checked rollback now live in one shared transaction that both `reconcile resources --materialize-project` and startup activation call, with the public route's human/JSON report, retry string, stage list, and no-op wording unchanged. Activation is pinned to the exact session the open targets: a Project whose Registry projects a different session name is refused at `topology prevalidation` with zero sessions created and zero Registry writes. Startup tables cover picker-off default and picker-on `Project topology` producing the identical materialize-then-open order with no `EnsureSession` fallback, `Latest snapshot`/`Named snapshot` staying wholly on the snapshot engine with zero topology activation, a failed activation surfacing the exact stage with no open and no ensure, an existing live Project opening with neither activation nor trust gate, and the retired `empty` mode/value spelling on the internal `switch sidebar-open` transport still resolving to the topology start. A directory with no Registry Project and a Project with no Registry Window both report nothing to materialize through the zero-write snapshot read -- zero transactions, zero writes, zero tmux calls -- so a first open of an unregistered directory keeps its single default session. The production wiring is asserted to bind the `-L projmux` exact socket rather than an inherited client. Startup copy is catalog-backed in en-US and ko-KR for the `Project topology` row, its shared `restore every saved Window and shell Pane` description, the Settings ask/topology choices, and the `Esc: Project topology` footer, and the picker no longer advertises `Empty session`. The isolated real-tmux startup smoke strips inherited `TMUX`/`TMUX_PANE`, uses a unique `TMUX_TMPDIR` with an exact `-L` socket plus a sibling socket, attaches a real client, opens a closed multi-Window/multi-Pane Project through the installed `switch open` route, and observes the full Registry uid topology, the client moved to that session, an empty start command on every recreated Pane, no Agent left Running, a failed activation that leaves the client where it was, a byte-identical sibling socket, and cleanup of only the sockets whose queried `#{socket_path}` is under the smoke root.
42
44
  - `make test` / `make test-integration` / `make test-e2e`: rename/rebind transport convergence keeps Registry uid/name/root authority and writes only the corresponding exact live field after the Registry transaction: Project name → `@projmux_project_name` without session rename, Window name → `@projmux_window_name` without `displayName`/`window_name`, Pane name → `@projmux_pane_label` without `pane_title`, and Project root → `@projmux_project_path` without filesystem or uid change. Public `rename agent` adds the exact-one Agent cardinality/aliases and changes only Window-scoped `metadata.name` plus its reservation, preserving topic annotations, provider, lifecycle linkage, and managed Pane metadata with zero tmux writes. Unit tables pin field-specific call logs, offline and inventory-unavailable later convergence, duplicate UID refusal, and post-commit live-write failures that exit nonzero with the public reconcile retry while retaining durable Registry drift. Immediate projection exists only for an inherited absolute `$TMUX` socket, routes every lookup/write through exact `-S`, targets stable `$N`/`@N`/`%N` handles, and performs zero tmux calls outside tmux or for a malformed relative inherited socket. Public reconcile treats a known unique Project UID as authoritative over the stale old path after rebind, plans/guards only the path-anchor write, repeats no-op, and keeps unknown/duplicate claims fail-closed. The isolated real-tmux smoke uses inherited-environment removal plus a validated exact socket path, asserts session/window/pane runtime names and a second socket remain unchanged, exercises offline later reconcile and injected option-write failure/retry, and cleans only the contained sockets.
43
45
  - `make test` / `make test-e2e`: canonical resource create lifecycle reentrancy coverage pins the private, session-scoped create lease passed atomically through `new-session -e`, exact-socket/session lifecycle inspection, live-only deferral, stale-lease cleanup, and a second in-transaction reconciliation after explicit mirrors. Standalone `after-new-window`/`after-split-window` hooks remain synchronous. Window/Pane materialization preserves combined tmux output on non-zero exit, accepts only one exact newly observed `@N`/`%N` handle for ownership-checked reverse rollback, emits zero stdout on failure, keeps the registry byte-identical, reports ambiguous or ownership-mismatched residual drift, and never removes an unrelated object. The isolated tmux smoke exercises `--create-window`, exact returned pane IDs, registry/runtime uid agreement, and forced synchronous-hook failure cleanup; the existing Agent-create smoke covers canonical creation in a live managed Window with its stub provider.
44
- - `make test` / `make test-e2e`: Window resource deletion converges the registry cascade and its exact live tmux binding. Explicit UID and selector-omitted active Window paths inventory only `-L projmux`, require exactly one `@projmux_window_uid` under the owning Project's Registry-authoritative exact session, validate the optional session `@projmux_project_uid` mirror when present, and refuse missing, duplicate, foreign, stale-session, or foreign-caller-socket bindings before any write. The plan and confirmation include the exact `@N` kill plus the implicit Project-session end when the selected set removes its last live Window; `--dry-run` renders both resource and live cascades with zero Registry transactions and zero tmux writes. External deletion revalidates both signatures under the Registry lock, kills exact live targets before committing the descendant delete, preserves the Project and sibling Windows, leaves Registry bytes untouched on tmux failure, and reports exact removed `@N`/session plus retained Registry UIDs if the store commit fails. A self-target commits the exact Registry cascade, writes and flushes the complete result, then queues a shell-quoted background `kill-window` through the same exact socket so caller teardown cannot interrupt its durable result; queue failure is nonzero and names the committed Registry/live-orphan drift. The isolated real-tmux smoke strips inherited `TMUX`/`TMUX_PANE` for external calls, verifies the actual socket path under its smoke root, covers two-Window sibling preservation, last-Window session termination, and implicit self-target output/Registry/live completion, then cleans up only that exact socket.
46
+ - `make test` / `make test-e2e`: create-window foreign-session attribution hardens the same lifecycle boundary with an atomic `Created` plus `$N`/`@N`/`%N` new-session result and exact owner verification. Before the first reconcile import, lease, mirror, or Registry write, create now requires the selected live session to carry the registered Project's exact uid and canonical root with no server-wide duplicate uid/root claimant; the check repeats against the reconciler's fresh inventory and immediately before an inner create, so outer-miss/inner-hit, first-use discovery, blank/foreign same-name, and duplicate-other-session races fail closed. New Windows accept only the exact composite `@N`/`%N` pair that is the sole global before/after addition under the stable `$N`; wrong, pre-existing, wrong-session, missing, or ambiguous handles remain unclaimed with exact residual diagnostics and byte-identical Registry/pre-existing options. Claim injection tests pin uid claim rollback ledger full mirror ordering for Session/Window/Pane, ownership-checked rollback, and chained name/uid selector refusal. The isolated real-tmux smoke preserves blank and foreign same-name sessions byte for byte on the exact contained socket, then runs eight concurrent same-name `--create-window` racers and requires one Window, nine unique Pane uid mirrors, exact returned `%N` handles, and unchanged pre-existing bindings.
47
+ - `make test` / `make test-e2e`: Window resource deletion converges the Registry desired-topology cascade and an exact live tmux binding when one exists. Non-implicit targets (explicit references or `--all`) inventory only `-L projmux`: zero exact `@projmux_window_uid` mirrors, including a typed absent-server result, is a valid Registry-only offline cascade, while one exact mirror is killed under the owning Project's Registry-authoritative session. Duplicate, foreign, stale-session, generic inventory-failure, and preflight-to-execution race states remain fail-closed; selector-omitted active deletion still requires its exact live mirror and refuses an implicit foreign-caller socket. The plan and confirmation include the exact `@N` kill plus implicit Project-session end when present, while an offline plan explicitly reports that no tmux Window will be killed; `--dry-run` renders the complete resource/live-or-offline cascade with zero Registry transactions and zero tmux writes. Execution revalidates both resource and live signatures under the Registry lock, preserves the Project, sibling Windows, unrelated sockets, and Registry bytes on pre-mutation failure, and reports exact removed `@N`/session plus retained Registry UIDs if the store commit fails. Repeat deletion of an already-removed offline UID is a no-match with zero writes. A self-target commits and flushes the Registry result before queueing a shell-quoted exact-socket kill. The isolated real-tmux smoke covers live sibling/last-Window/self-target parity plus an offline Window with shell and Agent descendants, byte-identical offline dry-run, exact Registry-only cascade, repeat no-match, and foreign-socket preservation.
45
48
  - `make test` / `make test-e2e`: Pane and Agent live-binding deletion extends the same fail-closed boundary to `delete pane|agent`. One exact `-L projmux` Pane inventory joins each Registry Pane uid through its owning Agent/Window/Project graph to the exact `@projmux_pane_uid`, `@projmux_window_uid`, optional session Project uid, and Registry-authoritative session projection; missing, duplicate, foreign-Window/Project, stale-session, and foreign-caller-socket rows cause zero writes. Agent deletion derives its runtime targets only from its Registry-owned managed Panes—never process content—and removes the Agent, managed Pane resources, and exact `%N` objects while preserving sibling Panes, Windows, and owner graph; deleting the current managed Pane alone preserves the Agent as `Offline` with a cleared `paneRef`. Dry-run, confirmation, and result output list every exact Pane plus implicit last-Pane Window and last-Window session cascades. Execution revalidates both resource and live signatures under the Registry lock, kills external targets before commit, and reports already-killed exact Pane/window/session/Pane-uid tuples with retained Registry UIDs on partial tmux or store failure. A caller-containing plan writes the complete deleted-Pane tombstone set under the Registry lock before commit; a partial tombstone or store failure restores every reachable marker while the Registry identities still exist, and any incomplete rollback keeps those Registry owners authoritative. After commit and flush, exact-socket background kills are queued only from revalidated tombstones, so any unqueued survivor remains explicitly tombstoned and retryable. Both anchored import and repair matching refuse that marker, preventing a failed delayed kill from re-importing a deleted Pane as an orphan. The isolated smoke strips inherited `TMUX`/`TMUX_PANE`, validates both socket paths below its dedicated root, covers sibling, last-Pane, managed-Pane-to-Offline, Agent cascade, anti-reimport, and second-socket preservation, and cleans up only the validated exact sockets.
46
49
  - `make test-e2e`: Implicit active target selector Phase 3 runs installed-shape apply and lifecycle convergence against real tmux servers under a dedicated `TMUX_TMPDIR`, after stripping inherited `TMUX`/`TMUX_PANE`. It verifies the real `#{socket_path}` stays below the smoke root; deleted managed Window/Pane uid options return to their original registry identities after exact-socket apply; a newly created Window and a split Pane carry bindings before the creating tmux command returns and before an implicit read; repeat apply/lifecycle leaves registry bytes and bindings unchanged; a second socket remains untouched; and cleanup kills only the validated exact sockets.
47
50
  - `make test`: Implicit active target selector Phase 2 scopes selector-omitted `get windows|panes|agents` inside tmux to the Project derived from the active Window uid mirror and registry owner chain through the selector engine's single `windowScope` seam. Tables pin inside/outside/missing-binding behavior, explicit `--project` parity, registry-wide `--all-projects`, read-route rejection of bare `--all`, unchanged global `get projects`, and unchanged notification/snapshot/destructive boundaries; the generated CLI reference documents the 0.x feature-minor default narrowing and its same-release escape hatch.
@@ -54,6 +57,7 @@
54
57
  - `make test`: CLI information architecture v2 Phase 7 Agent create composition: `create agent` and the `codex`/`claude`/`antigravity` shortcuts gain a resource-backed detached route behind the same `--project` dispatch discriminator `create pane` uses, so the `--project`-less invocation stays the byte-identical `ai split` bridge while the canonical route allocates a Window-owned Agent plus its Agent-owned `<agent-name>-pane` managed Pane, reuses the Phase 6 anchor resolution, `--create-window` ensure, operation ledger and rollback, and enforces a new `{create, Agent} = 1..N` fan-out cell that four tests falsify when it is weakened; a matrix covers provider-required refusals on every canonical spelling, shortcut-to-canonical registry-snapshot equivalence, non-reuse against a Window that already owns a running Agent of the same provider (`codex` → `codex-1` → `codex-2`, first Agent's `status.paneRef` untouched), Window-scoped explicit `--name` (legal across two Windows, exit 2 with zero mutations inside one), payload-after-`--` reaching the provider launch but never the Agent or Pane name, all seven shared `-o` modes end to end with `AgentList`/`AgentMetadataList` envelopes for a single result and `-o pane-id` resolving to the managed Pane rather than the anchor, multi-window fan-out hitting each Window's own anchor exactly once, four failure stages each leaving the registry byte-identical with no `Failed` Agent behind, six racers converging on one ensured Window uid under the real on-disk lock with `-race -count=10`, the Settings enabled-agents gate applying to the canonical route as a plain error without promoting `--force-agent`, and a help-honesty audit that runs every advertised `-o` token and every advertised `--flag` of the Agent node and all three shortcuts; the identity projections of the compatibility bridge become exit 2 naming `--project` as the fix instead of exit 1 claiming the composition is unwired.
55
58
  - `make test-e2e`: CLI information architecture v2 Phase 7 Agent create against a real tmux server on the same dedicated exact socket under the smoke root, with inherited `TMUX`/`TMUX_PANE` stripped from every call and only the verified `#{socket_path}` killed: a stub provider binary proves the launch actually ran in the created pane with the project root as its cwd, the managed pane carries both the Projmux Pane uid mirror and the `@projmux_ai_managed`/`@projmux_ai_agent` options, the active window and pane are unchanged and the new pane is not active, the `codex` shortcut allocates `codex-1` instead of reusing the first Agent, a `--` payload reaches the provider argv while the name stays `codex-2`, a missing `--provider` and an explicit name collision are each exit 2 with zero stdout and an unchanged registry md5, and the `--project`-less bridge still refuses its identity projections with a message naming `--project`.
56
59
  - `make test` / `make test-e2e`: canonical create even-axis parity shares the legacy AI split geometry parser, row/column peer selector, and deterministic remainder distributor across `create pane`, `create agent`, and every provider shortcut; route tests pin split → Window-local geometry read → axis-only resize ordering for explicit/default anchors, right/down placement, fan-out, ensured Windows, one-pane no-op, malformed/unreadable topology, unrelated rows/columns, and silent post-split observation/resize failures with zero false rollback. The shared geometry format carries the materializer's escaped `\037` row separator rather than a raw tab, and a dedicated test pins both that the format holds no raw tab and that a `_`-sanitized row parses as no geometry: tmux 3.5a rewrites a raw tab in list output to `_` while 3.6 emits it verbatim, so the tab spelling parsed every row as a single field and made equalization a silent no-op on 3.5a — the version the e2e image runs. The isolated real-tmux create smoke repeats horizontal and vertical canonical creates to four panes, measures a maximum one-cell size difference, preserves raw pane IDs, Registry mirrors, focus, and an unrelated Window's exact geometry, and retains exact socket-contained cleanup.
60
+ - `make test` / `make test-e2e`: resource scope short-option parity registers `-p` for Project and `-w` for Window on the shared selector, standalone Pane read, resource-backed create, and canonical focus seams, while `-A` remains exclusive to `get windows|panes|agents`. Deterministic matrices compare long-only, short-only, and mixed argv for selector results, cardinality/error bytes, positional interleave, repeated Window order, stdout/stderr/exit, Registry mutation, and tmux call plans; explicit negatives keep Pane without a shortcut, reject `-A` elsewhere, and leave `agent usage --window` without `-w`. Help-honesty and generated-reference checks require every advertised alias to parse and preserve the root-help and command-tree goldens byte-for-byte. The isolated real-tmux smoke exercises short-form create agent/pane, read, focus, and all-Project escape on its exact guarded socket, with the first `--` preserving all following scope-looking payload bytes.
57
61
  - `make test` / `make test-e2e`: Registry–tmux Phase 6 Agent interaction and launch-readiness coverage pins exact active/explicit Agent topic and semantic-status resolution, Registry-first commit plus exact live projection and public reconcile retry, Offline topic resume mirroring, lifecycle-separated closed interaction kinds with restart/stale/Offline current-`unknown` invalidation, and Agent-owned Pane plus shell-manual-attention Window badge priority without durable glyph/color/aggregate state. Provider hooks coalesce session-ref and semantic observations into one Registry transaction when both occur and never synthesize an Agent for a shell Pane. Initial-prompt create distinguishes `Running` resource creation from bounded provider activation, returns an exact Agent/Pane retry-and-cleanup diagnostic when unconfirmed, and proves prompt non-persistence plus zero pane capture. Provider-neutral `--cwd`/repeatable `--add-dir` validation rejects nonexistent, traversal/symlink escape, unauthorized sibling, duplicate, and provider-unsupported paths before runtime mutation; preserves the Window's Project owner; stores effective Agent/Pane workspace; and forwards only caller-explicit additional roots through Codex/Claude argv. The isolated real-tmux slice uses exact primary/foreign sockets to cover immediate topic/status projection, Offline topic-to-resume mirroring, committed projection failure plus public reconcile retry/no-op, raw title/topic non-identity, foreign-socket containment, and explicit cross-Project workspace with unchanged owner Project. Compatibility `ai topic|status` keeps its stdout/stderr/exit surface while forwarding managed Pane changes through Agent authority, and notify queue acknowledgement/TTL/severity plus shell manual attention remain unchanged.
58
62
  - `make docs`: regenerates `docs/cli.md` from the `internal/cli` command manifest through `go run ./internal/tools/gendocs`. The render goes to stdout and is captured through a temp file, so a failed generator cannot truncate the checked-in page. Run it whenever a route, summary, usage synopsis, output mode, or field projection changes; `make test` fails until the checked-in page matches.
59
63
  - `make test`: CLI information architecture v2 Phase 8 generated CLI reference: `docs/cli.md` is rendered from the command tree in `internal/cli/catalog.go` -- the same manifest the binary renders `projmux help` from -- and the hand-maintained prose it used to hold moved to `docs/cli-guide.md`, which the generated page, `README.md`, and `README-ko.md` all link. The drift gate compares the checked-in page byte for byte against a fresh render and names `make docs` in its failure, so the CI `Unit Tests` job catches a stale page; a determinism test renders twice and rejects an embedded repository or temp path, which is what makes regeneration on an unchanged tree a guaranteed no-op. Coverage is a two-way diff rather than a count: every public route and sub-route in the tree has a section and every section in the document is a real node, with a non-vacuity guard that both a canonical and a shortcut route exist to cover. The internal boundary is held on the published page as well as in the primary listing -- no hidden route gets a section, and no `Canonical spelling:` line may point at an executable path absent from the manifest. The generator reads the command tree and never `canonical.go`; canonical-only summaries are therefore checked as an explicit divergent set rather than leaking into command help. Advertisement-honesty checks keep registry reads aligned with the outputs their handlers implement and keep restore limited to its shipped dry-run preview contract.
@@ -70,6 +74,7 @@
70
74
  - `make test`: Columnar default for list reads Phase 0 plus display-first resource projection: the default human plural reads (`get projects|windows|panes|agents`) are space-aligned tables whose first two columns are always `DISPLAY NAME`, `NAME`, followed by the existing per-kind columns in their prior relative order (`STATUS`, owner chain, optional AGENT/SESSION, `AGE`). Four whole-table goldens pin Project aliases, the already-projected tmux Window display/tab beside its distinct stable name, and current Pane/Agent fallback; the fallback uses metadata.name only when `strings.TrimSpace(metadata.displayName)` is empty and preserves a non-empty stored displayName byte-for-byte. Adversarial Pane `status.displayTitle` and Agent topic values, raw-title wording, tmux handles, duplicate display aliases, and selector negatives prove that secondary display data is never promoted and that only metadata.name or explicit `uid:` remains selectable. Each golden also asserts zero box/rule characters, tabs, and trailing whitespace. Widths stay terminal-display-cell based through `projmuxpicker.VisibleLen`; Hangul, long values, empty interior/trailing cells, and the direct rune-count falsification remain covered. The table remains gated on `OutputModeDefault` plus plural `list`: singular `get`, `describe`, and rename/create summaries remain byte-frozen, as do scalar `-o uid|name|ref|none` and whole List-envelope `-o metadata|json` projections. Zero rows still succeed with zero stdout/stderr bytes, and the injected-clock AGE boundary/determinism suite remains unchanged.
71
75
  - `make test`: Resource timestamps surfacing Phase 0 timestamps in read surfaces: the registry has always stored `metadata.createdAt` on every resource, `Condition.firstObservedAt`/`lastTransitionAt` on every observed condition, and `Agent.status.lastTransitionAt` on every Agent, and `-o json` has always emitted them -- but no human-facing surface printed a single one of them, so `describe pane` carried zero time rows and the columnar `get` carried no `AGE`. Both now render, and nothing new is collected: no tmux query, no schema field, and no second relative-time implementation. The plural reads gain an `AGE` column, last on all four kinds because that is the one column whose width moves with the clock and last is the only position where growing it cannot walk an owner-chain column sideways; it is rendered through `i18n.FormatDuration`'s compact form, the same single implementation `notify list` already renders its own AGE through. `internal/app` pins the arithmetic as a boundary table asserted from both sides of every unit rollover (`59s`/`1m`, `59m`/`1h`, `23h`/`1d`, up to `900d`), plus the three cases that render an empty cell rather than a number -- a uid that has left the registry, a resource stored before `createdAt` was stamped whose zero Time would otherwise date it to year 1, and a caller that passed no clock -- and a whole-table golden spanning `9s`/`47m`/`6h`/`11d` in one read, which is also the alignment case because the widest cell sets the column. Determinism is stated as a two-sided claim rather than a single assertion, since only the pair is falsifiable: the same injected clock twice must be byte-identical (which a renderer ignoring the clock would also satisfy) **and** a clock advanced three days must produce a different table (which a renderer reading `time.Now` would fail), so only a renderer reading the passed-in clock and nothing else survives both. The clock reaches exactly one projection: `writeResourceProjection` takes it as an explicit parameter and the singular call sites (`get pane`, `describe`, `rename`, and the create fan-out, whose default projection is the `<kind>/<name> created` line and never the table) pass the zero Time, which `resourceAgeCell` renders as no age at all rather than as "created exactly now". `describe` gains absolute UTC instants instead: a `CreatedAt` row beside the identity rows on all four kinds, `PhaseSince` under an Agent's phase, and the second half of a condition's stored pair (`lastTransitionAt` beside the `firstObservedAt` that was already there), each omitted when the registry file predates the field so a pre-`createdAt` registry keeps its exact current block. The machine-consumer contract is frozen the whole way: `-o uid|name|ref|none` byte for byte, and `-o metadata|json` as whole documents rather than by substring -- they are rendered from the same `ObjectMeta` the AGE cell reads, so a new key anywhere in either reddens. The Columnar track's `describe pane` byte-golden moves by exactly one row and keeps its own purpose intact: what it guards is that the columnar table is reachable only with `list` true, and a header line or a padded column in a singular route still reddens it.
72
76
  - `make test`: Settings IA re-cut Phase 0 full navigation cutover: the visible Settings tree is declared once in `internal/app/settings_nav.go` and frozen by `internal/app/testdata/settings-nav-tree.golden`, so an IA change is a golden diff rather than a shape spread across the section loops. Structural tests prove the catalog is a tree (unique ids, resolvable parents, a scope root at the top of every path), that every control hangs off exactly one owning `[View]` and that only a View owns children (row-affordance exclusivity), and that each rendered picker value maps onto exactly one navigation node whose owning loop the closed reachability table actually handles (control-owner cardinality); the drift guard runs in both directions, so a section loop cannot grow a row the golden does not show. The removed-path negative guard proves `Labs`, the separate `Theme` root, `Project recipe` and `Effective merge view` are neither rendered, nor catalogued, nor routable through `runSection` — the retired handlers, action metadata, and i18n entries are absent rather than hidden redirects — and the legacy-copy guard rejects the retired vocabulary (`AI Settings`, `Project Picker`, `Project Root`, `Workdirs`, `Enabled agents`, `Default split mode`, `Resume picker`, `Session State`, `Kill Session`, `New window`, `Hook quiet policy`, `Delivery sources`, `Labs`, `Usage HUD`, `Path icon`, `Notify icon`, …) across navigation labels, rendered rows and keymap display labels, in both locales. Display renaming is proven display-only: the keymap action IDs (`new-window`, `Sidebar:KillSession`, `ai-split-right`, …), the `sessionstate:`/`project-hooks:`/`live-resources:`/`statusbar-decoration:`/`workdir:`/`projdir:`/`switch:`/`keymap:`/`theme:` action spellings and their owner contracts are all unchanged behind the renamed rows. Keybinding navigation is category-based: an exhaustiveness test requires every catalog action to carry exactly one explicit category (never an ID-prefix inference), every surface action to sit in the sidebar/picker category with a display label for its surface, one display label per action, and each action to render exactly once across the rendered categories, while the category rows carry their members' action IDs, labels, chords and state so search still crosses the category boundary. The action detail projects the product semantics the target IA asks for — target kind, result kind, placement and anchor — and pins that every interactive right/down split declares the explicit current-Pane anchor and a right/down placement, that `Create <Provider> Agent` is always-new, and that resume names the existing `Offline`/`Failed` Agent it rebinds. Navigation is proven side-effect free (opening the root, every section and every container writes no config and runs no tmux command), every `[State]` row carries the catalogued passive value so Enter can never fall through, and the promoted rows keep their old state at the new route (Resources reads the same `live-resources` file it read from Labs, the project automation policy the same `project-hooks` file, Theme the same global `[theme]` block through `Appearance > Theme`). The pinned Project item view keeps Project identity separable (display name, unique name, uid, bound root, condition, missing-since, runtime) and offers same-uid remediation through the canonical `rebind project` route instead of re-pinning, with the unregistered-path case rendering a disabled rebind row carrying its reason. The Agent Usage HUD ownership guard keeps `agent usage` canonical: no Settings row spells a `Usage` resource or a `get usage` read, and `docs/settings-ia.md` names the owner.
77
+ - `make test` also covers Settings directional hierarchy parity: every catalogued navigation/actionable/passive/Back kind has a Right/Left outcome; Right opens only forward-navigation rows and never executes mutation, passive, or Back rows; child and grandchild Left return exactly one View while root Left stays in place; Esc and the Settings popup-toggle close action remain available at root and every visited depth; en-US/ko-KR frames advertise the arrows; and typed-query, color-grid, scope-chip/chord, and key-recorder input retain their existing native semantics. A native root → child → grandchild → back → root interaction and a config-tree mutation-negative audit pin the shared policy without changing global picker arrows.
73
78
  - `make test`: Settings IA re-cut Phase 1 Automation and Notification ownership: the two containers are proven to be different product concepts by their labels alone. Automation names only Projmux-owned user scripts and the policy that gates them (`Projmux session lifecycle`, `After notification queued`, `Project automation policy`) and never a Provider or an integration; Notifications names only delivery and projection and never a lifecycle script or a `[hooks.*]` key. The Provider inventory is pinned to the Claude/Codex/Antigravity enum: the tmux bell producer is filtered out of Provider Integrations by producer identity and rendered flat at its own `tmux event source` View, whose wiring row says in words that the bell is not an Agent Provider. `Agent event behavior` renders the same Provider enum, so a Provider can never be wireable in one destination and missing from the other, and a provider x event matrix walks every event to its four-way `default`/`notify`/`state`/`quiet` choice and asserts each choice is addressed to the provider and event that rendered it. The two Provider destinations are proven disjoint: install/remove/conflict/config-path/`Check integration` rows exist only in the wiring detail and the `ai-hook-set:` choices only in the behavior view, with neither surface rendering the other's action prefix. `Check integration` (and `Check` for the bell) is an observable read-only action rather than a placeholder: it re-reads the shipped doctor diagnostic, reports status/config path/conflict as Settings feedback, shells out to nothing, leaves the config tree byte-identical, and errors on an unknown id. The `[hooks.send-noti]` + `PROJMUX_NOTIFY_HOOK` parity test sets both at once and proves they are a supported combination rather than an either/or: each keeps its own resolved value, the desktop row disclaims the fan-out and the Automation row disclaims the sender, neither row renders the other's value, and the three session lifecycle events stay empty. An automation event x scope matrix walks `pre-create`/`post-create`/`post-attach`/`send-noti` in both scopes, keeps the technical `[hooks.<event>]` spelling in the state row, requires the project scope to name its missing-context reason instead of falling through to a global value, and pins `send-noti` as a sibling of the lifecycle list rather than a fourth member of it. The attention guard keeps live Pane badges out of every Notifications surface while the badge style keeps its Appearance home, and the retired `Hook quiet policy` vocabulary is gone from the shared UI text catalog and from the rendered rows in both locales while staying on the negative-guard list.
74
79
  - `make test`: interaction-first Keybindings details keep action state, current single keys/sequences, and add/manage/remove/reset/test actions ahead of passive explanation. `TestSettingsKeybindingActionDetailMatrix` verifies semantic/handler/`internal/cli` manifest parity directly against the internal models while separately proving every rendered selectable row is handled; `TestSettingsKeybindingActionDetailPrioritizesStateBindingsAndActions` freezes the default/custom/unbound row order and mutation reachability; `TestSettingsKeybindingKeyDetailRowsAreAllHandled` keeps replace/remove/test routes and requires an unavailable test row to carry both reason and next step; and `TestSettingsKeybindingRetiredContainerCopyIsGone` renders Action/Key/Sequence surfaces in en-US and ko-KR and rejects visible Target/Result/Placement/Anchor/Handler, canonical storage/delivery, cancellation teaching, and Advanced/Details/Delivery/About-style containers. Delivery outcome, one-reader, unavailable/cancel/no-write, capture/typed normalization, feedback-stage recovery, protected-action, reserved-key, sequence storage/runtime, and distinct create/resume parity tests remain in the maintained suite.
75
80
  - `make test-e2e`: Settings IA re-cut Phase 0 native navigation smoke against the real attached tmux client: a deep search from the Settings root walks `Appearance > Status Bar > Git > Icon` — proving search lands on the owning View rather than executing a control, and that reaching the icon chooser writes no decoration file — then returns to the root through the Back rows, and the keybinding recorder flow now reaches its action through the `Launch & popups` category, searching for the action's own name at the root to prove search crosses the category boundary.
@@ -115,7 +120,7 @@
115
120
  - `make test` also covers AI desktop notification dedupe precedence (env override > Settings saved value > default), Desktop notifications mode persistence through `desktop-notify-mode`, saved-config precedence over live tmux options, `projmux config apply` regeneration of `@projmux_desktop_notify_mode`, configured dedupe-window collapse/send behavior, Settings > Notifications AI dedupe preset/custom rows, explicit notify focus consume rules for selected critical rows and older same-pane non-critical AI cleanup, preservation of critical/permission/stop-failure/external/git/k8s rows during bulk cleanup, `projmux internal focus --uri` compatibility-input queue consume, and attention clear paths that do not ack the queue.
116
121
  - `make test` also covers the two-state desktop notification contract: `NormalizeDesktopNotifyMode` accepting only `off`/`notify` while reading retired `raise`/`auto-raise`/`autoraise` literals as `notify`, per-source legacy-alias resolution that keeps env > saved file > tmux option precedence instead of falling through, a platform-neutral `notify` default including WSL + Windows Terminal, saved/tmux-applied values restricted to `off`/`notify`, Settings > Notifications > Desktop notifications offering exactly `Off` and `Notify` with no `Raise` row/description/search keyword in English and Korean, WSL Toasts carrying no `launch`/`activationType="protocol"` payload and writing no `projmux://` protocol-handler registration, and `projmux focus` never dispatching a host-window focus adapter in any mode.
117
122
  - `make test` also covers AI hook runtime action precedence over catalog defaults, runtime quiet for known Codex notify events, runtime notify for known Claude quiet events, generic in-app-only notify rows for known Codex hook events without specialized handlers, suppression of desktop notification and `send-noti` dispatch on that generic path, separation of runtime hook action from catalog install events, Settings > Notifications hook quiet policy display/write behavior without external install/remove execution, hook desktop notification payload parity with the in-app queue text across the shared OS notification payload, normal/transient OS urgency and expiration for critical AI queue rows, and dormant title/capture fallback gating once a pane is hook-active.
118
- - `make test` also covers Settings > Session State as global settings-only UI with default-off auto-save, Sidebar startup picker relocation/compatibility, and no window/pane tree, Settings > Project > Session State project-derived identity plus project auto-save override/effective source rows and distinct Save latest snapshot / Save named snapshot labels, closed-project save disabled reasons, named snapshot portable path conversion, project/global auto-save precedence, Projects > Sessions > State read-only latest/named snapshot overview with window/pane cwd/recipe/agent-resume health, user-facing canonical snapshot status/save/delete/restore actions including explicit manual save bypass of disabled autosave and agent resume status/confidence preview, and Project open sidebar startup coverage for Session State opt-in `Start project`, default-off empty creation, Latest snapshot, Named snapshot, Empty session, Back, saved-at row metadata, existing-session skip, startup-before-trust ordering, detached sidebar open continuation before client-scoped separate trust popup handoff without sidebar inline trust UI, trust approve continuation, and trust deny/cancel no-session sidebar refresh behavior.
123
+ - `make test` also covers Settings > Session State as global settings-only UI with default-off auto-save, Sidebar startup picker relocation/compatibility, and no window/pane tree, Settings > Project > Session State project-derived identity plus project auto-save override/effective source rows and distinct Save latest snapshot / Save named snapshot labels, closed-project save disabled reasons, named snapshot portable path conversion, project/global auto-save precedence, Projects > Sessions > State read-only latest/named snapshot overview with window/pane cwd/recipe/agent-resume health, user-facing canonical snapshot status/save/delete/restore actions including explicit manual save bypass of disabled autosave and agent resume status/confidence preview, and Project open sidebar startup coverage for Session State opt-in `Start project`, default-off Project topology creation, Latest snapshot, Named snapshot, Project topology, Back, saved-at row metadata, existing-session skip, startup-before-trust ordering, detached sidebar open continuation before client-scoped separate trust popup handoff without sidebar inline trust UI, trust approve continuation, and trust deny/cancel no-session sidebar refresh behavior.
119
124
  - `make test` also covers `projmux shell` usage without startup selector flags, project-context default target lookup from `PROJMUX_CWD` or nearest project marker, explicit `--session` separation from project-derived defaults, and direct empty attach behavior without shell startup picker/replay.
120
125
  - `make test` also covers pane identity separation and projection: canonical `rename-pane-label` prompts with `pane label:` initialized only from the current label, Enter confirms, empty Enter clears, and native Esc cancels; unchanged Ctrl-Shift-M default and Settings expose only `rename-pane-label`, while stale `[bindings.rename-pane-topic]` keymaps fail with an exact `[bindings.rename-pane-label]` replacement. AI topic set/clear keep existing topic/manual ownership, no raw-title action is exposed, and pane border/window tab/popup/sidebar/recent-window resolve user label → agent AI topic → known shell → raw title.
121
126
  - `make test` also covers session snapshot pane identity capture, old/new decode, and replay field-matrix contracts: `Pane.Label`, raw title, recipe topic/manual ownership, startup fields, and agent/resume metadata stay separate; old snapshots leave label and ownership absent without equality inference; tmux creation-returned pane ids are the replay targets; and agent direct-start never copies a saved topic into the raw title.
@@ -156,8 +161,8 @@
156
161
  - `make test` also covers stale state cleanup policy: successful prune, runtime stop, runtime sessions picker, and switch/sidebar session kills trigger best-effort preview-row cleanup; notify refresh publishing sweeps dead-PID sockets while preserving live-PID sockets; snapshot prune listing distinguishes dead sessions from old live persistent sessions without deleting either, explicit named deletion removes only selected snapshots, malformed snapshots remain preserved with a warning, and doctor documents the manual-only retention policy.
157
162
  - `make test` also covers Recent Windows Phase 2/3/4 keybinding, popup, and runtime recorder integration: `RecentWindows:Open` owns the guaranteed `M-3` launch default, generated tmux config uses client-scoped `tmux popup-toggle recent-windows`, the popup body runs `projmux window recent`, generated tmux config installs background `window record` hooks for window and client-session changes plus first-use population without `session-window-changed`, `window record` snapshots current tmux socket/session/window/active-pane metadata into the socket-scoped store with escaped field parsing, repeated same-window record fires dedupe instead of growing entries, the queue remains bounded by `recentwindows.DefaultLimit`, Settings > Keybindings shows Recent Windows with the `M-3` default, setup probe text names Recent Windows, `SessionPopupToggle` no longer owns the guaranteed `M-3` default, and escaped tmux field separators are parsed for current/list window metadata.
158
163
  - `make test` also covers the Linux resource-attribution Phase 0 contract: tmux-only typed socket/session/window/pane PID/TTY/current-path/project-anchor inventory without unsupported placeholder values; explicit project-anchor priority plus read-only current-path fallback through canonical pinned/root-child discovery, longest-root matching, outside/home exclusion, and linked explicit/fallback conflict handling; one-pass procfs stat/meminfo collection; PID+starttime reuse defense and SID ownership; first/invalid/logical-CPU-change unknown CPU; host-share/core-equivalent CPU and RSS/host-ratio memory; unique pane-to-window-to-project conservation; linked-anchor `Shared / ambiguous`, missing-anchor `Unassigned`, setsid escape, fast-exit/race/permission partial states; and explicit host remainder/overage without silent clamping. The opt-in `PROJMUX_RESOURCE_PROJECT_FALLBACK_SMOKE=1 go test -run TestResourceProjectFallbackTransientSmoke -v ./internal/integrations/tmux` test provides an isolated read-only fallback fixture, while `PROJMUX_RESOURCE_TMUX_SOCKET=projmux PROJMUX_RESOURCE_EXPECT_PROJECT_ROOT=/path/to/project go test -run TestResourceAttributionRealTmuxReadOnlySmoke -v ./internal/integrations/tmux` provides sanitized live-socket project-bucket/count evidence; benchmark commands and current measurements are maintained in [resource-attribution.md](resource-attribution.md).
159
- - `make test` also covers the Resource Inspector Phase 1 vertical slice: immediate non-actionable warming chrome; Project → Window → Pane → read-only detail hierarchy with project/window `PANES n` and pane `PROCESSES n` projections; explicit attributed buckets plus the non-actionable Other summary; shared label → active-agent topic → interactive shell → raw-title pane identity precedence with reused `Process <PaneCommand>`/PID-SID/Pane %N/TTY secondary and detail naming; resource-only Right/Enter forward, Left back with root no-op, all-depth Esc close, and complete Alt-Left action/hint removal; statusbar-parity CPU 70/90 and memory 75/90 exact semantic boundaries with severity-word-free values, unavailable `--`, lifecycle text, and resolved default/custom theme roles; Name-default Tab-computed CPU/Memory/Name ordering that remains stable across automatic refresh; stable-id search metadata plus refresh selection/query/breadcrumb/focus preservation and vanished-row nearest-neighbor behavior; fresh/stale/partial/unavailable/empty/gone/overage transitions with atomic refresh-progress/footer updates; an exact fixed five-row divider/Host/Attributed/Coverage/Sample bottom summary dock that remains independent of scroll/search and separate from the action footer, with bounded diagnostics on Sample only, minimum navigable list height, and no divider/border bleed; changed-row synchronized differential frames at 80x24 and wide en-US/ko-KR layouts; truthful RSS-sum rendering; immediate automatic pre-collection not-fresh progress with the last complete sample retained, gated non-overlapping two-second and Ctrl-R sampling, cancellation, and prior-sample disposal; direct CLI and client-scoped popup mode; no-default `Resources:Open` catalog/Settings/config/custom-alias close behavior; statusbar `resources` range/click byte-equivalence, Labs visibility independence, other-client isolation, and existing `projmux internal status resources`/window-click fallback regression.
160
- - `make test` also covers the Resource Inspector readable-layout Phase 0 contract: non-duplicating scope breadcrumbs; separate host, attributed, Other, diagnostic, list/detail, and contextual-action bands; primary identity separated from secondary context; vertically comparable CPU/Memory/count clusters; deterministic 80x24 and wide themed frame geometry without border bleed; en-US/ko-KR copy and explicit Unassigned/Shared stable-root parity; read-only pane detail without search, pointer, Enter, or sort affordances; explicit non-actionable Other/empty/vanished surfaces; populated/empty deferred action reconciliation without hidden sort; and resource-scoped directional hierarchy flow while collector, attribution, cadence, and client-scoped popup contracts remain unchanged.
164
+ - `make test` also covers the Resource Inspector Phase 1 vertical slice: immediate non-actionable warming chrome; Project → Window → Pane → read-only detail hierarchy with project/window `PANES n` and pane `PROCESSES n` projections; concrete-project and `No project match` drill-down plus the non-actionable Other summary; defensive Shared attribution omitted from project rows while its CPU/RSS/pane aggregate remains in Attributed totals and a bounded non-drillable Sample diagnostic; shared label → active-agent topic → interactive shell → raw-title pane identity precedence with reused `Process <PaneCommand>`/PID-SID/Pane %N/TTY secondary and detail naming; resource-only Right/Enter forward, Left back with root no-op, all-depth Esc close, and complete Alt-Left action/hint removal; statusbar-parity CPU 70/90 and memory 75/90 exact semantic boundaries with severity-word-free values, unavailable `--`, lifecycle text, and resolved default/custom theme roles; Name-default Tab-computed CPU/Memory/Name ordering that remains stable across automatic refresh; stable-id search metadata plus refresh selection/query/breadcrumb/focus preservation and vanished-row nearest-neighbor behavior; fresh/stale/partial/unavailable/empty/gone/overage transitions with atomic refresh-progress/footer updates; an exact fixed five-row divider/Host/Attributed/Coverage/Sample bottom summary dock that remains independent of scroll/search and separate from the action footer, with bounded diagnostics on Sample only, minimum navigable list height, and no divider/border bleed; changed-row synchronized differential frames at 80x24 and wide en-US/ko-KR layouts; truthful RSS-sum rendering; immediate automatic pre-collection not-fresh progress with the last complete sample retained, gated non-overlapping two-second and Ctrl-R sampling, cancellation, and prior-sample disposal; direct CLI and client-scoped popup mode; no-default `Resources:Open` catalog/Settings/config/custom-alias close behavior; statusbar `resources` range/click byte-equivalence, Labs visibility independence, other-client isolation, and existing `projmux internal status resources`/window-click fallback regression.
165
+ - `make test` also covers the Resource Inspector readable-layout Phase 0 contract: non-duplicating scope breadcrumbs; separate host, attributed, Other, diagnostic, list/detail, and contextual-action bands; primary identity separated from secondary context; vertically comparable CPU/Memory/count clusters; deterministic 80x24 and wide themed frame geometry without border bleed; en-US/ko-KR copy and concrete/Unassigned stable-root parity with no Shared root row; read-only pane detail without search, pointer, Enter, or sort affordances; explicit non-actionable Other/empty/vanished surfaces; populated/empty deferred action reconciliation without hidden sort; and resource-scoped directional hierarchy flow while collector, attribution, cadence, and client-scoped popup contracts remain unchanged.
161
166
  - Current focused unit coverage also includes strict notify SOT behavior
162
167
  (TTL does not remove rows, focus success and target-gone clicks ack,
163
168
  reconcile reports inactive `queue-stale` rows), `get notifications --live` queue/live explanations, notify sidebar
@@ -433,6 +433,13 @@ Runtime observation and resource status:
433
433
  never releases a name reservation** — the same preservation contract
434
434
  `MissingRoot` established for a Project whose root disappeared. There is no
435
435
  auto-prune.
436
+ - Explicit canonical deletion is the authority that retires that preserved
437
+ desired topology. A non-implicit `delete window` target (an explicit
438
+ reference or `--all`) accepts zero exact Window mirrors on its selected
439
+ socket as a Registry-only cascade through the Window's Agents and Panes. One
440
+ exact mirror is killed before the Registry commit; duplicate, foreign,
441
+ stale-owner, inventory-failure, and plan-to-execution race states remain
442
+ fail-closed. An implicit active Window is never treated as offline.
436
443
  - The inventory is a pure **read**. It never writes, re-mirrors, or adopts a uid
437
444
  onto a live tmux object; reattaching a lost binding belongs to the reconciler
438
445
  (see *Binding reapply and adoption* below). After a tmux server restart the
@@ -625,6 +632,52 @@ Public resource reconciliation:
625
632
  unchanged Project UID, and becomes a no-op after convergence. Unknown or
626
633
  duplicate Project UID claims remain refused.
627
634
 
635
+ Explicit Registry topology materialization:
636
+
637
+ - `reconcile resources --materialize-project <name|uid:uid>` selects exactly
638
+ one Registry Project and uses a separate pure plan. The default reconciliation
639
+ shadow never calls the materializer, and the materialization plan never runs
640
+ blank adoption, orphan minting, or Agent phase observation. Registry insertion
641
+ order determines session/Window/Window-owned shell Pane creation order; report
642
+ keys provide a separately stable rendering order.
643
+ - Registry presence is desired topology. Missing runtime sessions, Windows, and
644
+ Window-owned `role=shell` Panes are drift; canonical Registry deletion removes
645
+ that desire. Exact uid/name/owner mirrors are retained. Stored Pane CWD drives
646
+ only that Pane's detached runtime cwd, while Project root remains the session
647
+ path anchor and `PROJMUX_CWD` hook value. `Pane.spec.command`, Agent-owned
648
+ Panes, Agent providers, snapshots, notifications, and ephemeral sessions are
649
+ never execution inputs.
650
+ - Preflight rejects a missing/invalid root or Pane CWD, a zero-Window Project,
651
+ a primary ref that is not a direct Window-owned shell Pane, and foreign,
652
+ duplicate, wrong-owner, or ambiguous live claims before the first create.
653
+ Execute rechecks the same plan under the Registry lock. A server-wide uid
654
+ preflight runs first, *before* the selected Project session is created,
655
+ because creating it runs the public pre/post-create hooks whose side effects no
656
+ rollback can undo; a missing server is read as an empty inventory. The
657
+ inventory is then refreshed once the session exists, so the new tuple is
658
+ covered and any race since the preflight is caught. Together, and before the
659
+ first Window or Pane mutation, they prove that every planned live Window is
660
+ owned by the selected Session, every planned live Pane is owned by its planned
661
+ Window, and every planned Window/Pane uid is live on exactly the expected
662
+ handle or nowhere at all. UID equality alone is insufficient: a
663
+ relinked Window or a join-paned Pane keeps its uid, and a Window relinked out
664
+ of the selected Session is invisible to a selected-Session plan. Each created
665
+ Pane proves its own parent before its uid claim. It records only objects it
666
+ creates -- including one that tmux mutated before reporting a synchronous
667
+ hook failure -- and rolls those objects back in reverse order only while their
668
+ exact uid mirror still proves ownership. External hook effects are outside
669
+ that rollback guarantee. A successful repeat performs no session ensure,
670
+ lease write, tmux create, Registry replace, or mirror write.
671
+ - Exact `--socket/-L` supports offline full materialization with the existing
672
+ pre/post-create hook contract. Exact `--socket-path/-S` supports live partial
673
+ Window/Pane repair. Offline session creation through arbitrary `-S` is a
674
+ stable safety refusal: the public hook contract exposes name-only
675
+ `PROJMUX_SOCKET`, so an absolute path cannot be represented without either
676
+ silently routing hook re-entry to another server or changing that public
677
+ contract. A future versioned hook socket-path contract can lift the refusal.
678
+ Only the selected exact socket is claimed and mutated; sibling sockets are
679
+ tested unchanged, and no global uniqueness across unknown sockets is claimed.
680
+
628
681
  Agent runtime linkage:
629
682
 
630
683
  - Once a live tmux pane has settled on a registry Pane, reconcile decides which
package/docs/cli-guide.md CHANGED
@@ -71,13 +71,24 @@ The resource routes (`get`, `describe`, `create`, `rename`, `rebind`, `delete`,
71
71
  The grammar in one paragraph: a value is either `uid:<uid>` or a bare
72
72
  `metadata.name`. There is no bare-uid form, values are never split on commas,
73
73
  and a `displayName`, a `spec.root` path, or a raw tmux `%N`/`@N`/`$N` handle
74
- never resolves anything. `--project` occurs at most once and fixes the scope;
75
- `--window` and `--pane` repeat and union in argv order; `--selector key=value`
74
+ never resolves anything. `--project`/`-p` occurs at most once and fixes the
75
+ Project scope; `--window`/`-w` and `--pane` repeat and union in argv order;
76
+ `--selector key=value`
76
77
  repeats and ANDs. A singular route also accepts the target as a positional
77
78
  `<ref>`, which may appear before or after the flags. How many resolved targets
78
79
  each `<verb, kind>` pair accepts is a declared matrix, not a per-route rule, and
79
80
  a violation is exit `2` with a candidate listing bounded to five rows.
80
81
 
82
+ `-p` always means Project, never Pane; Pane has no shorthand. The three plural
83
+ reads `get windows|panes|agents` also accept `-A` as the exact alias of
84
+ `--all-projects`. That escape is mutually exclusive with either spelling of the
85
+ Project selector and is not registered on any other route. Long-only,
86
+ short-only, and mixed scope occurrences share the same value sinks, so repeated
87
+ Window order, cardinality, output, and mutation plans do not depend on spelling.
88
+ For resource-backed `create pane|agent|<provider>`, only aliases before the
89
+ first `--` are Projmux scope flags; every token after it, including `-p`, `-w`,
90
+ `--project`, and `--window`, remains opaque payload in its original order.
91
+
81
92
  ### Empty selector: the active tmux target
82
93
 
83
94
  Inside tmux, an invocation that carries **no selector at all** addresses the
@@ -100,7 +111,7 @@ The contract:
100
111
  --name current` succeeds — so a sentinel token would silently shadow a real
101
112
  resource.
102
113
  - **"No selector at all" means exactly that.** Any positional `<ref>`, any
103
- `--project`/`--window`/`--pane`, or any `--selector` label keeps the
114
+ `--project`/`-p`, `--window`/`-w`, `--pane`, or any `--selector` label keeps the
104
115
  pre-existing behavior unchanged. The fallback is never blended into a
105
116
  partially specified selector.
106
117
  - **Only the singular routes.** The plural reads (`get projects|windows|panes|
@@ -246,7 +257,7 @@ source, different output, different failure surface — both stay.
246
257
  ## reconcile resources
247
258
 
248
259
  ```text
249
- projmux reconcile resources [--dry-run] [--socket <name> | --socket-path <absolute>] [-o json]
260
+ projmux reconcile resources [--dry-run] [--materialize-project <name|uid:uid>] [--socket <name> | --socket-path <absolute>] [-o json]
250
261
  ```
251
262
 
252
263
  `reconcile resources` is the explicit repair boundary for drift between the
@@ -288,6 +299,42 @@ It does not run `config apply`, reload unrelated configuration, reconstruct a
288
299
  missing Registry, heuristically merge identities, or turn a read verb into a
289
300
  write transaction.
290
301
 
302
+ `--materialize-project <name|uid:uid>` is the explicit activation opt-in for
303
+ one exact Registry Project. Its separate pure plan treats Registry insertion
304
+ order as desired topology and reports a missing persistent session, each
305
+ missing Window, and each missing Window-owned `role=shell` Pane. Execute creates
306
+ only that missing subset on the selected socket, mirrors the existing Registry
307
+ UID/name/owner graph, preserves each Pane's stored CWD, and is a true no-op when
308
+ the graph is already live. The option is exact-one: repeating it is a usage
309
+ error, and the reported retry preserves both the selector and socket route.
310
+
311
+ Opening a closed Project is the other activation authority for that same
312
+ engine. The Alt-1 sidebar/`switch open` path materializes the selected Project's
313
+ declared shell topology on the app's own `-L projmux` socket and moves the
314
+ client only after it converges; a refusal, a failed preflight, or a rolled-back
315
+ partial leaves the client where it was and reports the exact stage. The
316
+ activation is pinned to the session the open targets, so a Project whose
317
+ 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,
326
+ every pre-existing uid-less Pane is refused rather than adopted; foreign,
327
+ duplicate, wrong-owner, or otherwise ambiguous UID state is refused before the
328
+ first create. Layout uses the existing deterministic right-axis equalizer and does
329
+ not promise historic geometry. Canonical Registry deletion removes desired
330
+ topology, so a deleted Window or Pane is not recreated; raw runtime loss while
331
+ the resource remains is drift and is materialized. A non-implicit offline
332
+ Window target (an explicit reference or `--all`) can be canonically deleted
333
+ Registry-only: its complete Agent/Pane cascade is shown under `--dry-run`, no
334
+ tmux object is killed, and unrelated live objects and sockets are untouched. A
335
+ unique live mirror keeps the exact-kill path, while duplicate, foreign,
336
+ stale-owner, inventory-failure, and revalidation-race states are refused.
337
+
291
338
  ## Internal plumbing (`projmux internal ...`)
292
339
 
293
340
  `internal` is a hidden namespace for the routes generated tmux config, tmux
@@ -545,8 +592,8 @@ back into the unredacted Doctor Go types.
545
592
 
546
593
  ```
547
594
  projmux focus project <ref> [--socket <path>] [--client <tty>] [--json]
548
- projmux focus window <ref> --project <ref> [--socket <path>] [--client <tty>] [--json]
549
- projmux focus pane <ref> --project <ref> --window <ref> [--socket <path>] [--json]
595
+ projmux focus window <ref> -p <project-ref> [--socket <path>] [--client <tty>] [--json]
596
+ projmux focus pane <ref> -p <project-ref> -w <window-ref> [--socket <path>] [--json]
550
597
  ```
551
598
 
552
599
  Moves one attached client to an exact live Project, Window, or Pane. It never
@@ -702,10 +749,12 @@ core-equivalent CPU. Project and window rows count panes, while pane rows count
702
749
  the attributed processes. Pane rows and detail share the resolved pane identity
703
750
  and label the tmux current command, PID/SID, pane id, and TTY separately. Memory
704
751
  is explicitly an RSS sum (shared pages can be counted more than once) plus its
705
- host ratio. `No project match`, `Multiple project matches`, and non-drillable
706
- `Other / unattributed` remain explicit. The first two are display labels over
707
- stable internal attribution keys. Warming, partial, unavailable, unknown, and
708
- overage states also remain explicit. No process
752
+ host ratio. Concrete projects and `No project match` are the only project
753
+ drill-down groups; non-drillable `Other / unattributed` remains explicit.
754
+ Defensive ambiguous attribution stays in its stable internal bucket, is
755
+ included in Attributed totals, and appears only as a bounded CPU/RSS/pane
756
+ diagnostic rather than a project row. Warming, partial, unavailable, unknown,
757
+ and overage states also remain explicit. No process
709
758
  command list, mutation, history, graph, daemon, persistence, or Session State
710
759
  telemetry is created. Linux/tmux provides attribution; unsupported platforms
711
760
  show an unavailable reason rather than zero metrics.
@@ -1368,11 +1417,14 @@ human configuration work should prefer `config render` and `config apply`.
1368
1417
  generated config. The generated app config uses absolute `$SHELL` as the
1369
1418
  tmux default shell when set, otherwise `/bin/sh`. `shell` starts or attaches
1370
1419
  the app session directly after resolving the target app session name and
1371
- startup directory. Alt-1 sidebar project open defaults to `Empty session`; the
1372
- Session State `Sidebar startup picker` opt-in shows `Latest snapshot`, `Named
1373
- snapshot`, and `Empty session` before creating a closed project session.
1374
- `Latest snapshot` is auto-saved; named snapshots are fixed until the user
1375
- saves or replaces them.
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.
1376
1428
  - `quit` — open an action picker with `Quit projmux` and `Cancel`. Selecting
1377
1429
  `Quit projmux` terminates only a `tmux -L projmux` runtime whose global
1378
1430
  `@projmux_app` option is set by the generated app config. Missing servers,
package/docs/cli.md CHANGED
@@ -64,7 +64,7 @@ Manage Agent state, topic, integrations, and account usage
64
64
  projmux agent status [get [<agent-ref>] | set <unknown|idle|in_progress|approval_required|input_required|response_complete> [<agent-ref>]] [--agent <ref>]
65
65
  projmux agent topic get|clear [<agent-ref>] [--agent <ref>]
66
66
  projmux agent topic set <text> [<agent-ref>] [--agent <ref>]
67
- projmux agent resume <ref> [--project <ref>] [--window <ref>]...
67
+ projmux agent resume <ref> [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]...
68
68
  projmux agent integrate <provider> [--dry-run]
69
69
  projmux agent usage [--model <name>] [--window <name>] [--json] [--force]
70
70
  ```
@@ -103,7 +103,7 @@ projmux agent topic set <text> [<agent-ref>] [--agent <ref>]
103
103
  Rebind an Offline or Failed Agent to a new managed Pane
104
104
 
105
105
  ```
106
- projmux agent resume <ref> [--project <ref>] [--window <ref>]... [--selector key=value]...
106
+ projmux agent resume <ref> [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--selector key=value]...
107
107
  ```
108
108
 
109
109
  ### `projmux agent integrate`
@@ -283,10 +283,10 @@ projmux config apply [--bin <path>] [--config <path>] [--socket <name>]
283
283
  Create Projmux resources
284
284
 
285
285
  ```
286
- projmux create window --project <ref> [--name <name>] [--label key=value]... [-o <mode>] [-- <payload>]
287
- projmux create pane --project <ref> [--window <ref>]... [--pane <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
288
- projmux create agent --provider <provider> --project <ref> [--cwd <path>] [--add-dir <path>]... [--window <ref>]... [--pane <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
289
- projmux create codex|claude|antigravity --project <ref> [--cwd <path>] [--add-dir <path>]... [--window <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
286
+ projmux create window {--project <ref> | -p <ref>} [--name <name>] [--label key=value]... [-o <mode>] [-- <payload>]
287
+ projmux create pane {--project <ref> | -p <ref>} [--window <ref> | -w <ref>]... [--pane <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
288
+ projmux create agent --provider <provider> {--project <ref> | -p <ref>} [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
289
+ projmux create codex|claude|antigravity {--project <ref> | -p <ref>} [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
290
290
  projmux create notification --text <s> --target <SESSION[:WINDOW[.PANE]]> [--socket <s>]
291
291
  projmux create snapshot
292
292
  ```
@@ -316,7 +316,7 @@ Canonical spelling: `projmux create window`, `projmux create pane`, `projmux cre
316
316
  Create a Window and its initial Pane below one Project; the runtime is materialized detached
317
317
 
318
318
  ```
319
- projmux create window --project <ref> [--name <name>] [--label key=value]... [-o <mode>] [-- <payload>]
319
+ projmux create window {--project <ref> | -p <ref>} [--name <name>] [--label key=value]... [-o <mode>] [-- <payload>]
320
320
  ```
321
321
 
322
322
  Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`
@@ -326,7 +326,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`
326
326
  Create a shell Pane; --project splits the resolved Windows detached, without it the current Window
327
327
 
328
328
  ```
329
- projmux create pane --project <ref> [--window <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
329
+ projmux create pane {--project <ref> | -p <ref>} [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
330
330
  projmux create pane [--placement right|down] [-o <mode>]
331
331
  ```
332
332
 
@@ -337,7 +337,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`
337
337
  Create an Agent and its managed Pane; --provider is required, and --project splits the resolved Windows detached
338
338
 
339
339
  ```
340
- projmux create agent --provider <provider> --project <ref> [--cwd <path>] [--add-dir <path>]... [--window <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
340
+ projmux create agent --provider <provider> {--project <ref> | -p <ref>} [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
341
341
  projmux create agent --provider <provider> [--placement right|down] [-o pane-id|none] [-- <payload>]
342
342
  ```
343
343
 
@@ -364,7 +364,7 @@ projmux create snapshot
364
364
  Provider shortcut for create agent --provider codex
365
365
 
366
366
  ```
367
- projmux create codex --project <ref> [--cwd <path>] [--add-dir <path>]... [--window <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
367
+ projmux create codex {--project <ref> | -p <ref>} [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
368
368
  projmux create codex [--placement right|down] [-o pane-id|none] [-- <payload>]
369
369
  ```
370
370
 
@@ -375,7 +375,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`
375
375
  Provider shortcut for create agent --provider claude
376
376
 
377
377
  ```
378
- projmux create claude --project <ref> [--cwd <path>] [--add-dir <path>]... [--window <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
378
+ projmux create claude {--project <ref> | -p <ref>} [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
379
379
  projmux create claude [--placement right|down] [-o pane-id|none] [-- <payload>]
380
380
  ```
381
381
 
@@ -386,7 +386,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`
386
386
  Provider shortcut for create agent --provider antigravity
387
387
 
388
388
  ```
389
- projmux create antigravity --project <ref> [--cwd <path>] [--add-dir <path>]... [--window <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
389
+ projmux create antigravity {--project <ref> | -p <ref>} [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
390
390
  projmux create antigravity [--placement right|down] [-o pane-id|none] [-- <payload>]
391
391
  ```
392
392
 
@@ -397,16 +397,16 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`
397
397
  Delete Projmux resources with an explicit cascade plan
398
398
 
399
399
  ```
400
- projmux delete window [<ref>...] [--project <ref>] [--selector key=value]... [--all] [--dry-run] [--yes]
401
- projmux delete pane [<ref>...] [--project <ref>] [--window <ref>]... [--all] [--dry-run] [--yes]
402
- projmux delete agent [<ref>...] [--project <ref>] [--window <ref>]... [--all] [--dry-run] [--yes]
400
+ projmux delete window [<ref>...] [--project <ref> | -p <ref>] [--selector key=value]... [--all] [--dry-run] [--yes]
401
+ projmux delete pane [<ref>...] [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--all] [--dry-run] [--yes]
402
+ projmux delete agent [<ref>...] [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--all] [--dry-run] [--yes]
403
403
  ```
404
404
 
405
405
  Subcommands:
406
406
 
407
407
  | Route | Summary |
408
408
  | --- | --- |
409
- | [`projmux delete window`](#projmux-delete-window) | Delete exact live tmux Windows and every descendant Agent and Pane; no selector inside tmux means the active Window, and --all means every Window in the registry |
409
+ | [`projmux delete window`](#projmux-delete-window) | Delete Registry Windows and every descendant Agent and Pane, killing an exact live tmux mirror when present; no selector inside tmux means the active Window, and --all means every Window in the registry |
410
410
  | [`projmux delete pane`](#projmux-delete-pane) | Delete Panes; an Agent-owned current Pane leaves its Agent Offline; no selector inside tmux means the active Pane, and --all means every Pane in the registry |
411
411
  | [`projmux delete agent`](#projmux-delete-agent) | Delete Agents and their managed Panes; no selector inside tmux means the active Agent, and --all means every Agent in the registry |
412
412
  | [`projmux delete notification`](#projmux-delete-notification) | Delete pending notification rows |
@@ -416,10 +416,10 @@ Canonical spelling: `projmux delete window`, `projmux delete pane`, `projmux del
416
416
 
417
417
  ### `projmux delete window`
418
418
 
419
- Delete exact live tmux Windows and every descendant Agent and Pane; no selector inside tmux means the active Window, and --all means every Window in the registry
419
+ Delete Registry Windows and every descendant Agent and Pane, killing an exact live tmux mirror when present; no selector inside tmux means the active Window, and --all means every Window in the registry
420
420
 
421
421
  ```
422
- projmux delete window [<ref>...] [--project <ref>] [--selector key=value]... [--all] [--dry-run] [--yes]
422
+ projmux delete window [<ref>...] [--project <ref> | -p <ref>] [--selector key=value]... [--all] [--dry-run] [--yes]
423
423
  ```
424
424
 
425
425
  Aliases: `windows`
@@ -429,7 +429,7 @@ Aliases: `windows`
429
429
  Delete Panes; an Agent-owned current Pane leaves its Agent Offline; no selector inside tmux means the active Pane, and --all means every Pane in the registry
430
430
 
431
431
  ```
432
- projmux delete pane [<ref>...] [--project <ref>] [--window <ref>]... [--all] [--dry-run] [--yes]
432
+ projmux delete pane [<ref>...] [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--all] [--dry-run] [--yes]
433
433
  ```
434
434
 
435
435
  Aliases: `panes`
@@ -439,7 +439,7 @@ Aliases: `panes`
439
439
  Delete Agents and their managed Panes; no selector inside tmux means the active Agent, and --all means every Agent in the registry
440
440
 
441
441
  ```
442
- projmux delete agent [<ref>...] [--project <ref>] [--window <ref>]... [--all] [--dry-run] [--yes]
442
+ projmux delete agent [<ref>...] [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--all] [--dry-run] [--yes]
443
443
  ```
444
444
 
445
445
  Aliases: `agents`
@@ -469,10 +469,10 @@ Aliases: `snapshots`
469
469
  Describe one Projmux resource
470
470
 
471
471
  ```
472
- projmux describe project [<ref>] [-o <mode>]
473
- projmux describe window [<ref>] [--project <ref>] [-o <mode>]
474
- projmux describe pane [<ref>] [--project <ref>] [--window <ref>]... [-o <mode>]
475
- projmux describe agent [<ref>] [--project <ref>] [--window <ref>]... [-o <mode>]
472
+ projmux describe project [<ref>] [--project <ref> | -p <ref>] [-o <mode>]
473
+ projmux describe window [<ref>] [--project <ref> | -p <ref>] [-o <mode>]
474
+ projmux describe pane [<ref>] [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [-o <mode>]
475
+ projmux describe agent [<ref>] [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [-o <mode>]
476
476
  ```
477
477
 
478
478
  Subcommands:
@@ -491,7 +491,7 @@ Canonical spelling: `projmux describe project`, `projmux describe window`, `proj
491
491
  Describe one Project resource; with no selector inside tmux, the active Project
492
492
 
493
493
  ```
494
- projmux describe project
494
+ projmux describe project [<ref>] [--project <ref> | -p <ref>] [-o <mode>]
495
495
  ```
496
496
 
497
497
  Aliases: `projects`
@@ -503,7 +503,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `none`
503
503
  Describe one Window resource; with no selector inside tmux, the active Window
504
504
 
505
505
  ```
506
- projmux describe window
506
+ projmux describe window [<ref>] [--project <ref> | -p <ref>] [-o <mode>]
507
507
  ```
508
508
 
509
509
  Aliases: `windows`
@@ -515,7 +515,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `none`
515
515
  Describe one Pane resource; with no selector inside tmux, the active Pane
516
516
 
517
517
  ```
518
- projmux describe pane
518
+ projmux describe pane [<ref>] [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [-o <mode>]
519
519
  ```
520
520
 
521
521
  Aliases: `panes`
@@ -527,7 +527,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `none`
527
527
  Describe one Agent resource; with no selector inside tmux, the Agent owning the active Pane
528
528
 
529
529
  ```
530
- projmux describe agent
530
+ projmux describe agent [<ref>] [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [-o <mode>]
531
531
  ```
532
532
 
533
533
  Aliases: `agents`
@@ -592,8 +592,8 @@ Move the current client to a live resource
592
592
 
593
593
  ```
594
594
  projmux focus project <ref>
595
- projmux focus window <ref> --project <ref>
596
- projmux focus pane <ref> --project <ref> --window <ref>
595
+ projmux focus window <ref> {--project <ref> | -p <ref>}
596
+ projmux focus pane <ref> {--project <ref> | -p <ref>} {--window <ref> | -w <ref>}
597
597
  ```
598
598
 
599
599
  Subcommands:
@@ -619,7 +619,7 @@ projmux focus project <ref> [--socket <path>] [--client <tty>] [--json]
619
619
  Move the current client to an already-live Window; never materializes
620
620
 
621
621
  ```
622
- projmux focus window <ref> --project <ref> [--socket <path>] [--client <tty>] [--json]
622
+ projmux focus window <ref> {--project <ref> | -p <ref>} [--socket <path>] [--client <tty>] [--json]
623
623
  ```
624
624
 
625
625
  ### `projmux focus pane`
@@ -627,7 +627,7 @@ projmux focus window <ref> --project <ref> [--socket <path>] [--client <tty>] [-
627
627
  Move the current client to an already-live Pane; never materializes
628
628
 
629
629
  ```
630
- projmux focus pane <ref> --project <ref> --window <ref> [--socket <path>] [--json]
630
+ projmux focus pane <ref> {--project <ref> | -p <ref>} {--window <ref> | -w <ref>} [--socket <path>] [--json]
631
631
  ```
632
632
 
633
633
  ## `projmux get`
@@ -635,12 +635,12 @@ projmux focus pane <ref> --project <ref> --window <ref> [--socket <path>] [--jso
635
635
  Read Projmux resources by selector
636
636
 
637
637
  ```
638
- projmux get projects [--project <ref>] [--selector key=value]... [-o <mode>]
639
- projmux get windows [--project <ref>] [--window <ref>]... [--selector key=value]... [--all-projects] [-o <mode>]
640
- projmux get panes [--project <ref>] [--window <ref>]... [--pane <ref>]... [--selector key=value]... [--all-projects] [-o <mode>]
641
- projmux get agents [--project <ref>] [--window <ref>]... [--selector key=value]... [--all-projects] [-o <mode>]
638
+ projmux get projects [--project <ref> | -p <ref>] [--selector key=value]... [-o <mode>]
639
+ projmux get windows [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--selector key=value]... [--all-projects | -A] [-o <mode>]
640
+ projmux get panes [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--all-projects | -A] [-o <mode>]
641
+ projmux get agents [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--selector key=value]... [--all-projects | -A] [-o <mode>]
642
642
  projmux get pane --current -o cwd
643
- projmux get pane [--project <ref>] [--window <ref>]... [--pane <ref>]... [--selector key=value]... [-o <mode>]
643
+ projmux get pane [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [-o <mode>]
644
644
  ```
645
645
 
646
646
  Subcommands:
@@ -662,7 +662,7 @@ Canonical spelling: `projmux get projects`, `projmux get windows`, `projmux get
662
662
  List Project resources
663
663
 
664
664
  ```
665
- projmux get projects
665
+ projmux get projects [--project <ref> | -p <ref>] [--selector key=value]... [-o <mode>]
666
666
  ```
667
667
 
668
668
  Aliases: `project`
@@ -674,7 +674,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `none`
674
674
  List Window resources; inside tmux defaults to the active Project, and --all-projects lists every Project
675
675
 
676
676
  ```
677
- projmux get windows [--project <ref>] [--window <ref>]... [--selector key=value]... [--all-projects] [-o <mode>]
677
+ projmux get windows [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--selector key=value]... [--all-projects | -A] [-o <mode>]
678
678
  ```
679
679
 
680
680
  Aliases: `window`
@@ -686,7 +686,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `none`
686
686
  List Pane resources; inside tmux defaults to the active Project, and --all-projects lists every Project
687
687
 
688
688
  ```
689
- projmux get panes [--project <ref>] [--window <ref>]... [--pane <ref>]... [--selector key=value]... [--all-projects] [-o <mode>]
689
+ projmux get panes [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--all-projects | -A] [-o <mode>]
690
690
  ```
691
691
 
692
692
  Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `none`
@@ -696,7 +696,7 @@ Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `none`
696
696
  List Agent resources; inside tmux defaults to the active Project, and --all-projects lists every Project
697
697
 
698
698
  ```
699
- projmux get agents [--project <ref>] [--window <ref>]... [--selector key=value]... [--all-projects] [-o <mode>]
699
+ projmux get agents [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--selector key=value]... [--all-projects | -A] [-o <mode>]
700
700
  ```
701
701
 
702
702
  Aliases: `agent`
@@ -728,7 +728,7 @@ Aliases: `snapshot`
728
728
  Read one Pane resource; with no selector inside tmux, the active Pane
729
729
 
730
730
  ```
731
- projmux get pane [--current] [--project <ref>] [--window <ref>]... [--pane <ref>]... [--selector key=value]... [-o <mode>]
731
+ projmux get pane [--current] [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [-o <mode>]
732
732
  ```
733
733
 
734
734
  Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `none`
@@ -903,7 +903,7 @@ projmux quit [--yes|--force]
903
903
  Preview or repair Registry and exact tmux resource drift
904
904
 
905
905
  ```
906
- projmux reconcile resources [--dry-run] [--socket <name> | --socket-path <absolute>] [-o json]
906
+ projmux reconcile resources [--dry-run] [--materialize-project <name|uid:uid>] [--socket <name> | --socket-path <absolute>] [-o json]
907
907
  ```
908
908
 
909
909
  Subcommands:
@@ -919,7 +919,7 @@ Canonical spelling: `projmux reconcile resources`
919
919
  Preview or repair safe Registry and tmux UID, owner, and runtime drift on one exact socket
920
920
 
921
921
  ```
922
- projmux reconcile resources [--dry-run] [--socket <name> | --socket-path <absolute>] [-o json]
922
+ projmux reconcile resources [--dry-run] [--materialize-project <name|uid:uid>] [--socket <name> | --socket-path <absolute>] [-o json]
923
923
  ```
924
924
 
925
925
  ## `projmux rebind`
@@ -927,7 +927,7 @@ projmux reconcile resources [--dry-run] [--socket <name> | --socket-path <absolu
927
927
  Rebind a Project to a new absolute root without moving files
928
928
 
929
929
  ```
930
- projmux rebind project [<ref>] --root <absolute-path>
930
+ projmux rebind project [<ref>] [--project <ref> | -p <ref>] --root <absolute-path>
931
931
  ```
932
932
 
933
933
  Subcommands:
@@ -943,7 +943,7 @@ Canonical spelling: `projmux rebind project`
943
943
  Rewrite one Project spec.root; no filesystem move, no heuristic uid merge
944
944
 
945
945
  ```
946
- projmux rebind project [<ref>] --root <absolute-path>
946
+ projmux rebind project [<ref>] [--project <ref> | -p <ref>] --root <absolute-path>
947
947
  ```
948
948
 
949
949
  ## `projmux rename`
@@ -951,10 +951,10 @@ projmux rebind project [<ref>] --root <absolute-path>
951
951
  Rename a Projmux resource metadata.name
952
952
 
953
953
  ```
954
- projmux rename project [<ref>] --name <name>
955
- projmux rename window [<ref>] --name <name> [--project <ref>]
956
- projmux rename pane [<ref>] --name <name> [--project <ref>] [--window <ref>]...
957
- projmux rename agent [<ref>] --name <name> [--project <ref>] [--window <ref>]...
954
+ projmux rename project [<ref>] [--project <ref> | -p <ref>] --name <name>
955
+ projmux rename window [<ref>] --name <name> [--project <ref> | -p <ref>]
956
+ projmux rename pane [<ref>] --name <name> [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]...
957
+ projmux rename agent [<ref>] --name <name> [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]...
958
958
  ```
959
959
 
960
960
  Subcommands:
@@ -973,7 +973,7 @@ Canonical spelling: `projmux rename project`, `projmux rename window`, `projmux
973
973
  Rename a Projmux Project resource; with no selector inside tmux, the active Project
974
974
 
975
975
  ```
976
- projmux rename project
976
+ projmux rename project [<ref>] [--project <ref> | -p <ref>] --name <name>
977
977
  ```
978
978
 
979
979
  Aliases: `projects`
@@ -983,7 +983,7 @@ Aliases: `projects`
983
983
  Rename a Projmux Window resource; with no selector inside tmux, the active Window
984
984
 
985
985
  ```
986
- projmux rename window
986
+ projmux rename window [<ref>] --name <name> [--project <ref> | -p <ref>]
987
987
  ```
988
988
 
989
989
  Aliases: `windows`
@@ -993,7 +993,7 @@ Aliases: `windows`
993
993
  Rename a Projmux Pane resource; with no selector inside tmux, the active Pane; does not change tmux pane_title
994
994
 
995
995
  ```
996
- projmux rename pane
996
+ projmux rename pane [<ref>] --name <name> [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]...
997
997
  ```
998
998
 
999
999
  Aliases: `panes`
@@ -1003,7 +1003,7 @@ Aliases: `panes`
1003
1003
  Rename an Agent stable resource name without changing its topic, provider, or managed Pane
1004
1004
 
1005
1005
  ```
1006
- projmux rename agent
1006
+ projmux rename agent [<ref>] --name <name> [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]...
1007
1007
  ```
1008
1008
 
1009
1009
  Aliases: `agents`
@@ -699,10 +699,12 @@ override with `inherit`, `on`, and `off`; `inherit` follows the global value,
699
699
  while `on` and `off` take precedence. Auto-save only updates the latest
700
700
  snapshot. Named snapshots are manual and are never updated by auto-save.
701
701
 
702
- Project open from the Alt-1 sidebar defaults to opening a closed project as an
703
- `Empty session`. The optional `Settings > Session State > Sidebar startup
704
- picker` toggle enables the native sidebar `Start project` step. Rows appear as `Latest
705
- snapshot`, named snapshot rows, `Empty session`, and `Back`. `Latest snapshot`
702
+ Project open from the Alt-1 sidebar defaults to opening a closed project as its
703
+ `Project topology`, which materializes every Registry Window and Window-owned
704
+ shell Pane of that project before the client moves. The optional `Settings >
705
+ Session State > Sidebar startup picker` toggle enables the native sidebar `Start
706
+ project` step. Rows appear as `Latest snapshot`, named snapshot rows, `Project
707
+ topology`, and `Back`. `Latest snapshot`
706
708
  is the snapshot auto-save that changes as auto-save runs; named snapshots are
707
709
  fixed snapshots. Rows include saved-at date/time metadata when projmux can
708
710
  determine it. `Back` returns to the project list without creating, replaying, or
@@ -161,6 +161,13 @@ at every depth. Unsupported platforms show an unavailable reason, not zero
161
161
  metrics. PSS, non-Linux collectors, process-list drill-down, history, and
162
162
  resource mutation remain outside this contract.
163
163
 
164
+ The project root exposes only concrete project paths and `No project match` as
165
+ drill-down groups. `ProjectShared` remains a defensive core attribution bucket,
166
+ but it is never projected as a project row, even when a snapshot contains
167
+ ambiguous panes or windows. Those CPU, RSS, and pane aggregates remain included
168
+ in Attributed totals and appear only as a bounded, non-drillable Sample
169
+ diagnostic. They are neither dropped nor reassigned to a concrete project.
170
+
164
171
  Host and attributed CPU/memory use the same semantic classifier as the live
165
172
  statusbar: CPU is normal below 70%, warning at 70–89.9%, and critical at 90%
166
173
  or above; memory is normal below 75%, warning at 75–89.9%, and critical at 90%
@@ -197,9 +204,9 @@ boundary, so diagnostic values and key hints never share a role. The 80x24
197
204
  layout retains a navigable list viewport without clipping, border bleed, or a
198
205
  second dock divider.
199
206
 
200
- Project rows label project paths explicitly. The two attribution buckets keep
201
- their stable core keys but display `No project match` and `Multiple project
202
- matches` with bounded explanations. Pane primary identity follows the shared
203
- label → agent-only AI topic → interactive shell → raw title resolver; pane id,
207
+ Project rows label project paths explicitly, and the unassigned attribution
208
+ bucket keeps its stable core key while displaying `No project match` with a
209
+ bounded explanation. Pane primary identity follows the shared label →
210
+ agent-only AI topic → interactive shell → raw title resolver; pane id,
204
211
  process id, and TTY remain labeled secondary details and stable keys are
205
212
  unchanged.
@@ -97,16 +97,23 @@ existing or non-empty live sessions.
97
97
  Named snapshots may currently be backed by legacy project files in
98
98
  `<project>/.projmux/layouts/*.toml`. They reuse the same window, pane, cwd, and
99
99
  startup recipe concepts as session snapshots, but the user-facing restore model
100
- is still `Latest snapshot`, `Named snapshot`, or `Empty session`. The legacy
100
+ is still `Latest snapshot`, `Named snapshot`, or `Project topology`. The legacy
101
101
  files are imported read-only by Project open
102
102
  when building `Named snapshot` candidates; new primary surfaces should describe
103
103
  the restore unit as a snapshot, not as a separate layout or preset feature.
104
104
 
105
- Project open from the Alt-1 sidebar defaults to opening a closed project as an
106
- `Empty session`. `Settings > Session State > Sidebar startup picker` is an opt-in toggle;
105
+ Project open from the Alt-1 sidebar defaults to opening a closed project as its
106
+ `Project topology`: the same explicit Registry materialization engine the public
107
+ `reconcile resources --materialize-project` route uses rebuilds every Registry
108
+ Window and Window-owned shell Pane under their existing uids, and the client
109
+ moves only after that converges. A refusal or failure reports the exact stage and
110
+ leaves the client where it was. A directory with no Registry Project, and a
111
+ Project with no Registry Window, still start as a single default session.
112
+ `Settings > Session State > Sidebar startup picker` is an opt-in toggle;
107
113
  when it is on, closed project open advances inside the sidebar to the native
108
114
  `Start project` step. Rows are ordered `Latest snapshot`, named snapshot rows,
109
- `Empty session`, then `Back`. `Latest snapshot` is the auto-saved snapshot that
115
+ `Project topology`, then `Back`; a snapshot row stays entirely on the snapshot
116
+ engine, so the two sources are never mixed in one open. `Latest snapshot` is the auto-saved snapshot that
110
117
  keeps changing as auto-save runs. `Named snapshot` is a fixed, user-named
111
118
  snapshot and is not updated by auto-save. Rows include saved-at date/time
112
119
  metadata when available. `Back` returns to the project list without creating,
@@ -136,7 +143,7 @@ Default `projmux shell` no longer opens a compatibility startup picker and no
136
143
  longer accepts startup selector flags for snapshot restore. It always
137
144
  follows the normal empty attach path after resolving the target app session name
138
145
  and startup directory. Use `Settings > Session State > Sidebar startup picker` for
139
- interactive Latest snapshot / Named snapshot / Empty session selection.
146
+ interactive Latest snapshot / Named snapshot / Project topology selection.
140
147
 
141
148
  ## Operational diagnostics
142
149
 
@@ -46,8 +46,15 @@ step, never a silent no-op.
46
46
  calls the canonical `rebind project` route and keeps the same uid. Settings
47
47
  performs no heuristic uid merge and no automatic prune.
48
48
  - `Project Sidebar [View]` — `Closed Project startup` chooses between using
49
- the stored Project topology and asking for a Snapshot. The saved file keeps
50
- its `sidebar-startup-picker` spelling.
49
+ the stored Project topology and asking for a Snapshot. `Use Project
50
+ topology` materializes every Registry Window and Window-owned shell Pane of
51
+ that Project under their existing uids and only then moves the client; it is
52
+ not an empty session, and the startup picker names that row `Project
53
+ topology` for the same reason. `Ask for Snapshot or Project topology` adds
54
+ the `Latest snapshot` and `Named snapshot` rows, which stay on the Session
55
+ State snapshot engine — the two sources are never mixed in one open. Neither
56
+ choice resumes an Agent or executes a stored `Pane.spec.command`. The saved
57
+ file keeps its `sidebar-startup-picker` spelling.
51
58
  - **AI** — `AI` is a product category, never an addressable resource.
52
59
  - `Default launch target [Choice]` — an Agent Provider, a Shell Pane, or
53
60
  choose-at-launch. It is a keybinding/picker preference and does not weaken
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projmux",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "tmux project session manager",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/crevissepartners/projmux#readme",
@@ -28,9 +28,9 @@
28
28
  "package:npm:pack": "scripts/package-npm.sh --pack"
29
29
  },
30
30
  "optionalDependencies": {
31
- "@projmux/linux-x64": "0.12.1",
32
- "@projmux/linux-arm64": "0.12.1",
33
- "@projmux/darwin-x64": "0.12.1",
34
- "@projmux/darwin-arm64": "0.12.1"
31
+ "@projmux/linux-x64": "0.12.2",
32
+ "@projmux/linux-arm64": "0.12.2",
33
+ "@projmux/darwin-x64": "0.12.2",
34
+ "@projmux/darwin-arm64": "0.12.2"
35
35
  }
36
36
  }