projmux 0.13.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/docs/agent-workflow.md +96 -2
- package/docs/cli-guide.md +78 -17
- package/docs/cli.md +262 -3
- package/docs/configuration.md +36 -13
- package/docs/install.md +23 -7
- package/docs/keybindings.md +3 -1
- package/docs/testing.md +13 -6
- package/docs/troubleshooting.md +157 -0
- package/docs/upgrading.md +23 -6
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -55,6 +55,8 @@ distribution path for normal users.
|
|
|
55
55
|
|
|
56
56
|
Verify with `projmux version`. Then `projmux doctor` provides read-only local
|
|
57
57
|
runtime diagnostics (tmux 3.4+ and hook integration health).
|
|
58
|
+
If installation succeeds but the app cannot enter or npm reports an incomplete
|
|
59
|
+
optional dependency, follow [Troubleshooting](docs/troubleshooting.md).
|
|
58
60
|
|
|
59
61
|
Manual Go, source checkout, GitHub Release, and packaging details live in
|
|
60
62
|
[Install](docs/install.md).
|
|
@@ -151,6 +153,7 @@ Templates and naming conventions for Claude, Codex, and other agents are in
|
|
|
151
153
|
- [Resource Inspector](docs/resource-attribution.md)
|
|
152
154
|
- [Session State](docs/session-restore.md)
|
|
153
155
|
- [Operational Diagnostics](docs/operational-diagnostics.md)
|
|
156
|
+
- [Troubleshooting](docs/troubleshooting.md)
|
|
154
157
|
- [Agent Workflow](docs/agent-workflow.md)
|
|
155
158
|
|
|
156
159
|
## Development
|
package/docs/agent-workflow.md
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
## Maintained Test List
|
|
29
29
|
|
|
30
|
+
- `make test` / `make test-integration`: Phase 1 explicit-create route correction preserves inherited app-owned socket/PID evidence while excluding unrelated `TMUX_PANE` containment. `TestExactProjectCreateWindowUsesAppRouteDespiteStaleInheritedPane` pins a nondefault logical `-L` alias resolved through the exact inherited `-S` path, ownership/logical markers, and PID; `TestExactProjectCreateWindowIsByteEquivalentAcrossAmbientPanes` requires byte-equivalent results and tmux call ledgers across current and stale ambient Panes; and `TestExactProjectCreateWindowRejectsInheritedAppDriftBeforeWrite` keeps stale PID and foreign marker at zero Registry/runtime writes. Outside tmux retains validated default app `-L`, standalone inherited routes still require exact Pane containment, and the discovery integration fixture keeps its custom app logical socket without requiring `env -u`.
|
|
30
31
|
- `make test` / `make test-integration` / `make test-e2e`: Window anchor and primary-shell separation Phase 0 finalizes the unpublished Registry v2 Window shape without consuming a new schema number. `TestFinalV2AnchorAndDefaultShellValidationMatrix`, `TestSchemaVersion2WindowShapeNormalizationTable`, and the deterministic random-graph properties cover required same-Window shell or managed-Agent `anchorPaneRef`, optional direct-shell `defaultShellPaneRef`, mixed legacy/final refusal, v1 and intermediate-v2 deterministic normalization, final-writer legacy-field zero, and second-pass idempotence. `TestIntermediateV2NormalizationPublishesExactEvidenceAndSecondPassIsZeroByte`, `TestIntermediateV2NormalizationFailureHonorsEvidencePublicationBoundary`, and `TestPrivateEvidencePublicationIsExclusive` pin the exclusive exact mode-0600 backup, checksum/repair report, pre-publication cleanup, post-publication evidence preservation, pre-replace source-byte preservation, and final-v2 zero-byte pass. Existing create, materialize, resume, focus, and real-tmux outcomes stay at Phase-0 parity through the pure compatibility shell resolver; the E2E stale-anchor fixture now corrupts `anchorPaneRef` and keeps its isolated zero-write refusal.
|
|
31
32
|
- `make test` / `make test-integration` / `make test-e2e`: Window anchor and primary-shell separation Phase 2 closes the role/live consumer table. `TestAnchorAwareCreatePaneAndAgentUseExactLiveShellAnchorDetached`, `TestAnchorAwareCreatePaneAndAgentUseExactLiveAgentAnchorDetached`, `TestAnchorAwareCreatePaneAdoptsNewShellAsEmptyDefaultWithoutReplacingShellAnchor`, the paired resume tests, and the explicit-Pane/popup precedence tests pin detached exact-Window outcomes and create-time default-shell adoption. `TestAnchorAwareCreateRefusesDeadAndCrossWindowAnchorsWithZeroWrites` and `TestRegistryTopologyMaterializationRefusesDeadAnchorWithoutAlternateLivePaneInference` prohibit alternate-live-Pane inference. `TestRegistryTopologyMaterializationAgentAnchorLazyDefaultShellAndRepeatNoop` plus the installed isolated real-tmux Agent-only Window fixture pin the plan-visible lazy shell, preserved Agent anchor/Pane uid, exact default owner/ref graph, smoke-root socket containment, and repeat no-op. `TestFocusKindOnAnOfflineTargetExitsTwoAndMaterializesNothing`, `TestFocusKindMovesTheClientToAnAlreadyLiveTarget`, and `TestControlOwnedRegistryKeepsMaterializeGreen` cover strict read/navigation-only focus and the Project/ControlSession root boundary.
|
|
32
33
|
- `make test` / `make test-integration` / `make test-e2e`: Window anchor and primary-shell separation Phase 3 keeps canonical Pane/Agent delete and clean exit independent from explicit Window deletion. Registry-order sibling choice, optional-empty default shell, Offline Agent retention, and same Window UID/name remain pinned for non-last Pane deletion. Project runtime stop Phase 1 supersedes the old last-Pane replacement boundary: an exact clean last-Pane receipt waits for its matching `window-unlinked`, then deletes only the Window subtree with zero replacement allocation. Explicit `delete window --yes` uses the same root-preserving shape: a non-last primary reanchors to its existing sibling and a last Window leaves the Project uid/root/reservation with empty `primaryWindowRef`; only explicit `delete project` unregisters the root. Unknown, killed, unpaired, foreign, stale, missing-server, and empty observations retain zero Window-delete authority.
|
|
@@ -35,9 +36,13 @@
|
|
|
35
36
|
- `make test` / `make test-integration` / `make test-e2e`: Codex native control plane Phase 7 admits only bounded scalar progress from the exact current app-server turn. The adapter drops plan explanation/step text, diff bytes and paths after a 256 KiB header-only scan, and every item body while mapping only the installed-schema discriminator into the ten-value activity set. Unit and fake-clock properties pin 99 plan steps, 999 changed-file headers, 32 opaque dedupe ids, deterministic dropped/unknown/overflow counters, identical/out-of-order write zero, and at most four non-terminal Registry writes per Agent per second. Adapter-to-reducer-to-Registry/read/UI tests pin exact activation turn attribution, KO/EN 80/100/120-cell rendering, read-time-only Window counts, and immediate zero-history clear on completed/interrupted/failed turn, generation replacement, disconnect/invalidation, Offline, and Failed; unavailable native progress leaves the existing interaction UI unchanged. Privacy inventories reject prompt, reasoning, step, command/tool/file/path/name/output, diff body/hash, model, and effort fields across the reducer input, durable Agent status, describe/Inspector projection, tmux options, and diagnostics.
|
|
36
37
|
- `make test`: Codex native control plane Phase 0 keeps the app-server wire in `internal/integrations/agents/codexappserver` and pins the headerless JSON-RPC JSONL frame bound, monotonic request IDs, out-of-order response routing, initialize/initialized lifecycle, typed notification delivery, context-bounded request and initialized-notification writes, local cancellation with ignored late responses, and connection replacement with no goroutine leak. A fake stdio proxy matrix closes healthy, missing/disconnected, unsupported `-32601`, malformed, and timeout outcomes into exactly one of available/unsupported/unavailable/timeout/protocol-error, while Doctor, support-report redaction, and the Settings no-op state row expose only source/reason/version/endpoint-kind/connection-state. Negative source and argv audits keep daemon mutation, login, config writes, remote transport, all existing Agent/hook/resume/usage consumers, giant provider interfaces, and Settings source controls at zero.
|
|
37
38
|
- `make test`: Codex native control plane Phase 1 pins the closed trigger × probe × start × readiness lifecycle table: Doctor, Settings, and support reports repeat with zero daemon mutation; only a native user action whose official local socket is missing or refuses a connection may share one in-process `codex app-server daemon start`; and already-running, executable-missing, nonzero, timeout, socket-unavailable, unsupported, protocol, and other endpoint failures retain exact content-free reasons and the existing fallback. Fake CLI/proxy fixtures cover exact argv, cold and warm initialize, bounded readiness, concurrent callers, per-caller cancellation, late-arrival handoff, zero waiter leaks, and discarded process output. The opt-in installed-Codex smoke strips inherited `TMUX`/`TMUX_PANE`, contains the Codex home and control socket below one validated temporary root, proves idempotent start-to-proxy-initialize readiness, and records the exact cleanup target without adding product shutdown behavior.
|
|
39
|
+
- `make test` / `make test-integration` / `make test-e2e`: Codex app-server readiness diagnostics keep endpoint readiness, running executable/version, official daemon-manager ownership, and remote-control capability as independent content-free axes. The managed-current, unmanaged-current, unmanaged-skew, and endpoint-dead matrix proves a ready endpoint never hides ownership or skew; unknown evidence remains typed unknown. Doctor, the Settings no-op row, and support `doctor.json` project the same fields and explicit native-action refusal, shared-client interruption risk, and operator recovery code without retaining managed/socket paths or remote identity. Repeated read-only surfaces emit only `app-server proxy`, `app-server daemon version`, and `remoteControl/status/read`; their process/config/socket mutation argv ledger is zero, while a product-source audit forbids shared-daemon stop/restart/kill/remote-control mutation argv. Managed-current and the exact cold user-action `daemon start` contract remain covered separately.
|
|
38
40
|
- `make test`: Codex native control plane Phase 2 binds `thread/start` plus its single optional `turn/start`, or `thread/resume` with zero thread creation, to the exact Agent uid, managed Pane uid, and activation generation. App-server identity is additive schema-2 state on the Pane activation while the durable Agent `sessionRef` remains the conversation pointer; native Pane describe rows expose binding source, generation, thread, and current turn. Fake protocol and materialization tables pin one-prompt create, stored-thread resume, unavailable/unsupported fallback through the unchanged CLI/hook contract, indeterminate post-create refusal without a second lane, and zero-write rejection for another Pane, generation, or thread. Exact late hooks may refine only the current turn; native-bound hooks fail closed before tmux remap or Registry writes, while legacy/unmanaged hooks and bounded late acknowledgement retain their previous behavior.
|
|
41
|
+
- `make test`: Codex empty-prompt create fallback keeps fresh native identity limited to immediately attachable input. `TestStartDefaultThreadEmptyAndPromptedRequestCounts` pins zero proxy/open/thread/turn requests plus safe fallback for empty input and exactly one thread plus turn for prompted input. `TestEmptyPromptCodexCreateUsesOnePlainCLILaneAndNoNativeBinding` and `TestEmptyPromptCodexSplitProducersKeepOnePlainCLILane` pin one unchanged plain CLI launch across canonical, provider-shortcut, and split producers; `TestEmptyPromptCodexFallbackFirstInputConvergesSessionRefAndRouting` pins the first real hook input converging the durable Agent `sessionRef` and live Pane routing. `TestCodexFanOutKeepsCurrentPlainCLILaneWithoutNativeCreate` and the prompted/stored/picker/post-mutation tests keep fan-out, exact resume, and fail-closed parity.
|
|
39
42
|
- `make test` / `make test-integration` / `make test-e2e`: Codex native control plane Phase 4 makes `thread/list` the Resume Picker catalog source and keeps the exact-id `thread/read(includeTurns=false)` validation seam for Session State. Unit/fake-server goldens pin explicit `cli`/`vscode`/`appServer` source kinds, non-archived and recency filters, opaque multi-page cursor progression, repeated/empty/exhausted cursor rejection, cwd depth, exact thread id, provider name or short-id-only fallback, branch, closed runtime status, deterministic ordering/dedupe, and typed source/confidence/fallback reason. A native failure discards every partial native row before exactly one rollout scan; native-empty never merges rollout history. Picker-to-create tests require native rows to call `Resume` with the exact listed id and rollout rows to stay on the current CLI lane. Session State tests require a bound session id or persisted resume id to win without `thread/read`, a thread-only candidate to pass one exact-id read before storage, a mismatched read to write zero, and the default validator to remain probe-only so autosave never starts the shared daemon. Provider parity and negative audits keep Claude/Antigravity and snapshot recipes unchanged and keep native title inference, prompt preview, transcript turns, and XML unwrap at zero. The installed read-only catalog smoke is opt-in through `PROJMUX_CODEX_CATALOG_SMOKE_ROOT=/tmp/<isolated-root>` with matching contained `CODEX_HOME`; it strips inherited tmux identity, ensure-readies once, lists metadata, and validates the first listed exact id without turns or thread mutation.
|
|
40
43
|
- `make test` / `make test-integration` / `make test-e2e`: Resume Picker stable-summary Phase 0 settles Codex, Claude, and Antigravity through provider-local bounded summary adapters before the picker opens, then performs one global dedupe/newest-first sort/cap and renders that immutable list from the first input frame through close. `ResumeSummary` contains only provider, exact resume id, timestamps, compact label, branch, depth-gated relative cwd, and source reference; turns, runtime state, confidence/reason explanation, pagination detail, and preview/transcript bytes cannot enter its row/search projection. Codex reads at most one native summary page under a 300 ms budget while its bounded rollout fallback walks and parses concurrently with context cancellation; native success (including native-empty) keeps authority, while native failure/timeout selects the already-settled fallback or an available empty/partial result and drops every late native result for the current invocation. At the unchanged 450 ms population cutoff, a bounded cancellation handoff collects a matching fallback partial even when its buffered send follows `ctx.Done`; that handoff remains inside the `<500ms` first-frame contract and does not extend discovery. Envelope expiry never renders `unavailable`, while a genuine provider error still does. Six provider completion permutations, exact 0/50/500 ms plus blocked-native fixtures, a large-rollout cutoff race, and an empty Codex store pin one settled first frame under 500 ms, exact native/fallback/Claude/Antigravity id and source handoff, and invariant row value/order hashes. Phase 1 removes every `status\t<provider>` item and projects each settled provider as the first line of the fixed lower footer with distinct available, empty, fallback, and unavailable states; AI Resume passes zero upper `ChromeBands`. Row/status tables require every item value to be `new` or an exact parseable resume; native picker tests pin provider state outside query/cursor/Enter cardinality and the exact lower ordering `provider line → shown count → content-free MoreNotLoaded`, including independent EN/KO clipping at 80 columns. Turn enrichment and page continuation do not write the current list. Phase 2 routes focus through typed `SelectionDetail` updates whose `Items`, Header/Footer, and `MoreNotLoaded` set flags stay zero; the renderer reserves and pads the independent down detail dock from the first frame so empty/help, loading, ready, unavailable, clipped, and scrolled content cannot move the list or footer. The visible provider-neutral row grammar is `relative age → provider badge → branch → optional relative cwd → conversation title` under one width policy; turns, row-local `[fallback]`, and runtime/source/confidence/reason explanations live only in the invocation-local detail projection, while exact source remains in the frozen SearchKey and resume routing. Picker geometry goldens and a 20-row round-trip cursor sweep pin identical 80x24/120x40 origins, limits, dividers, footer positions, query bytes, and item values; app race/privacy tests pin latest-focus-wins cancellation, one read per exact `(provider, id, updatedAt)` key, preview failure isolation, and zero preview-byte persistence.
|
|
44
|
+
- `make test` / `make test-integration` / `make test-install-smoke`: App socket marker migration Phase 0 keeps ordinary mutation fail-closed on app-owned pre-0.13 partial markers with a typed exact `config apply --socket <name>` recovery and zero writes; pins config apply's `-L` to absolute `-S` path/PID, pre-source app ownership and missing-or-exact logical policy, typed route-marker write plan, post-write reobservation, and refusal of foreign/forged/mismatch/alias/PID-drift inputs; projects missing/mismatch/unreadable Doctor findings identically in text/JSON without mutation; and recovers an installed live legacy session while preserving its exact server generation.
|
|
45
|
+
- `make test` / `make test-install-smoke` / `make test-e2e-update`: App socket marker migration Phase 1 makes npm, Go, GitHub Release, and source `make install` converge the exact live route before binary publication and verify it with the published binary afterward. Stage-table tests require an exact eventual `--bin` target, stop publication after pre-apply failure, make source/publication/post-verify failure non-success with the Phase 0 exact recovery, and keep `--no-apply` pre-apply-free with zero live access plus an explicit apply-required state. The isolated install fixture pauses the atomic move and runs legacy/built/installed shell and attach consumers before/during/after while preserving the exact v0.12.2-modeled socket path, PID, and sessions without exposing a raw missing-marker error.
|
|
41
46
|
- `make fmt`: repository formatting for Go, shell snippets, and generated docs where applicable.
|
|
42
47
|
- `make fix`: safe automatic fixes such as `go fix` and repository-approved cleanup steps.
|
|
43
48
|
- `make npm-pack`: macOS-hosted local npm binary package staging and `npm pack --dry-run` for the root package plus platform packages, including native-key-adapter checks for both Darwin architectures; release staging reuses the release matrix archives.
|
|
@@ -53,10 +58,11 @@
|
|
|
53
58
|
- `make test` / `make test-integration` / `make test-e2e`: Runtime topology exit cascade Phase 1 exact Offline Pane/Agent canonical deletion: unit/runtime tables require an explicit exact `uid:` plus durable `MissingRuntime=True/RuntimeUnbound` Pane evidence or an unbound `Offline` Agent whose retained Panes all carry that evidence, and require a positive exact socket identity plus non-empty inventory proving zero mirrors; implicit/name/scope/`--all`, no-server, empty/error/permission inventory, live-without-evidence, duplicate, and foreign ownership cases refuse with zero writes. Route tests pin byte-consistent dry-run/apply Registry-only plans and the exact `would be killed`/`was killed` diagnostics, locked zero-to-live, live-to-zero, generation, owner, duplicate, and foreign revalidation, repeat no-match idempotence, zero raw tmux kills, and preservation of the owning Window/root, sibling Panes, and a resumed live Agent bound to a newer Pane generation. The isolated exact-socket e2e repeats Pane and Agent dry-run/apply/repeat on app-owned and standalone hosts, keeps sibling Registry/tmux hashes and a foreign socket unchanged, and verifies that no canonical `kill-pane` targets the already absent runtime while missing-server authority is refused.
|
|
54
59
|
- `make test`: Registry-first control plane Phase 2 resolved resource graph: `internal/core/resourcegraph` is the one join between the Registry's desired topology and one exact tmux server, and it ships with its contract frozen rather than with a consumer. The attribution matrix table covers managed sessions/windows/Agent-owned panes, an app-owned control session, the same control marker refused on a standalone host, an unknown role value, an ephemeral scratch session, ephemeral outranking a control marker on one session, unmarked objects resolving to unattributed inside a managed enclosure or on an app-owned server and to foreign otherwise, unknown Project/Window/Pane uids resolving to recoverable, duplicate claims, a window whose session mirrors another Project, a pane whose window mirrors another Window, Pane and Agent uids mirrored onto the wrong kind of option, and absent containment evidence still binding on an exact uid. Status derivation is pinned for all four kinds including `missing-root` outranking a live session, an Agent deriving its managed Pane's status while its phase stays the Registry's, and a paneless Agent reading offline rather than unknown. The owner-chain invariant crosses every level of runtime containment and then asserts that no surviving binding disagrees with the Registry chain, so a cross-Project match is a test failure rather than a review question. Partial failure is a per-scope table: a failed sessions, windows, or panes query downgrades only its own rows to `unknown` with a stated reason while every row count is preserved. Determinism is proven by sixteen shuffled inventories marshalling byte-identically plus a fixed ascending tmux-id order, `Resolve` is proven not to mutate either argument, and a `go/parser` import audit is what keeps the package unable to write anything -- no filesystem, process, or tmux dependency can be added to it without that test failing. The transport table covers explicit `--socket`, an absolute `--socket-path` including cleaning and the relative-path refusal, the mutually exclusive flags, explicit flags outranking an inherited `$TMUX`, an inherited absolute socket path, a discarded relative inherited value, and no transport as a legal state; `@projmux_app` is trusted only at its exact value. `internal/integrations/metadata` covers the observation adapter: a fixed four-call budget that does not grow with a 200-session server and does not repeat across five `Observe` calls, every call pinned to the exact socket with an explicit mutating-verb denylist, the four transport modes (app-owned `-L`, standalone `-L`, explicit `-S`, and no transport with zero tmux calls), two sockets never reading each other, one failed query degrading exactly one scope while the full budget still runs, an absent server costing one call and reading as nothing-live rather than unknown, identical managed rows resolved from one tmux output under both host modes, and the routing layer refusing an absent transport, a non-tmux executable, and a missing runner instead of falling back to the default server. An opt-in real-tmux smoke (`PROJMUX_RESOURCEGRAPH_TMUX_SMOKE=1`, skipped by default so a unit run never spawns a server) builds its own isolated `TMUX_TMPDIR` and two unique sockets, strips the inherited `TMUX`/`TMUX_PANE` from every invocation, and proves the format strings and the containment join against real tmux: the managed Project/Window/Pane bind to the exact `$N`/`@N`/`%N` the server reported, Home resolves to control, a scratch session to ephemeral, a plain session to unattributed, a sibling socket carrying the same uids is never read and is unchanged afterwards, and cleanup kills only the exact `#{socket_path}` it has confirmed lives inside its own temporary root.
|
|
55
60
|
- `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.
|
|
61
|
+
- `make test` / `make test-e2e`: Invocation target and Agent control authority convergence Phase 1 projects the closed `natural-omitted` / `explicit-target` / `refusal` / `explicit-fan-out` classification from the unified CLI Route graph. `TestInvocationAuthorityCensusIsACompleteBijection`, `TestCanonicalAndAliasProjectionsKeepOneInvocationAuthority`, `TestMixedCommandFamiliesOverrideTheirNamespaceClass`, and `TestInvocationAuthorityHasNoSecondRouteManifest` reject missing, duplicate, conflicting, unknown, alias-divergent, and independently maintained rows across every graph node plus the parser-owned bare/help/version bridges; generated reference and runtime route help project the same class. `TestCreateAgentExplicitMissingWindowWithoutProjectRefusesBeforeWrite` pins an explicit missing Window plus `--create-window` without its Project owner as actionable exit-2 with zero stdout, Registry write, tmux write, or ambient observation. `TestExactProjectCreateWindowUsesAppRouteDespiteStaleInheritedPane`, `TestExactThreeUIDCreateAgentRouteIgnoresUnrelatedAmbientPane`, and `TestOutsideTmuxExactThreeUIDCreateAgentUsesOnlyQuietAppRoute` pin explicit resource scope to one validated app logical `-L` route, independent of unrelated or stale inherited `TMUX_PANE`, with no bare/default probe. L06 crosses the shortcut and resource spellings over that same missing-owner refusal and exact-Project success pair on a real custom app socket, requiring refusal-time Registry/runtime/foreign/focus hashes and tmux-call count to remain unchanged, then one quiet `%N` result through only validated `-L`/`-S` calls. `TestExplicitLiveSocketPathIsAmbientInvariant` and the existing exact delete/reconcile transport tables keep an explicitly selected live socket on byte-stable `-S`; `TestNativePrivateActivationAnchorNegativeAuthorityMatrixIsFirstWriteZero`, `TestDetachedExplicitAnchorAuthorityNegativeMatrixIsFirstWriteZero`, and `TestSidebarProjectOpenRouteRejectsActualPaneProjectOwnershipMismatchWithoutWrites` retain strict socket/PID/marker/generation/owner containment and first-write zero.
|
|
56
62
|
- `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.
|
|
57
63
|
- `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 3 command-scoped controller kernel makes `internal/core/controller` the one authorization and ordering seam that `projmux reconcile resources` runs through: observe one exact server, resolve a `resourcegraph.Graph`, plan, commit the Registry, guard tmux, execute, reobserve. The policy is a closed intent x attribution table and is tested as one -- every cell is covered exactly once with a stated reason, `start`/`import`/`delete` are refused for all seven classes, repair is allowed only on `managed` and `unattributed`, `recoverable`/`foreign`/`conflict` are refused, `control`/`ephemeral` are observe-only, and an invented class fails closed. A verb gate refuses `new-session`, `new-window`, `split-window`, `kill-session`, `kill-pane`, and an empty argv even on a managed handle while still passing `set-option` and `rename-window`, so "convergence never created or killed a runtime object" is structural. Authorization resolves an operator-spelled `session:index` target to its stable `@N` before anything runs, refuses a target the observation never saw, attaches the uid guard plus the containment guard (`session_id` for a window, `window_id` for a pane) only to writes that will actually run, and walks containment two levels to recognize a managed enclosure. Plan order is proven determinism-under-shuffle for keys and marshalled bytes plus session-before-window-before-pane containment order, which is load bearing rather than cosmetic. App-level coverage pins acceptance directly: dry-run and execute project the identical key sequence, execute reports `reobserved: converged`, and the repeat is a no-op with zero Registry writes, zero tmux mutations, and byte-identical Registry and tmux state; a machine carrying an offline Registry Project, an app-owned `role=control` Home session, and an unattributed scratch session survives an execute with zero lifecycle verbs, unchanged session/window/pane counts, untouched control and scratch options, the offline Project neither started nor pruned nor duplicated, and the start/import/delete refusals stated in the report; a uid claimed by somebody else at the plan-to-execute boundary aborts at `tmux prevalidation` with zero writes, the durable Registry commit already recorded, and the exact retry; a `#{socket_path}` that changes under the run aborts before the first write; and an injected Registry commit failure reaches tmux zero times while keeping the stage list. The pre-existing partial-tmux-failure suite now converges through the kernel unchanged. The in-memory tmux fake gained `show-options -gv` for the `@projmux_app` marker and a targetless `display-message -p '#{socket_path}'`, which is the socket guard. The observation adapter gained one required fix the kernel depends on: tmux answers a read of an unset `@projmux_app` with a non-zero `invalid option`, so treating that as a failed query left host mode permanently `unknown` on every server projmux did not start and downgraded each unmarked object there from `unattributed` to `foreign`; an unset marker now reads as `standalone` with zero degraded scopes while a genuinely unreadable option still reads as `unknown`. Because that distinction only appears against real tmux, the policy carries one explicit grant: `reconcile resources` cannot run without an operator-named exact server, and that selection -- and only that selection -- makes an unmarked `foreign` object repairable; the grant is proven to change exactly the two foreign repair cells and to buy no import, start, or delete. Explicit topology materialization keeps its own engine and its own plan-time guard and is deliberately not routed through the kernel, because it plans against objects it is about to create.
|
|
58
64
|
- `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 4 runtime diagnostics escape hatch adds `projmux get runtime sessions|windows|panes [--socket <name> | --socket-path <absolute>] [-o json|none]` and the `projmux runtime diagnostics` picker over a new pure package, `internal/core/runtimediag`, which projects the runtime half of a resolved `resourcegraph.Graph` and re-derives no attribution of its own. The pure tests pin that every observed object is emitted with its class, its reason, its stable tmux id and its fully qualified coordinate -- managed no-ops included -- that a `recoverable` object reports its mirrored uid and is still handed no resource identity, that a duplicate claim reaches both claimants as a conflict naming both handles, that a coordinate degrades from the session name to the `$N` id and disappears entirely when the enclosing session cannot be resolved, that a no-transport graph projects every scope unavailable with a never-null empty item list while a readable empty server projects neither, that the three report kinds are byte-identical across eight rebuilds and hold only their own object kind, and that the class tally names only the classes present in the closed declaration order. The route tests run the production observation adapter against a fake server carrying a managed Project session, the Home control session, a scratch session, an unmarked window and pane, and a window mirroring an unknown uid: they assert the full row set per kind, the human table golden and its host/transport header, the stable JSON envelope and its byte-identical repeat, the app-owned/standalone/no-transport/inherited-`$TMUX` matrix, and a call audit proving the read issues exactly `show-options`, `list-sessions`, `list-windows`, and `list-panes` -- four per observation, zero write verbs, and zero calls on a second socket that exists in the same fixture. The picker tests assert the rendered rows and tally, that Focus forwards `--target <session>:@N.%N --socket <#{socket_path}>` to the existing focus route, that Attach reaches `attach project uid:<uid>` only for a bound session and only outside tmux and otherwise renders the stated refusal, that Inspect reaches the shipped Resource Inspector with no arguments, that the whole action vocabulary of every row is those three plus Back and inert rows, and that ko-KR localizes the chrome while leaving every tmux id, uid, class, and name untranslated. The Linux integration smoke runs the read against three real isolated servers -- an app-owned host, a standalone host mirroring the same Registry uids, and a sibling that must never be read -- and proves the classes, the identical managed identity under both hosts, the control marker refused on the guest, the no-transport projection, byte-identical repeats, and unchanged Registry/tmux/sibling state. The Linux e2e opens `projmux runtime diagnostics` in a real `display-popup` on a real attached client, walks the list to its last row so every live session, window, and pane id is observed on screen, opens one action menu and asserts Focus/Attach-refusal/Resource Inspector with no destructive entry, and then proves the Registry bytes, the exact server, the sibling socket, and the client focus are all unchanged.
|
|
59
|
-
- `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 5 makes the primary navigation surfaces Registry-first over a new pure package, `internal/core/registryview`, which projects a resolved `resourcegraph.Graph` plus the caller's filesystem discovery into three sections: the Registry Projects with their Windows, shell Panes, Agents and Agent-owned Panes; the discovered directories no Project claims; and one Runtime link. The pure tests pin the Project -> Window -> Pane/Agent hierarchy and its depths and parents in Registry slice order, that an Agent-owned Pane is emitted once and only under its Agent, that a no-transport graph yields the same row ids in the same order with status downgraded to unknown and the offline start/resume action still offered, that closing a runtime object changes only status and moves no row, that identity/order/eligibility are identical on an app-owned and a standalone host while the runtime tally moves the same object from unattributed to foreign, that no control or ephemeral runtime object can become a row, that a session literally named `home` with no role marker stays unattributed, that a discovered directory already claimed by a Project root is not duplicated, that a missing-root row offers rebind and delete only, that the Runtime link is emitted even on an empty Registry with no transport, that `Descendants` returns exactly one Project subtree, that an Agent phase is reported verbatim, and that `Build` mutates neither argument. The app tests run the production observation adapter against the same fake app-owned/standalone/sibling servers the Phase 4 escape hatch uses: three navigation refreshes issue exactly `show-options`, `list-sessions`, `list-windows` and `list-panes` -- four per observation, zero write verbs, zero reconcile or materialize, zero calls on the sibling socket -- while a no-transport read issues zero tmux calls and still lists the Registry rows, and both host modes produce byte-identical rows. The Projects picker tests pin the section order (Registry Projects, unregistered candidates, Runtime link), the identical row values with and without a server, the withdrawal of the Home control session, a scratch session and a hand-opened window from the managed list, the uid-carrying selection of a missing-root Project that used to fail the whole picker on directory validation, the Runtime link forwarding to `runtime diagnostics`, the dedicated key opening `Projects > Resources`, the declined Project lookup for an unregistered path, and Registry-sourced window tabs on an offline Project. The recent-session and recent-window tests pin id-based attribution (`$N` and `@N`, never a name join), the withheld class tally and its Runtime link, the managed resource name carried beside the exact tmux handle the action adapter targets, the always-kept current window, and full degradation to the observed list when no graph is available. The hierarchy action tests prove every action forwards to a shipped route: `focus --target <coordinate> --socket <exact path>` for a live row, `attach project uid:<uid>` for an offline Project and for the owning Project of a row beneath one, `agent resume uid:<uid>` for an Agent, and `runtime diagnostics` for the link -- with rebind and delete listed as eligible with their exact command rather than executed. `internal/integrations/tmux` now reads `#{session_id}` with the recent-session rows so that pairing is exact. The integration smoke asserts the non-interactive `switch preview` projection of the view model on a real app-owned server, on a real standalone server, and outside tmux: three Registry rows with live/unknown status, byte-identical row identity and order across the transition and across the two hosts, the per-host Runtime tally, no Registry created by a no-transport read, and a Registry plus both servers plus the sibling socket unchanged. The e2e smoke drives a real 80x24 popup through a real attached client: the Projects sidebar shows the Runtime link with its control/ephemeral/recoverable tally, the filter finds the managed Project row, the dedicated key opens the read-only hierarchy with the host header and live rows and without any runtime-only object, and after the managed session is killed the same Project is still a row whose hierarchy reports offline with the start action -- with the Registry byte-identical, the sibling socket unchanged, and the attached client never moved.
|
|
65
|
+
- `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 5 makes the primary navigation surfaces Registry-first over a new pure package, `internal/core/registryview`, which projects a resolved `resourcegraph.Graph` plus the caller's filesystem discovery into three sections: the Registry Projects with their Windows, shell Panes, Agents and Agent-owned Panes; the discovered directories no Project claims; and one Runtime link. The pure tests pin the Project -> Window -> Pane/Agent hierarchy and its depths and parents in Registry slice order, that an Agent-owned Pane is emitted once and only under its Agent, that a no-transport graph yields the same row ids in the same order with status downgraded to unknown and the offline start/resume action still offered, that closing a runtime object changes only status and moves no row, that identity/order/eligibility are identical on an app-owned and a standalone host while the runtime tally moves the same object from unattributed to foreign, that no control or ephemeral runtime object can become a row, that a session literally named `home` with no role marker stays unattributed, that a discovered directory already claimed by a Project root is not duplicated, that a missing-root row offers rebind and delete only, that the Runtime link is emitted even on an empty Registry with no transport, that `Descendants` returns exactly one Project subtree, that an Agent phase is reported verbatim, and that `Build` mutates neither argument. The app tests run the production observation adapter against the same fake app-owned/standalone/sibling servers the Phase 4 escape hatch uses: three navigation refreshes issue exactly `show-options`, `list-sessions`, `list-windows` and `list-panes` -- four per observation, zero write verbs, zero reconcile or materialize, zero calls on the sibling socket -- while a no-transport read issues zero tmux calls and still lists the Registry rows, and both host modes produce byte-identical rows. The Projects picker tests pin the section order (Registry Projects, unregistered candidates, Runtime link), the identical row values with and without a server, the withdrawal of the Home control session, a scratch session and a hand-opened window from the managed list, the uid-carrying selection of a missing-root Project that used to fail the whole picker on directory validation, the Runtime link forwarding to `runtime diagnostics`, the dedicated key opening `Projects > Resources`, the declined Project lookup for an unregistered path, and Registry-sourced window tabs on an offline Project. The recent-session and recent-window tests pin id-based attribution (`$N` and `@N`, never a name join), the withheld class tally and its Runtime link, the managed resource name carried beside the exact tmux handle the action adapter targets, the always-kept current window, and full degradation to the observed list when no graph is available. The hierarchy action tests prove every action forwards to a shipped route: `focus --target <coordinate> --socket <exact path>` for a live row, `attach project uid:<uid>` for an offline Project and for the owning Project of a row beneath one, `agent resume uid:<uid>` for an Agent, and `runtime diagnostics` for the link -- with rebind and delete listed as eligible with their exact command rather than executed. `internal/integrations/tmux` now reads `#{session_id}` with the recent-session rows so that pairing is exact. The integration smoke asserts the non-interactive `switch preview` projection of the view model on a real app-owned server, on a real standalone server, and outside tmux: three Registry rows with live/unknown status, byte-identical row identity and order across the transition and across the two hosts, the per-host Runtime tally, no Registry created by a no-transport read, and a Registry plus both servers plus the sibling socket unchanged. The e2e smoke drives a real 80x24 popup through a real attached client: the Projects sidebar shows the Runtime link with its control/ephemeral/recoverable tally, the filter finds the managed Project row, the dedicated key opens the read-only hierarchy with the host header and live rows and without any runtime-only object, and after the managed session is killed the same Project is still a row whose hierarchy reports offline with the start action -- with the Registry byte-identical, the sibling socket unchanged, and the attached client never moved. Sidebar Window runtime-state projection is maintained by `TestInventoryObserverParsesExactWindowActiveWithoutAnotherQuery`, `TestInventoryObserverRejectsMalformedWindowActiveInWindowScopeOnly`, `TestResolveProjectsExactWindowLiveAndActiveAsOrthogonalFacts`, `TestGraphWindowRuntimeStateInvariantRejectsMalformedFacts`, `TestBuildPreservesSeparateWindowLiveAndActiveFacts`, `TestViewWindowRuntimeStateInvariantRejectsMalformedRows`, `TestSwitchRegistryWindowTabsSelectActiveThenStableLiveThenOffline`, `TestSwitchRegistryAndTmuxWindowTabProducersHaveRuntimeSemanticAndByteParity`, `TestBuildSwitchRowsSidebarThreeLineCardHardClipsWithin80ColumnNativeBudget`, `TestFormatSidebarSwitchWindowTabsStableRuntimePartitionAndCap`, `TestFormatSidebarSwitchWindowTabsOfflineOnlyKeepsRegistryOrderInactive`, and `TestFormatSidebarSwitchWindowTabsDoesNotRepairMalformedActiveFacts`: exact `window_active` rides the existing four-call inventory, Live and Active remain orthogonal through graph/view and both producers, the fixed three slots stably select active then other live then offline/unknown, and malformed active facts fail upstream validators rather than being repaired by the renderer.
|
|
60
66
|
- `make test` / `make test-e2e`: Registry-first control plane Phase 5 corrective, Project sidebar presentation order: Phase 5 inverted the row *source* correctly and then ordered the result by nothing but the pin flag, which put `$HOME` behind every managed Project and left liveness out of the order entirely. Membership stays the Registry's and order becomes the sidebar's: `internal/app/switch_registry_rows.go` partitions the managed Projects into three presentation tiers -- pinned, then live, then closed -- with Registry order preserved inside each tier as a stable tie-break, and `internal/app/switch.go` lifts the discovered `$HOME` row out of the unregistered section to lead the whole list as chrome. Nothing is synthesized and nothing is stored: a tier is a projection of one exact host's live overlay, `internal/core/registryview` still emits Registry order, and no Registry schema, order field, or desired topology is involved. Because a tier can move a row between refreshes, the sidebar refresh no longer lets the cursor ride a position or a path: it resolves the pre-refresh selection to its Project uid and that uid back to whatever row the Project renders as now, so a Project that loses its root and starts carrying `uid:<uid>` keeps the cursor too. The unit tests pin the exact mixed-fixture order (Home, the two pinned Projects whether live or closed, the two live Projects, the two closed Projects, the unregistered candidate, the Runtime link) over a Registry whose slice order disagrees with every tier, the isolated pinned-offline-beats-unpinned-live rule, the no-transport collapse into one closed tier in Registry order, byte-identical rows and order across app-owned and standalone hosts with the same live overlay, Home leading the list while resolving to no Registry Project and claiming no uid anchor, the uid anchor surviving both a tier change that provably moves the row's index and a root that goes missing under it, the absent anchor for Home/Settings/the Runtime link/an unregistered path, and a negative audit that three refreshes over the anchor's extra bounded read issue no write verb, never touch the sibling socket, and leave the exact server byte-identical. The e2e smoke extends the real-tmux Projects sidebar with a second managed Project so the live and closed tiers are both populated, reads the rows out of a real pane rather than a popup so their order is observable, and proves the order flips exactly as the live overlay changes across a real open -> close -> refresh while the Registry stays byte-identical.
|
|
61
67
|
- `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.
|
|
62
68
|
- `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.
|
|
@@ -301,6 +307,56 @@
|
|
|
301
307
|
command, path, permission, and request content out of Registry, tmux, notify,
|
|
302
308
|
diagnostics, support, and Archive sinks.
|
|
303
309
|
|
|
310
|
+
- `make test` / `make test-integration`: Codex public turn control live-binding
|
|
311
|
+
compatibility uses one strict six-field tmux frame. Unit tests accept only
|
|
312
|
+
literal `\037` and raw unit-separator spellings without generic escape
|
|
313
|
+
decoding or value trimming; missing, extra, mixed, multiline, Registry
|
|
314
|
+
activation, runtime, Pane, thread, generation, and epoch drift all fail
|
|
315
|
+
closed with zero app-server writes. The isolated real-tmux regression removes
|
|
316
|
+
inherited `TMUX`/`TMUX_PANE`, uses run-unique `TMUX_TMPDIR` and `-L` names,
|
|
317
|
+
pins tmux 3.4's literal frame, and cleans only its immediately queried exact
|
|
318
|
+
socket path.
|
|
319
|
+
|
|
320
|
+
- `make test`: Invocation-target authority Phase 2 keeps create, topology replay,
|
|
321
|
+
resume, and native Codex on one typed exact-route Pane binder. The maintained
|
|
322
|
+
matrix crosses `-L`/`-S` with success and first/middle/last option-write
|
|
323
|
+
failure, requires complete managed/native option projection or reverse-order
|
|
324
|
+
compensation, and rejects route-less observer transport. Exact same-Window
|
|
325
|
+
create ignores inherited `TMUX_PANE` and private child anchors while retaining
|
|
326
|
+
explicit route-anchor plus socket/marker/PID guards. Observer tests pin
|
|
327
|
+
inherited-tmux environment removal, bounded connect/control fallback, exact
|
|
328
|
+
Registry/Pane/generation/thread guards, and explicit-route-only sink writes;
|
|
329
|
+
control tests require the resolved logical route plus current Pane/thread and
|
|
330
|
+
non-empty epoch before start, steer, interrupt, or approval can reach a wire.
|
|
331
|
+
|
|
332
|
+
- `make test` / `make test-integration` / `make test-e2e`: Codex observer
|
|
333
|
+
recovery Phase 0 gives native create and resume one post-commit startup
|
|
334
|
+
contract: the exact Agent/Pane/generation/runtime/route watcher must complete
|
|
335
|
+
a bounded private handshake as `provider-control-plane` with a non-empty
|
|
336
|
+
epoch and live control endpoint, or converge to `provider-hook` with an empty
|
|
337
|
+
epoch and a typed start-failed, early-exit, handshake-timeout, endpoint, or
|
|
338
|
+
control-endpoint reason. Fake-child and exact-route tests pin argv,
|
|
339
|
+
environment, ready-settle lifetime, process-group cleanup, and the full
|
|
340
|
+
startup matrix; observer/control integration pins exact active-turn steer and
|
|
341
|
+
socket removal. Binding replacement and cancellation keep old authority,
|
|
342
|
+
fallback, cleanup, progress, and sibling writes at zero.
|
|
343
|
+
|
|
344
|
+
- `make test` / `make test-integration` / `make test-e2e`: Codex observer
|
|
345
|
+
recovery Phase 2 revokes E1 control before publishing disconnect
|
|
346
|
+
invalidation, then retries only the same exact Agent/Pane/generation/runtime/
|
|
347
|
+
thread binding with 100ms exponential backoff capped at 1s and six failed
|
|
348
|
+
replacement attempts. A recovered observer publishes E2 only after a fresh
|
|
349
|
+
snapshot and control endpoint are ready; exhaustion publishes the single
|
|
350
|
+
typed `reconnect-exhausted` provider-hook fallback and exits. Unit tests pin
|
|
351
|
+
E1-to-E2 ordering, stale epoch/event zero-write, exact retry delays and count,
|
|
352
|
+
terminal cleanup, and independent multi-Agent convergence on one shared fake
|
|
353
|
+
endpoint. Public start, steer, interrupt, and approval tests require zero
|
|
354
|
+
app-server writes throughout invalidating, reconnect-gap, exhausted, and
|
|
355
|
+
identity-drift states. The isolated lifecycle E2E records fake provider
|
|
356
|
+
mutations across old -> down -> replacement and requires the same public
|
|
357
|
+
zero-write gap before a distinct ready epoch appears; the run-owned tmux,
|
|
358
|
+
proxy, control socket, and observer process cleanup contract is unchanged.
|
|
359
|
+
|
|
304
360
|
- `make test` / `make test-integration` / `make test-e2e`: Window anchor and primary-shell role separation Phase 1 makes launch-authorship promotion one atomic Registry/runtime transaction. `TestResolveAgentPaneAuthorityClosedTable` closes explicit launch-authorship, hook-only, no-marker, and ambiguous authority; `TestMarkAIHookPaneSeparatesTransientShellObservationFromOwnedAgentStatus` keeps exact Agent-owned hook status/sessionRef separate from unbound shell transients; `TestPublicAuthorshipPromotionMultiAllocationOrderParity` and `TestPublicAuthorshipPromotionAttachesExistingAgentWithoutAllocation` pin symbolic UID order plus distinct mint/attach allocation; `TestPublicResourceReconcilePromotesCanonicalLaunchAuthorshipAtomicallyAndRepeatsEmpty` covers Pane owner/role, Agent paneRef, anchor/default transitions, guarded exact runtime options, dry-run/execute structure, and repeat zero-write; `TestControllerRuntimeMutationAcceptsExactSiblingUIDEffectBetweenPlanAndGuard` permits only a same-plan exact sibling UID that converged between observation and guard while retaining foreign-UID zero-write refusal; `TestPublicLaunchProviderConflictFreezesExactTargetAndConvergesUnrelatedTarget` requires target conflict zero-write without suppressing unrelated convergence; and `TestPublicAuthorshipPromotionPrecommitAndGuardFaultMatrixLeavesNoMixedState`, `TestAuthorshipPromotionRuntimeRollbackIsIndependentOfActionOrder`, and `TestAuthorshipPromotionPreservesSiblingProjectSocketAndOtherHostDesiredState` cover fault/action-order rollback with no mixed state and preservation of sibling Project/socket, other-host-only desired status, and D5 state.
|
|
305
361
|
|
|
306
362
|
- `make test` / `make test-e2e`: interactive tmux `run-shell` producers converge on one output channel. tmux paints a foreground `run-shell` job's stdout, its stderr, and its `'<command>' returned <n>` line as a view-mode screen over the pane the key was pressed in, which is how a successful pane-menu split and a successful Window create/rename came to hide the Codex process the operator was reading behind `[0/1452]`. `internal/app/run_shell_output_ledger.go` declares the closed set: every producer -- catalog binding, context-menu item, status-bar binding, generated hook, startup command, runtime command, and the one allowlisted control sentinel -- carries exactly one of `intentional-ui`, `stdout-stderr-zero`, `redirect`, and `exact-client-message`, and `forbidden-overlay` exists only so the test has a name for what it refuses. Two sweeps keep the set closed: the generated standalone and app configs are rendered with every catalog action bound (Window create and rename ship with no default chord, so a stock-config sweep would never see them), each `run-shell` occurrence must resolve to exactly one row, and each row's declared background/redirect/exit-guard must match the bytes; a source sweep then requires every non-comment `run-shell` line in `internal/app` to be a registered site, so a new producer cannot be added without classifying it. Enforcement is structural rather than per-route: `App.Run` dispatches the ledgered interactive argv through a guard that buffers stdout and stderr, converges a failure to one bounded `display-message` on the exact client, and exits zero -- with the single exception of an undeliverable message, where the original error is returned rather than claiming success. Coverage pins the guarded/unguarded matrix (public `switch open`, `create`, `rename`, `describe`, popup payload routes, the picker, the supervisor, and every help spelling stay unguarded and byte-identical), a loud success reaching tmux as zero bytes and zero messages, the bounded one-line failure copy, and the confirmed producers' new copy: `Created Pane`, `Created Window`, `Renamed Window: <name>`, each delivered to the exact client with the canonical projection consumed in-process and left to the public CLI. The direct split bridge stops reporting a refusal twice (client message plus non-zero exit), the status-bar keyboard table now carries `#{client_tty}` so a second attached client never receives another operator's result, and the foreground runtime-created hook is exit-guarded so a refused convergence cannot paint the pane that just appeared. The generated-config theme goldens were rebaselined once for those two config changes.
|
|
@@ -312,12 +368,18 @@
|
|
|
312
368
|
- Behavior changes require new parity assertions or a different e2e scenario.
|
|
313
369
|
- A target stops being authoritative and must be replaced.
|
|
314
370
|
|
|
315
|
-
- `make test` / `make test-integration` / `make test-e2e`:
|
|
371
|
+
- `make test` / `make test-integration` / `make test-e2e`: Stable UID authority and tmux locator rebinding Phase 0 makes clean Agent exit a fixed point over supervisor receipt, exact same-socket `pane_dead=1`, Registry projection, and controller-lock ordering. C-1 enforcement is `TestC1ExistingLiveManagedRootCreateRebindsCurrentWindowAndClearsMissingRuntime` and `TestC1C2CurrentUIDContainmentRebindsCachedLocatorsForRetainedDeadPaneCleanup`: Project and ControlSession creates atomically replace stale `$N/@N` diagnostics and clear `MissingRuntime`, while retained cleanup transports current stable UID/owner mirrors with current locators. C-2 enforcement is `TestC2StableUIDGenerationAuthorityAcceptsReboundCurrentLocators`, `TestC2StableAuthorityConflictsWriteZeroAndReturnRetryErrors`, and `TestC2LifecycleFirstWriteGuardRejectsOwnerAndServerPIDDriftWithZeroKill`: stable UID/ownerRef/activation generation authorizes the plan, cached handles never do, stable-fact conflicts fail before a Registry transaction, and the first write re-proves current containment, positive dead state, exact socket, and server PID generation. `TestExactDeadPaneReceiptWaitUsesLiveMinusDeadObservation` proves a retained dead Pane is not mistaken for a running Pane during the bounded lock-free receipt wait. `TestPaneAgentCascadeDeletePlanReleasesPaneAndRetainsAgentWindow` and `TestReleasedSameGenerationDeadPaneRetryConvergesAndPreservesSessionRef` pin both orderings: a current binding and an already released Offline Agent with matching same-generation Pane/Agent supervisor evidence both remove only the Pane, preserve Agent/sessionRef/Window, and never follow a resumed non-empty new binding. `TestExactDeadPaneUIDDisambiguatesAReusedRuntimeHandle` keeps a historical Pane with the same recycled tmux `%N` activation handle and requires the exact current `pane_dead=1` mirrored uid to select and delete only the new dead Pane while the historical Pane/Agent remain byte-identical. `TestExactPaneExitReceiptPermutationsConvergeToOneIdempotentPlan`, `TestSimultaneousCleanAgentExitsConvergeWithoutDeadOrMirroredResiduals`, and `TestCoalescedCleanAgentExitEventsConvergeBothExactDeadPanes` require receipt duplicates, event order, two near-simultaneous exits, and one-worker coalescing to reach byte-identical repeat results with sibling state unchanged. Exact dead runtime cleanup precedes the final Registry graph commit; `TestDeadPaneCleanupFailureExposesTypedRetryAndNextPassConverges` requires the machine-checkable `exact-dead-pane-cleanup-retry` reason and next-pass convergence without replacement authority. `TestControllerRetriesTypedDeadPaneCleanupReasonAndConvergesNextPass` requires that reason to remain visible while the same controller worker retries the exact event before widening. `TestC2StableAuthorityConflictRemainsInDurableControllerRetryQueue`, `TestATransientConvergenceErrorReplaysTheDrainedEventAndConverges`, and `TestAConvergenceErrorReachesTheProducer` require a stable-authority conflict or ordinary convergence failure to restore the drained exact event, retry it by the same worker up to the bound, and retain it durably with the terminal error if the failure persists; whole-host coalescing preserves the retry generation instead of resetting it. The isolated real-tmux L17 smoke removes inherited `TMUX`/`TMUX_PANE`, uses a unique `TMUX_TMPDIR` with exact `-L`, proves both queried socket paths stay below the fixture root, restarts one server, binds the original stable Project/Window/shell UID chain at deliberately different current `$N/@N/%N`, materializes that exact current pair, and requires a supervisor-normal Agent exit to remove only its current retained dead Pane/runtime while leaving the Agent resumable Offline and the sibling socket byte-identical. Abnormal, killed, unknown, foreign, empty, unavailable, stale, and resumed observations retain zero clean-delete authority. Phase 1 now owns the last-Pane Window/resource boundary; Phase 0's non-last Pane fixed point and public hook routing remain unchanged.
|
|
372
|
+
- `make test` / `make test-integration` / `make test-e2e`: Preexisting dead Agent Pane startup reconciliation adds one private config-apply candidate producer without widening exhausted replay or any public surface. C-1/C-2 enforcement is `TestPreexistingDeadAgentPaneCandidateAuthorityTable`, `TestPreexistingDeadAgentPaneForeignRouteIsTypedFirstWriteZero`, `TestPreexistingDeadAgentPaneObservationFailureTableIsTypedFirstWriteZero`, `TestPreexistingDeadAgentPaneDuplicateStableIdentityIsAmbiguous`, `TestPreexistingDeadAgentPaneReceiptConflictMatrixIsFirstWriteZero`, `TestPreexistingDeadAgentPaneDispositionSessionAndRepeatMatrix`, `TestPreexistingDeadAgentPaneLockedSupervisorAndActivationDriftWriteZero`, `TestPreexistingDeadAgentPaneLockedPositiveDeadBecomesLiveIsFirstWriteZero`, `TestPreexistingDeadAgentPaneLockedActivationDriftTableIsFirstWriteZero`, `TestConfigApplyProducerConvergesExactlyOnePreexistingCandidateThenRepeatsNoOp`, `TestConfigApplyProducerContainmentDriftIsTypedFirstWriteZero`, `TestConfigApplyProducerBuildsOneDeterministicCandidatePerBoundedPass`, `TestConfigApplyControllerLeaseBoundsOneCandidatePassesAndProvesFixedPoint`, and `TestPreexistingLifecycleFirstWriteGuardRechecksOriginalSupervisorAbsence`: only one exact app-socket positive-dead Pane with complete stable Project/Window/Pane UID, ownerRef/paneRef, current Agent/generation/operation/runtime, app session marker, and bounded absent original supervisor PID reaches the existing journal absorption, `DispositionFor`, Pane-Agent cascade, and dead-Pane first-write guard. Matching current receipts retain their existing normal/abnormal/unknown disposition, absence remains reconcile/unknown, stale and duplicate receipts add no authority, and conflicts or live/ambiguous/foreign/drift/unreadable observations are typed write-zero refusals. The integration `preexisting dead Agent Pane startup recovery` and L17 `exit reconciliation e2e preexisting dead Agent Pane startup recovery` fixtures recreate the historical retained-dead shape only under unique `TMUX_TMPDIR`/exact physical sockets, kill the exact supervisor with hooks absent, reach the Offline/no-Pane fixed point in one startup apply, and require repeat Registry bytes plus Project, Window, shell anchor, sibling socket, and provider sentinel to remain identical.
|
|
373
|
+
- `make test` / `make test-integration` / `make test-e2e`: Exhausted clean-exit startup replay reserves only retry-3 exact `pane-exited` records from ordinary destructive drain and re-evaluates them after config apply has verified and marked one app-owned physical socket. `TestExhaustedCleanExitReplayReservesTerminalEventsAndAcknowledgesOnlySuccess` pins non-destructive selection, exact success acknowledgement, concurrent-event preservation, and repeat no-op; `TestExhaustedCleanExitReplayRetainsIneligibleAndFailedEventsByteIdentical` keeps authority conflicts and first-write failures byte-identical; `TestConcurrentExhaustedEventNeverJoinsAnotherWorkersFinalRetry` proves a terminal record published after another worker starts is reserved while that worker alone receives its own retry-3 final attempt. `TestC1ExhaustedCleanExitReplayConvergesStableAgentAndRepeatsWriteFree` requires the existing exact lifecycle planner and dead-Pane runtime guard to remove only the retained runtime/Pane row while preserving Agent uid/name/sessionRef/lastTermination as resumable Offline; `TestC1ExhaustedCleanExitEligibilityTableWritesAndCleansZero` covers receipt absent, unknown, abnormal, killed, intentional, operation/generation mismatch, live/no-dead, foreign/ambiguous mirrors, and newer activation with zero Registry/runtime writes. `TestPublicConfigApplySurfacesExactControlTargetRefusal` also pins replay-before-generic-controller ordering on the verified physical `-S` target. The integration `exhausted clean-exit startup replay` and e2e `exit reconciliation e2e exhausted startup replay` fixtures reconstruct a historical terminal event only under run-unique `TMUX_TMPDIR`/exact `-L` sockets, require success ack plus Offline/no-Pane convergence and byte-identical repeat, preserve sibling Project/socket and provider/empty-prompt sentinels, and clean up only the observed physical sockets.
|
|
316
374
|
|
|
317
375
|
- `make test` / `make test-integration` / `make test-e2e`: Create Agent early-exit authority keeps the provider behind the creator's exact Registry commit without weakening rollback identity. The supervised activation child enters the existing cross-process Registry lock and admits only the committed Agent-owned Pane whose generation, operation, `%N` runtime, and Running `paneRef` all match; the read-only callback writes zero Registry bytes. The creator carries one private cleaned absolute Registry path through the supervisor, so admission reads and termination-receipt writes stay on the creator's state root even when the tmux server inherited another XDG environment; this path is not exported to the provider or public hooks. On POSIX, a private CLOEXEC failure channel distinguishes admission/provider-exec failure (no termination receipt) from the same numeric provider exit (exact supervisor receipt), while HUP during admission kills the supervised gate before provider side effects and remains exact killed evidence. `TestSuperviseWaitsForTheCommittedActivationBeforeStartingTheChild`, `TestSuperviseRefusesTheChildWhenCreateAbortsBeforeActivationCommit`, `TestSuperviseActivationAdmissionIsExactAndZeroWrite`, `TestActivationAuthorityRejectsNonExactRegistryPathsBeforeProviderStart`, `TestAgentAdmissionAndReceiptUseCreatorRegistryAuthorityNotAmbientXDG`, `TestActivationExecFailureHandshakeIsTyped`, `TestSuperviseActivationHandshakeDistinguishesGateFailureFromProviderExit`, and `TestActivationExecCloseOnExecDoesNotWaitForProviderDescendants` pin commit/abort, blank/malformed/foreign identity refusal, split XDG authority, sibling byte preservation, exit 1/42 disambiguation, HUP side-effect zero, and descendant fd containment. The real-tmux integration and L17 e2e run an immediate exit-42 provider with the generated `pane-exited` hook live and require no blank-UID rollback, exact generation/operation evidence, sibling preservation, and fixed-point repeat. `PMX_TEST_EARLY_EXIT_REPEATS=30 test/integration/create-agent-early-exit-repeat.sh` is the only repeat interface: it builds one immutable attempt binary, then gives every sample a fresh HOME/XDG/Registry/root and unique app/sibling sockets before exact-owned cleanup. Shell Pane supervision and the public `PROJMUX_*` hook contract are unchanged.
|
|
318
376
|
|
|
319
377
|
- `make test` / `make test-integration` / `make test-e2e`: Native Codex exact-selector mutation route authority carries the activation gate's exact raw `%N` through the existing private `__PROJMUX_RUNTIME_ANCHOR_PANE` only; the public `PROJMUX_*` environment, activation generation, sessionRef, thread/turn, and lifecycle meanings stay unchanged. `TestNativeActivationEnvironmentAddsOnlyThePrivateExactRouteAnchor`, `TestExactThreeUIDCreateAgentRouteIgnoresUnrelatedAmbientPane`, `TestNativePrivateActivationAnchorNegativeAuthorityMatrixIsFirstWriteZero`, and `TestStandaloneRouteRequiresBlankClassAndExactInheritedPaneReceipt` pin exact anchor production, UID owner-chain independence from an unrelated client, stale/malformed/wrong-server/foreign first-write refusal, and unchanged standalone receipt strength. `TestOutsideTmuxExactThreeUIDCreateAgentUsesOnlyQuietAppRoute`, `TestOutsideTmuxExactThreeUIDCreateRoutesProductionAIPresentationWrites`, `TestOutsideTmuxProductionAIPresentationFailureIsVisibleAndRollsBack`, `TestAgentResumeRoutesProductionAIPresentationWritesThroughExactRuntime`, and `TestProductionAIPaneBindersUseOnlySuppliedExactRuntimeRoute` require an outside-tmux exact create to use only explicit `-L`/`-S` calls—including exact `select-pane -T ... -t %N` title presentation through the real AI binder—while returning one trimmed `%N` on stdout and no stderr; the provider shell contains no tmux mutation, and real permission and route-authority failures remain non-zero and transactional. The real-tmux early-exit integration asserts provider/private-anchor equality plus owner-title parity, and L06 audits the exact command's tmux argv while preserving two attached clients' sibling Window hash and the sibling socket hash.
|
|
320
378
|
|
|
379
|
+
- `make test` / `make test-integration` / `make test-e2e`: Project sidebar detached-open authority is explicit from the generated Alt-1 popup through materialization: `switch --anchor %N` retains the popup Pane unless that sidebar flow stops its origin Session, in which case it reobserves the exact target-client Pane once; the detached child serializes required `switch sidebar-open --anchor %N`, and typed Project-open/session/topology requests carry that same field into exact route binding. `TestBuildPopupToggleCarriesSidebarAnchorAsTypedSwitchArgumentOnly`, `TestSwitchExecuteSidebarHookProjectLaunchesContinuationBeforeSelfClose`, `TestSwitchExecuteSidebarAfterOriginStopRebindsContinuationAnchorToClient`, `TestSwitchSidebarOpenRequiresExplicitAnchorBeforeEffects`, `TestSwitchSidebarOpenCarriesOneExplicitAnchorThroughTypedMaterializerRequest`, `TestDetachedExplicitAnchorBindsExactAppRouteWithoutAmbientEnvironment`, `TestDetachedExplicitAnchorAuthorityNegativeMatrixIsFirstWriteZero`, `TestSidebarAnchorDriftAfterPreflightRefusesBeforeFreshRegistryWrite`, and `TestSidebarProjectOpenRouteRejectsActualPaneProjectOwnershipMismatchWithoutWrites` pin the generated command, one-time rebind, parser refusal, environment-free typed handoff, exact socket/PID/$/@/% containment, post-trust pre-write revalidation, and stale/wrong-server/foreign/managed-owner first-write-zero behavior. L11 keeps `TMUX`/`TMUX_PANE` absent from detached continuations, runs under a dedicated `TMUX_TMPDIR` and unique logical sockets, verifies offline Continue/Fresh plus exact client retention and error-sidebar recovery, and cleans only observed socket paths allowlisted beneath its isolated root. The native supervised Codex private carrier above remains unchanged.
|
|
380
|
+
|
|
381
|
+
- `make test` / `make test-integration` / `make test-e2e`: Save and Quit policy Phase 0 freezes one complete exact-route Registry/resource-graph observation and captures every `StatusLive` managed Project latest snapshot in Project UID/session order before the existing guarded shutdown. C-1 enforcement is `TestPlanQuitSnapshotBatchSelectsOnlyLiveManagedProjectsInStableOrder`, `TestQuitSavePreflightUnavailableOrConflictPerformsZeroCaptureAndKill`, `TestQuitSaveBarrierContinuesAfterMiddleFailureAndDoesNotKill`, `TestQuitSaveBarrierRetryRecapturesEveryTargetThenRunsOneGuardedShutdown`, `TestQuitSaveAndQuitIntegrationCapturesRegistryGraphBeforeGuardedShutdown`, and `TestQuitSaveBarrierKeepsNamedRegistryAndSiblingBytesInvariant`; missing-root, incomplete identity, marker/route/socket drift, 0/1/N, atomic validity, retry, named-snapshot, Registry, and sibling invariants remain fail-closed. C-2 enforcement is `TestQuitUnsavedActionsAndFlagsAreSnapshotFreeAndByteEquivalent`, `TestQuitCommandPickerActionsHaveEnglishKoreanMeaningParity`, and `TestQuitCommandCancelAndCloseDoNotShutdown`: explicit unsaved quit and existing `--yes`/`--force` retain zero snapshot I/O and exact guarded-shutdown compatibility while Cancel/Esc write nothing. The installed smoke strips inherited `TMUX`/`TMUX_PANE`, uses one unique `TMUX_TMPDIR` with exact app and sibling sockets, materializes two managed Projects plus Home/control and ephemeral sessions with auto-save off, requires two refreshed latest snapshots before app death, excludes every non-Project session, preserves named/Registry/sibling bytes, and cleans only queried socket paths proven below the smoke root.
|
|
382
|
+
|
|
321
383
|
## E2E Required-Gate Evidence
|
|
322
384
|
|
|
323
385
|
- `make test-e2e-coverage` is the maintained AGS-OEDR inventory gate. It
|
|
@@ -362,6 +424,38 @@
|
|
|
362
424
|
Codex, and npm consumers. Local Security is the exact three-way
|
|
363
425
|
`make security` topology; CI's required branch-protection context is the
|
|
364
426
|
fail-closed aggregate named `Test`.
|
|
427
|
+
- `test/e2e/evidence-contract.sh` (`make test-e2e-contract`) and
|
|
428
|
+
`test/e2e/reliability-contract.sh` (`make test-e2e-reliability`) keep the persisted
|
|
429
|
+
`projmux.e2e-attempt/v1` evidence and success result hash stable while adding
|
|
430
|
+
a separate stderr-only `projmux.e2e-terminal/v1` first-failure record. Its
|
|
431
|
+
closed fields identify scenario, phase, owner, shard, status, repository
|
|
432
|
+
source/line, sanitized single-scenario command, binary/state hashes, and exact
|
|
433
|
+
replay without raw argv. Synthetic L06/L08/L16 fixtures pin eight-racer lock
|
|
434
|
+
state, bounded Registry changed paths plus pending owned state, and child/file/
|
|
435
|
+
exact tmux state plus sanitized hashed tails; denylist checks reject token,
|
|
436
|
+
raw HOME/path, full Registry, and raw command leakage. Normal scenario meaning,
|
|
437
|
+
one-build/four-shard topology, and `.bin/e2e-evidence` retention are unchanged.
|
|
438
|
+
- `make test` / `make test-e2e`: L06 Registry lock recurrence applies the
|
|
439
|
+
unchanged 400-attempt, 2ms/50ms delay, and 30s stale budget to one verified
|
|
440
|
+
positive-PID owner lease. `TestRegistryLockRetryBudgetTracksVerifiedOwnerLease`
|
|
441
|
+
permits a waiter to continue across healthy owner turnover while unchanged,
|
|
442
|
+
empty, malformed, and unreadable owners remain bounded; the real-tmux L06
|
|
443
|
+
burst requires all eight exact-socket creates to converge on one Window with
|
|
444
|
+
nine unique mirrored Panes and no lock or staged residue.
|
|
445
|
+
- `make test` / `make test-integration`: attention omitted-target convergence
|
|
446
|
+
is enforced by `TestAttentionMutationOmittedTargetMatchesExplicitPaneLedger`,
|
|
447
|
+
`TestAttentionMutationOmittedTargetRefusesWithoutExactInvocationPane`, and
|
|
448
|
+
`TestAttentionMutationHelpUsesOptionalPaneCatalogUsage`; generated/reference/
|
|
449
|
+
guide parity stays pinned by `TestAttentionMutationOptionalPaneDocsParity`,
|
|
450
|
+
and generated hook argv by
|
|
451
|
+
`TestGeneratedAttentionFocusHooksKeepExplicitPaneArgv`. The unit tables pin
|
|
452
|
+
three-verb explicit/omitted handler-ledger parity, exact `%N` targeted
|
|
453
|
+
reobservation, no targetless tmux calls, and no-write refusal for outside,
|
|
454
|
+
missing, blank, malformed, stale, or contradictory inherited evidence. The
|
|
455
|
+
isolated real-tmux integration runs omitted toggle→clear and arm against one
|
|
456
|
+
inherited Pane with a sibling unchanged, then proves outside/stale failures
|
|
457
|
+
have non-zero status, stdout zero, bounded actionable stderr, and unchanged
|
|
458
|
+
attention state.
|
|
365
459
|
|
|
366
460
|
## Review Checklist
|
|
367
461
|
- The branch stays within its stated scope.
|
package/docs/cli-guide.md
CHANGED
|
@@ -63,6 +63,28 @@ rendered from the same command manifest the binary renders `projmux help`
|
|
|
63
63
|
from and is verified against it on every `make test`, so it cannot drift.
|
|
64
64
|
Nothing in this guide restates it.
|
|
65
65
|
|
|
66
|
+
Every executable route and root-level parser bridge in that same graph has one
|
|
67
|
+
**selectorless authority** class. The label describes what omission means; it
|
|
68
|
+
does not prevent a route from accepting an explicit selector:
|
|
69
|
+
|
|
70
|
+
- `natural-omitted` — omission resolves one predictable current resource or a
|
|
71
|
+
documented contextual read/scope, such as an active-root inventory or picker;
|
|
72
|
+
supplying any selector replaces that natural target or scope instead of
|
|
73
|
+
blending with it. Current Pane/Window mutations still require one exact
|
|
74
|
+
resource.
|
|
75
|
+
- `explicit-target` — the route or its generated caller must name the exact
|
|
76
|
+
target; ambient tmux context cannot supply or narrow it.
|
|
77
|
+
- `refusal` — the node has no safe selectorless action, so omission refuses
|
|
78
|
+
before output or mutation. Namespace nodes use this class when only a child
|
|
79
|
+
route is executable.
|
|
80
|
+
- `explicit-fan-out` — the route spelling is an intentional global or
|
|
81
|
+
whole-set operation. Resource mutations never enter this class merely
|
|
82
|
+
because a selector happened to match several rows.
|
|
83
|
+
|
|
84
|
+
The generated reference and `projmux <route> --help` print the class projected
|
|
85
|
+
from the graph. Completeness tests census every graph node plus bare/help/version
|
|
86
|
+
root bridges and reject missing, duplicate, conflicting, or unknown rows.
|
|
87
|
+
|
|
66
88
|
## Resource selectors and the active target
|
|
67
89
|
|
|
68
90
|
The resource routes (`get`, `describe`, `create`, `rename`, `rebind`, `delete`,
|
|
@@ -215,7 +237,7 @@ The scope resolves in two branches:
|
|
|
215
237
|
|
|
216
238
|
- **Explicit `--project`/`-p` wins**, inside tmux and outside it. The active
|
|
217
239
|
tmux target is not consulted at all.
|
|
218
|
-
- **With no
|
|
240
|
+
- **With no explicit scope occurrence, the Project comes from the active managed runtime**:
|
|
219
241
|
the `@projmux_window_uid` mirrored on the pane you are in, and that Window's
|
|
220
242
|
registry `ownerRef`. This is the same seam the empty-selector reads use.
|
|
221
243
|
|
|
@@ -224,7 +246,7 @@ flag alone:
|
|
|
224
246
|
|
|
225
247
|
```
|
|
226
248
|
projmux create codex # active Project, active Window, split from the active Pane
|
|
227
|
-
projmux create codex -w hi --create-window #
|
|
249
|
+
projmux create codex -p alpha -w hi --create-window # exact Project, new Window "hi"
|
|
228
250
|
projmux create codex -p beta -w main # everything explicit
|
|
229
251
|
projmux create pane -p alpha # every Window of alpha; a deliberate fan-out
|
|
230
252
|
```
|
|
@@ -237,6 +259,11 @@ shell-required offline operation may plan a lazy direct
|
|
|
237
259
|
`spec.defaultShellPaneRef` without replacing an Agent anchor. A missing or stale
|
|
238
260
|
anchor is exit `2` rather than a silent alternate-Pane repair.
|
|
239
261
|
|
|
262
|
+
An exact existing Window or Pane can reveal its owner Project through Registry
|
|
263
|
+
`ownerRef`. A Window named with `--create-window` does not exist yet and cannot;
|
|
264
|
+
without `--project` that spelling refuses and names `--project <ref>` as the
|
|
265
|
+
remedy, even when the invocation happens inside a managed Pane.
|
|
266
|
+
|
|
240
267
|
Refusals are exit `2` with zero Registry writes and zero tmux mutations, and
|
|
241
268
|
they name `--project` as the fix:
|
|
242
269
|
|
|
@@ -248,9 +275,19 @@ they name `--project` as the fix:
|
|
|
248
275
|
gone — a `recoverable` runtime is reported, never adopted.
|
|
249
276
|
|
|
250
277
|
Every create is **detached**: no create moves the client. Use `focus pane` or
|
|
251
|
-
`-o pane-id` when you want to end up in the new pane.
|
|
252
|
-
inherited exact
|
|
253
|
-
|
|
278
|
+
`-o pane-id` when you want to end up in the new pane. A natural create validates
|
|
279
|
+
the inherited exact route and Pane containment. An explicit resource scope
|
|
280
|
+
binds the selected app resource route without letting unrelated inherited
|
|
281
|
+
`TMUX`/`TMUX_PANE` choose or change the resource target; exact Project plus
|
|
282
|
+
`--create-window` therefore uses the validated app logical `-L` route on its
|
|
283
|
+
first attempt, with no `env -u` workaround. Runtime safety remains independent
|
|
284
|
+
and may refuse before mutation. An inherited app-owned `TMUX` socket/PID stays
|
|
285
|
+
route evidence: projmux validates its exact `-S` path, ownership/logical
|
|
286
|
+
markers, logical `-L` alias, and PID while ignoring unrelated `TMUX_PANE`
|
|
287
|
+
containment. Outside tmux it validates the default app `-L` route. Marker,
|
|
288
|
+
physical-socket, server-PID, generation, and owner reobservation remain
|
|
289
|
+
mandatory. Commands that explicitly select an existing live `--socket-path`
|
|
290
|
+
keep that exact `-S` route unchanged.
|
|
254
291
|
|
|
255
292
|
#### Splits started from a popup
|
|
256
293
|
|
|
@@ -1269,6 +1306,13 @@ projmux attention window [window]
|
|
|
1269
1306
|
Toggles the `✳` pane title prefix and the `@projmux_attention_state` pane
|
|
1270
1307
|
option. `toggle` flips between cleared and `reply`; `clear` always
|
|
1271
1308
|
clears; `arm` sets a pre-reply armed state used by the AI flow. The
|
|
1309
|
+
optional pane is the exact pane invoking the command: when it is omitted,
|
|
1310
|
+
Projmux requires inherited `$TMUX` plus an exact `$TMUX_PANE=%N` and verifies
|
|
1311
|
+
that same pane with a targeted tmux read before changing attention state. From
|
|
1312
|
+
outside tmux, or when that evidence is missing, malformed, or stale, pass an
|
|
1313
|
+
explicit pane target instead; the command fails without writing attention
|
|
1314
|
+
state. Explicit targets used by generated focus hooks keep their existing
|
|
1315
|
+
meaning.
|
|
1272
1316
|
producer side pushes the matching entry into the notify queue when the pane
|
|
1273
1317
|
has an associated AI agent option; clearing attention does not ack the queue
|
|
1274
1318
|
row (manual toggles on shell panes do not push). `list` reads `tmux list-panes -a` and shows live pane
|
|
@@ -1722,8 +1766,8 @@ keymap action is no longer accepted: replace a stale
|
|
|
1722
1766
|
The `projmux agent topic set/clear` commands keep
|
|
1723
1767
|
AI topic ownership separate from the user pane label and raw pane title.
|
|
1724
1768
|
`apply` regenerates the app tmux config and reloads the live `-L projmux`
|
|
1725
|
-
server without restarting it. `make install` and `projmux update apply` invoke
|
|
1726
|
-
|
|
1769
|
+
server without restarting it. `make install` and `projmux update apply` invoke
|
|
1770
|
+
it before binary publication and again afterward for verification. Settings > Keybindings normally runs the same
|
|
1727
1771
|
save/config/reload flow automatically; use `projmux config apply` (or its
|
|
1728
1772
|
hidden equivalent `projmux internal tmux apply`) as the CLI recovery or sync path after
|
|
1729
1773
|
hand-editing `keymap.toml`, after saving Settings outside tmux, or after
|
|
@@ -1812,15 +1856,21 @@ binary in `$GOBIN`/`$GOPATH/bin`/`~/go/bin` as `go`, and a local `go build`
|
|
|
1812
1856
|
still require an explicit `PROJMUX_INSTALLER=github-release`. Anything else is
|
|
1813
1857
|
reported as `unknown` with guidance.
|
|
1814
1858
|
`apply` is installer-aware and only runs after explicit user selection.
|
|
1815
|
-
For npm installs,
|
|
1859
|
+
For npm installs, the current binary first runs `config apply --bin` with the
|
|
1860
|
+
exact published target. Only after that succeeds does it run
|
|
1861
|
+
`npm install -g projmux@latest` (which reliably
|
|
1816
1862
|
crosses minor/major versions where `npm update -g` does not, and re-resolves
|
|
1817
1863
|
the per-platform optional dependency) and then runs the new binary's
|
|
1818
|
-
`projmux config apply
|
|
1864
|
+
`projmux config apply` as post-publication verification. A failed preparation
|
|
1865
|
+
does not invoke the installer; later failures are non-zero and print the exact
|
|
1866
|
+
`projmux config apply --socket projmux` recovery. With `--no-apply`, the
|
|
1867
|
+
pre-publication live convergence is omitted and the post-update step uses
|
|
1819
1868
|
`--no-reload`: it still migrates marker-owned files and writes generated
|
|
1820
|
-
configuration without accessing live tmux
|
|
1869
|
+
configuration without accessing live tmux, then explicitly reports that live
|
|
1870
|
+
apply remains required. For Go installs, it uses the same ordering around the existing atomic
|
|
1821
1871
|
replacement implementation. For `github-release` installs, it downloads the latest
|
|
1822
1872
|
matching `projmux_<version>_<goos>_<goarch>.tar.gz` release asset, extracts the
|
|
1823
|
-
binary, atomically replaces the current executable, then performs the same
|
|
1873
|
+
binary, pre-converges, atomically replaces the current executable, then performs the same
|
|
1824
1874
|
apply/`--no-reload` convergence. `source` installs report an
|
|
1825
1875
|
actionable error to update the checkout with `git pull --ff-only && make install`.
|
|
1826
1876
|
|
|
@@ -1867,12 +1917,23 @@ human configuration work should prefer `config render` and `config apply`.
|
|
|
1867
1917
|
a new Project/Window/shell UID chain and one same-root claimant. Repeating it
|
|
1868
1918
|
allocates another new identity. Neither action modifies snapshot
|
|
1869
1919
|
bytes, the project directory, git/worktrees, unrelated roots, or trust state.
|
|
1870
|
-
- `quit` — open an action picker with `
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1920
|
+
- `quit` — open an action picker with `Save Project snapshots and quit`, `Quit
|
|
1921
|
+
without saving`, and `Cancel`. The safe first action takes one complete,
|
|
1922
|
+
exact-socket Registry/resource-graph observation, freezes every live managed
|
|
1923
|
+
Project session in Project UID/session order, and captures each latest
|
|
1924
|
+
snapshot even when auto-save is off. Home/control, ephemeral, unattributed,
|
|
1925
|
+
recoverable, foreign, and offline sessions are excluded and reported as
|
|
1926
|
+
bounded class counts. Every target is attempted. A failed capture leaves the
|
|
1927
|
+
successful per-session atomic files in place, reports the exact failed
|
|
1928
|
+
session, and does not stop the app; retry captures every target again. Only an
|
|
1929
|
+
all-success ledger reaches the existing physical-socket, app-marker, and
|
|
1930
|
+
logical-route guarded shutdown. Named snapshots and Registry bytes are never
|
|
1931
|
+
written, and the batch is not a multi-file transaction or topology freeze.
|
|
1932
|
+
`Quit without saving` preserves the earlier guarded shutdown behavior:
|
|
1933
|
+
missing servers and runtimes without the app marker are no-ops. Existing
|
|
1934
|
+
non-interactive `--yes` and `--force` callers retain that same snapshot-free
|
|
1935
|
+
behavior and exact shutdown route; the default command always uses the
|
|
1936
|
+
action picker.
|
|
1876
1937
|
- `attach project <ref>` — enter a Project runtime from outside tmux.
|
|
1877
1938
|
Automatic live-runtime attachment is `runtime attach`.
|
|
1878
1939
|
- `settings` — interactive configuration UI for the project picker, AI
|