projmux 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/docs/agent-workflow.md +182 -4
- package/docs/cli-guide.md +94 -21
- package/docs/cli.md +267 -7
- package/docs/codex-native-required-migration.md +175 -0
- package/docs/configuration.md +36 -13
- package/docs/hooks.md +30 -5
- package/docs/install.md +23 -7
- package/docs/keybindings.md +3 -1
- package/docs/npm-distribution.md +6 -0
- package/docs/operational-diagnostics.md +7 -8
- package/docs/testing.md +33 -7
- package/docs/troubleshooting.md +164 -0
- package/docs/upgrading.md +38 -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.
|
|
@@ -105,7 +111,7 @@
|
|
|
105
111
|
- `make test`: fast unit coverage for app-layer AI split native agent launch/selective popup-toggle/settings/status/notification parity including AI pane option metadata, watcher metadata bootstrap for existing panes, capture-backed reply detection, missing-pane watcher shutdown, armed focus-only reply badge clearing, busy attention preservation on focus clear, manual AI topic preservation while watcher status still updates, agent-labeled desktop notification message context with pane-title-first body text, global/project-local lifecycle hook dispatch for post-create/pre-create/post-attach/send-noti with project hook and `.projmux/config.toml` trust-store hashing plus env/settings kill-switch gating, declarative startup command and hook run coverage, `send-noti` stdin JSON delivery plus `PROJMUX_NOTIFY_*` env payload, notify queue write success/failure/depth-guard dispatch rules, notify/statusbar/sidebar origin-client focus routing, project config generic env session environment application without Kube-specific projection, project hook/trust writes with startup/env compatibility, startup command send-keys orchestration plus startup pane replay markers, pre-create abort behavior, and shared projmux notification icon paths, and scoped even row/column resizing after shell and agent splits, status-bar git segment parity including muted git branch block styling, compact dirty/staged/ahead/behind state colors, and bounded git child execution, statusbar pwd display-only native-framed path popup with shared popup-wait-key/no-extra-payload-line command coverage, no clipboard or tmux buffer copy, and popup-wait-key cursor/raw-mode restore coverage, statusbar usage native HUD popup alignment/height-budget/threshold palette/muted sync-staleness/fallback coverage without raw CLI popup or popup-toggle stacking, statusbar settings click popup fallback and settings chip right-edge rendering without default trailing space, popup-toggle stale marker recovery, isolated `projmux shell` tmux app launch/config generation including home fallback plus project-context default session targeting from `PROJMUX_CWD` or nearest project marker, app-owned project-name statusbar layout, distinct project badge color, and quiet debounced session-state autosave command/app-config trigger, shell-entry welcome release prompt and inline update handling, shell update skip-by-latest-tag behavior plus best-effort stale-cache refresh, pane/window keybindings, keymap.toml tmux override rendering/stale unbinds including retired direct/UserKey cleanup, Settings Keybindings root/list/detail capture flows including parse-error rows, unsafe raw capture and timeout guards, disable/reset writes, app config regeneration, live tmux source-file reload, no-live-tmux save behavior, and stage-by-stage apply recovery copy, window rename bindings, pane rename helper/binding, pane-exit rebalance command/hooks, hook-pane and after-select-pane based attention focus hooks, attention badge toggle/clear/list/window rendering, attach/current/kill/pin/preview/prune/sessions/session-popup/settings commands, switch, tag, tmux helper commands, update status/check/apply cache and installer detection including GitHub Release binary asset selection/extraction/replacement, doctor versioned read-only report/section/verbosity contracts, AI notify integration diagnostics, and Session State resume metadata diagnostics, Settings AI notify integration diagnostics read-only status/conflict/CLI guidance, and Settings About update status/check action wiring, untitled standalone popup-toggle marker close/config install, direct popup minimum sizing, AI picker minimum width and height, sidebar minimum width and compact badge spacing, preview select writes, popup render output after cycling, switch picker pin action behavior without inline settings rows, nested settings hub sections for AI defaults, project picker filesystem scan/pin actions, Project Root settings source/shadowing/set/current/clear flows, app/keybinding info including Ctrl-M rename forwarding, and About version/source rendering, switch picker focused-session kill, switch picker launcher-key abort bindings, switch explicit project-root, unconfigured-root, and weak managed-root heuristic parity, switch popup hiding new-session candidates while sidebar keeps create-capable rows, switch row project-name display with `~` pinned to the top and live-session-first sorting, pretty-path, preview context without retired Kube metadata, switch settings subcommand flows including add-current-pin, interactive add-pin picker, and settings label/preview polish including Phase 2 Settings/hookmaker/project-startup/trust/quit destructive row color regression strings, native preview wiring, baseline picker surface parity including prompt/footer/header fallback without app-name filler and search-key scoped card matching, sidebar compact action-only key footer, sidebar preview-window/start-position behavior without focus-time session switching, sidebar row/window ANSI styling with pane-aggregated attention badge state and AI topic labels, Alt+2/Alt+3 legacy popup row, preview-window, pane metadata, and pane-snapshot parity, switch read0 card rows with active/inactive title styling, right-side status badges, combined directory/git metadata with muted inactive branch styling and bounded slow-repository branch enrichment, statusbar-matched block window tabs with window attention badges, read0 expect-key action parsing, restored pin/tag card badges, and restrained selected-row marker styling, switch preview metadata without duplicated directory/git rows, preview metadata rendering, popup pane display names for AI agents, AI topics, and shell commands, switch preview cycle bindings, sessions picker preview/cycle/open/kill wiring including attached-session fallback behavior, sessions picker launcher-key abort bindings, popup/switch preview summary formatting, popup sessions tmux entry helpers, switch/popup/session rendering, session identity, session-state Claude/Codex/Antigravity resume and declarative startup replay, session snapshot capture/autosave recipe classification including save/autosave pre-capture resume metadata refresh from live AI session ids including Antigravity conversation ids, Claude transcript paths, and Codex rollout log cwd matching/ambiguity skips, candidate discovery, config path derivation, popup preview read-models, and pure state rules including preview, tag, and lifecycle stores.
|
|
106
112
|
- `make test`: fast unit coverage for app-layer AI split native agent launch/selective popup-toggle/settings/status/notification parity including AI pane option metadata, watcher metadata bootstrap for existing panes, capture-backed reply detection, missing-pane watcher shutdown, armed focus-only reply badge clearing, busy attention preservation on focus clear, manual AI topic preservation while watcher status still updates, agent-labeled desktop notification message context with pane-title-first body text, global/project-local lifecycle hook dispatch for post-create/pre-create/post-attach/send-noti with project hook and `.projmux/config.toml` trust-store hashing plus env/settings kill-switch gating, declarative startup command and hook run coverage, `send-noti` stdin JSON delivery plus `PROJMUX_NOTIFY_*` env payload, notify queue write success/failure/depth-guard dispatch rules, notify/statusbar/sidebar origin-client focus routing, project config generic env session environment application without Kube-specific projection, project hook/trust writes with startup/env compatibility, startup command send-keys orchestration plus startup pane replay markers, pre-create abort behavior, and shared projmux notification icon paths, and scoped even row/column resizing after shell and agent splits, status-bar git segment parity including muted git branch block styling and compact dirty/staged/ahead/behind state colors, statusbar pwd display-only native-framed path popup with shared popup-wait-key/no-extra-payload-line command coverage, no clipboard or tmux buffer copy, and popup-wait-key cursor/raw-mode restore coverage, statusbar usage native HUD popup alignment/height-budget/threshold palette/muted sync-staleness/fallback coverage without raw CLI popup or popup-toggle stacking, statusbar settings click popup fallback and settings chip right-edge rendering without default trailing space, popup-toggle stale marker recovery, isolated `projmux shell` tmux app launch/config generation including home fallback plus project-context default session targeting from `PROJMUX_CWD` or nearest project marker, app-owned project-name statusbar layout, distinct project badge color, and quiet debounced session-state autosave command/app-config trigger, shell-entry welcome release prompt and inline update handling, shell update skip-by-latest-tag behavior plus best-effort stale-cache refresh, pane/window keybindings, keymap.toml tmux override rendering/stale unbinds including retired direct/UserKey cleanup, Settings Keybindings root/list/detail capture flows including parse-error rows, unsafe raw capture and timeout guards, disable/reset writes, app config regeneration, live tmux source-file reload, no-live-tmux save behavior, and stage-by-stage apply recovery copy, window rename bindings, pane rename helper/binding, pane-exit rebalance command/hooks, hook-pane and after-select-pane based attention focus hooks, attention badge toggle/clear/list/window rendering, attach/current/kill/pin/preview/prune/sessions/session-popup/settings commands, switch, tag, tmux helper commands, update status/check/apply cache and installer detection including GitHub Release binary asset selection/extraction/replacement, doctor versioned read-only report/section/verbosity contracts, AI notify integration diagnostics, and Session State resume metadata diagnostics, Settings AI notify integration diagnostics read-only status/conflict/CLI guidance, and Settings About update status/check action wiring, untitled standalone popup-toggle marker close/config install, direct popup minimum sizing, AI picker minimum width and height, sidebar minimum width and compact badge spacing, preview select writes, popup render output after cycling, switch picker pin action behavior without inline settings rows, nested settings hub sections for AI defaults, project picker filesystem scan/pin actions, Project Root settings source/shadowing/set/current/clear flows, app/keybinding info including Ctrl-M rename forwarding, and About version/source rendering, switch picker focused-session kill, switch picker launcher-key abort bindings, switch explicit project-root, unconfigured-root, and weak managed-root heuristic parity, switch popup hiding new-session candidates while sidebar keeps create-capable rows, switch row project-name display with `~` pinned to the top and live-session-first sorting, pretty-path, preview context without retired Kube metadata, switch settings subcommand flows including add-current-pin, interactive add-pin picker, and settings label/preview polish including Phase 2 Settings/hookmaker/project-startup/trust/quit destructive row color regression strings, native preview wiring, baseline picker surface parity including prompt/footer/header fallback without app-name filler and search-key scoped card matching, sidebar compact action-only key footer, sidebar preview-window/start-position behavior without focus-time session switching, sidebar row/window ANSI styling with pane-aggregated attention badge state and AI topic labels, Alt+2/Alt+3 legacy popup row, preview-window, pane metadata, and pane-snapshot parity, switch read0 card rows with active/inactive title styling, right-side status badges, combined directory/git metadata with muted inactive branch styling, statusbar-matched block window tabs with window attention badges, read0 expect-key action parsing, restored pin/tag card badges, and restrained selected-row marker styling, switch preview metadata without duplicated directory/git rows, preview metadata rendering, popup pane display names for AI agents, AI topics, and shell commands, switch preview cycle bindings, sessions picker preview/cycle/open/kill wiring including attached-session fallback behavior, sessions picker launcher-key abort bindings, popup/switch preview summary formatting, popup sessions tmux entry helpers, switch/popup/session rendering, session identity, session-state Claude/Codex/Antigravity resume and declarative startup replay, session snapshot capture/autosave recipe classification including save/autosave pre-capture resume metadata refresh from live AI session ids including Antigravity conversation ids, Claude transcript paths, and Codex rollout log cwd matching/ambiguity skips, candidate discovery, config path derivation, popup preview read-models, and pure state rules including preview, tag, and lifecycle stores.
|
|
107
113
|
- `make test` also covers GitHub Release updater resource and integrity boundaries: bounded compressed responses, decompressed tar streams, total and per-file regular bytes, entry counts, required SHA-256 asset digests, release-host/redirect allowlisting, scratch cleanup, and no atomic replacement after rejection.
|
|
108
|
-
- `make test` also freezes Doctor schema-versioned plain/JSON fixtures, exact text/JSON row and section projection parity, shared-inventory parity, default-versus-verbose detail ownership, projected exit semantics, Phase 2 ordered runtime/log typed findings (backend, bounded socket probe/output, bounded regular generated-config reads and digest drift, state/log/journal permissions, non-regular/symlink/oversize rejection,
|
|
114
|
+
- `make test` also freezes Doctor schema-versioned plain/JSON fixtures, exact text/JSON row and section projection parity, shared-inventory parity, default-versus-verbose detail ownership, projected exit semantics, Phase 2 ordered runtime/log typed findings (backend, bounded socket probe/output, bounded regular generated-config reads and digest drift, state/log/journal permissions, non-regular/symlink/oversize rejection, and bounded safe errors), exact removed-flag migration errors, typed display-only remediation, raw path/socket/routing/message/config/argv negative coverage, support-report scalar-array allowlisting and schema compatibility, and the strict no-installer/no-migration/no-operational-journal write or repair boundary across the full flag matrix.
|
|
109
115
|
- `make test` also covers resume-title cleaning at the shared Claude/Codex/Antigravity choke point: known Claude command, local-command, task-notification, and system-reminder scaffolding is discarded so discovery falls through to the first real prompt, while inline `<`, leading HTML, and custom-element prompts remain intact.
|
|
110
116
|
- `make test` also covers the hardcoded usage HUD manual refresh key, full enabled-adapter collection through the existing throttle/backoff policy, cooldown no-op, cached popup rerender, and unchanged click/`u` cache-only behavior.
|
|
111
117
|
- `make test` also covers usage collection-failure visibility: Claude/Codex/Antigravity adapter tables for one, several, and all rows failing field validation plus an empty array, asserting the surviving snapshot contents, the exact bounded warning string (count + row-index/field reasons, capped at five with `(+N more)`), and negative assertions that a skipped row is never fabricated as `0%`, a zero reset, or a synthesized identity; the two shape failures that still abort a whole Claude collect (non-array `limits`, the 64-row cap) versus a partial collect that refreshes the healthy rows; privacy guards that no raw upstream value, reset timestamp, opaque bucket ID, or decoder message reaches a warning; `formatStatusUsage` staleness goldens for fresh/stale/very-stale with the fresh golden byte-identical to the pre-change output and the age indicator driven by `staleAfter`/`veryStaleAfter` alone; and the `usage.collect.outcome` operations-journal family — closed provider/failure schema with shape rejection, one row per failing provider, zero rows on success, once-per-run suppression of identical tuples, best-effort appends that never change the command result, and `projmux diagnostics log --component usage --tail N` rendering.
|
|
@@ -130,6 +136,8 @@
|
|
|
130
136
|
- `make test` / `make test-integration` / `make test-e2e`: keymap multi-stroke Phase 0 pins v2 `sequences` parse/render/merge, marker-only v1 migration with pre-v2 backup and rollback, repeat no-op bytes, deterministic two-to-four-stroke trie/table generation, shared prefixes across standalone/app actions, duplicate/strict-prefix/single-first-stroke conflict rejection, rejection of the undeliverable `C-m`/`C-i`/`C-[` spellings a terminal reports as `Enter`/`Tab`/`Escape`, unsafe and picker-local/command/output/default-sequence negatives, apply-ordered retire of the recorded roots/tables strictly before `source-file` (the generated config records that state but never removes it, because a `run-shell` loop is not ordered against a sourced file), native modified-stroke transport allowlisting without independent dispatch, and zero-write config/live failure ordering. The isolated real-tmux paths prove single dispatch for shared-prefix `mouse.toggle`/`window.create`, Escape and unknown continuation cancellation to `root` with zero raw pane input, terminal and native `send-keys -K -c` logical-stream parity, repeated apply idempotence, sequence removal cleanup, and unchanged single-chord behavior.
|
|
131
137
|
- `make test` / `make test-integration` / `make test-e2e`: keymap multi-stroke Phase 1 Settings authoring pins one shared 1–4 stroke Add/replace recorder, picker-local sequence exclusion, en-US/ko-KR locale and navigation golden reachability, shared reserved-key policy with first-plain rejection and later-plain acceptance, typed/captured canonical v2 keymap and generated-config byte parity, length-driven single/sequence add and atomic cross-kind replace plus strict-prefix conflict recovery, saved/prepared/running-session reporting including no-live-tmux recovery, native-macOS-on/off versus Linux/WSL logical authoring parity with transport differences confined to Delivery diagnostics, and zero keymap/generated-config/live-server mutation from navigation, cancellation, Backspace editing, or delivery tests.
|
|
132
138
|
- `make test`: Settings IA re-cut Phase 3 versioned keymap action-ID migration. The action catalog carries a `CanonicalID` beside the runtime `ID`, and `keymapActionManifest` derives the exhaustive old→canonical table from it, so `TestKeymapManifestCoversEveryCatalogActionExactlyOnce` fails on any action without a v1 spelling or any manifest row that resolves to no action, `TestKeymapCanonicalIDsAreUniqueAndDistinctFromLegacyIDs` rejects a canonical id that duplicates another action's canonical or legacy id, `TestKeymapRetiredIDsAreDisjointFromTheManifest` keeps retired ids out of the rename table and requires a remediation for each, `TestKeymapCanonicalIDsUseTheDottedSchema` pins the dotted lower-case shape and that the writer quotes it, and `TestKeymapDispositionIsExactlyOnePerTable` proves every table gets exactly one of canonical/coalesce/preserve-unknown/retired-remediation. Parser coverage adds the root `schema_version` marker with duplicate, forward-version and unknown-root-key refusals, and rejects an unquoted dotted table because that is a nested TOML table rather than a dotted key (`TestKeymapV1FileParsesAndRoundTrips`, `TestKeymapRejectsUnquotedDottedTableAndFutureSchema`). `TestKeymapMigrationPreservesEffectiveBindings` migrates a fixture carrying a custom multi-alias, `keys = []`, a transport-dependent additive alias, a legacy `plain` and a legacy `prefix` and requires the merged chord table to be identical before and after. The apply half is covered by `TestKeymapMigrationCoalescesIdenticalDualTables`, `TestKeymapMigrationRefusesConflictingDualTablesWithZeroWrites` (no keymap write, no backup, no tmux call), `TestKeymapMigrationPreservesUnknownTables`, `TestKeymapMigrationRepeatRunIsAByteWriteFreeNoOp` (unchanged bytes *and* unchanged mtime), `TestKeymapMigrationOnAbsentFileIsANoOp`, `TestKeymapMigrationBackupIsDigestNamedExclusiveAndReused`, `TestKeymapMigrationRefusesToClobberAForeignBackup`, `TestKeymapMigrationFailsClosedWhenTheDirectoryIsReadOnly`, `TestKeymapMigrationPreservesFileModeAndSymlinkTarget` (writes through the link, keeps 0600), `TestKeymapRollbackRestoresV0` and `TestKeymapRollbackRefusesACorruptBackup`. Convergence and the write boundary are pinned by `TestSettingsKeySaveConvergesAV0KeymapToV2`, `TestSettingsKeySaveAbortsWhenMigrationConflicts` (Schema/Saved/Prepared/Running-session stages), `TestTmuxApplyRefusesToApplyWhenTheKeymapMigrationFails`, `TestTmuxApplyNoReloadMigratesWithoutTouchingTheServer`, `TestConfigRenderReportsMigrationOnStderrAndWritesNoKeymap` (the preflight never reaches the generated artifact on stdout) and `TestReadOnlyRoutesNeverRewriteTheKeymap`, which drives the real dispatcher against an isolated HOME/XDG for `help`, `version`, `get`, `describe`, `agent usage`, `doctor --help`, both `config render` artifacts and `update apply --dry-run`. `TestUpdateDryRunPreviewsTheMigrationStageWithoutPromisingADiff` requires the preview to name the stage and leak no canonical id, and `TestPostUpdateApplyArgsAlwaysReachTheNewBinary` pins that `--no-apply` becomes `config apply --no-reload` so an installer suppresses the reload without skipping the migration. `TestKeymapAndRegistrySchemaMarkersAreIndependent` and `TestKeymapMigrationFailureLeavesTheRegistryUntouched` keep the keymap `schema_version` domain separate from the resource registry `apiVersion`/`schemaVersion` envelope, with separate backups and no shared transaction.
|
|
139
|
+
- `make test`: update-availability parity Phase 0 post-apply version verification. `update apply` now reads the version of the executable PATH resolves for `projmux` (falling back to the running executable when PATH has none) before publication and again after every stage, and reports an explicit failure instead of success when that version did not rise. `TestUpdateApplyFailsWhenTheInstalledVersionDidNotChange` covers a same-version npm reinstall, the same for `go`, `--no-apply`, an install already at the expected version, and a version that went backwards, and requires the message to name the current version, the expected version, and the install channel. `TestUpdateApplyWithoutACachedCheckStillNamesTheMissingExpectedVersion` keeps the expected slot filled with an explicit unknown rather than dropped, `TestUpdateApplySucceedsWhenTheInstalledVersionRose` keeps a real upgrade passing and names the verified executable, `TestUpdateApplyRefusesToReportSuccessWhenTheVersionCannotBeRead` turns a failing or unparseable probe into an unverified failure with no `>> verified:` line, `TestUpdateApplyVerifiesThePathResolvedExecutable` pins that the comparison reads the PATH-resolved binary and never an installer report, `TestUpdateApplyFallsBackToTheRunningExecutableWhenPathHasNoProjmux` keeps an off-PATH install verifiable, `TestUpdateApplyStageOrderIsUnchangedByVersionVerification` proves the published pre-converge/publication/`config apply` sequence is byte-identical for npm, go, and both `--no-apply` forms, `TestUpdateApplyDryRunPreviewsVerificationWithoutProbing` keeps `--dry-run` a pure preview across all three installers, and `TestParseProjmuxVersionOutput` pins the `projmux <version>` line reader.
|
|
140
|
+
- `make test`: update-availability parity Phase 1 channel-scoped availability source. The judgment authority is now chosen from the detected install channel: npm asks `registry.npmjs.org/projmux` for `dist-tags.latest`, while go, github-release, and source builds keep the GitHub release API, and both requests go through the same update HTTP client so the shell gate's timeout and redirect ceiling are unchanged. The cache gains an additive `source` field; an answer recorded for a different source is discarded rather than aged out, and a cache written before the field existed still reads as a GitHub release answer so the unchanged channels neither refetch nor lose their offer. `TestUpdateCheckPicksTheAvailabilitySourceFromTheInstallChannel` pins the per-channel routing, the recorded source in the cache file, and the single `v`-prefixed version format both authorities produce. `TestShellGateWaitsForTheNPMRegistryToPublish` covers the reported symptom in both directions with the GitHub authority made unreachable: no `u` while only GitHub has the version, `u` once npm publishes it. `TestUpdateJudgmentIsUnchangedForNonNPMChannels` holds the change-freeze boundary for go, github-release, and source, including a pre-existing source-less cache that must not trigger a refetch. `TestUpdateCacheRecordsItsAvailabilitySourceAndDropsAForeignOne` proves the recorded source, the discard on a channel switch (status and `cachedLatestVersion` both fall back to unknown), the refetch that replaces it, and the unchanged cache file name. `TestShellGateStaysSilentWhenTheAvailabilitySourceFails` covers an unreachable registry, an error status, a response with no `dist-tags`, and a non-JSON body, requiring the failure to be reported and the status report to name the state and the source. `TestSettingsAboutRefreshRowNamesTheAvailabilitySource` pins that the Settings > About refresh row names the authority the check actually contacts.
|
|
133
141
|
- Darwin native-key focused coverage includes portable physical chord parsing for Alt/Option, Control, Control-Option, Shift, letters/digits/navigation/function keys, `darwin && cgo` synthetic event-to-canonical-chord mapping for Option, Control, shifted letters, function keys, and arrows with unmatched/plain event pass-through, shared `keymap.toml` custom-action loading (including `M-a`), focused tmux client discovery, canonical `send-keys -K -c` dispatch through the existing root key table, native Settings Add key capture winning over simultaneous terminal text/error results after ignoring the activation Enter while the non-Darwin path preserves immediate terminal capture, default-on shell startup, `PROJMUX_NATIVE_KEYS=0`/`false` and Settings `[ui].native_keys = false` broker opt-out gates, broker direct no-op behavior, one-time pre-permission consent guidance, broker-only process replacement after an Accessibility wait so fresh macOS permission state is observed without restarting tmux, preservation of the direct no-server shutdown boundary while permission is pending, and an explicit Linux/Windows/no-CGO shell startup no-op boundary.
|
|
134
142
|
- `make test` covers the default picker key ownership across catalog, generated tmux config, terminal adapters, setup probes, and native popup close hints: `Alt-4` opens AI Resume as a guaranteed launch default, while `Alt-7` opens the AI split picker as its editable default.
|
|
135
143
|
- `make test` also covers canonical `projmux setup terminal` terminal detection, preview no-write, apply/backup, Ghostty candidate conflicts, Windows Terminal merge, config overrides, and symlink refusal/allow; removed top-level command dispatch/help negatives, canonical rejection and help absence of the removed legacy-only `--dry-run` flag, and setup remediation hints are asserted separately.
|
|
@@ -167,7 +175,7 @@
|
|
|
167
175
|
- `make test` also covers `projmux agent integrate tmux-bell` dry-run/install/remove tmux command planning, managed `alert-bell` hook append/idempotence/removal, preservation of unmanaged bell hooks, and canonical `projmux internal agent-hook ingest bell --pane` queue push/metadata/dedupe behavior for non-AI-managed panes.
|
|
168
176
|
- `make test` also covers upgrade-safe managed ingest producer convergence: fresh Codex/Claude/Antigravity hooks, Antigravity Statusline, and tmux bell writers use `projmux internal agent-hook ingest`; marker-owned 0.10.1 fixtures migrate transactionally while missing and unmanaged providers remain untouched; dry-runs show every target and old→canonical command; repeats perform zero file/tmux mutations; later-provider conflicts preflight before any write; injected Antigravity second-file and tmux Nth-command failures restore original bytes, options, and unrelated hooks; `config apply --no-reload` migrates files without live calls; and normal apply targets the requested exact `-L` socket. The pinned v0.10.1 GitHub Release post-replace fixture additionally proves that normal update executes exact `tmux apply` against the candidate and converges all four managed files plus the exact bell hook through the hidden current-apply handoff, while old `--no-apply` executes the candidate zero times and requires the documented explicit candidate `config apply --no-reload` split stage; current updater `--no-apply` continues to invoke the new binary automatically.
|
|
169
177
|
- `make test` also covers `projmux diagnostics agent-hook` tail/path rendering and bounded JSONL log trimming for ingest diagnostics.
|
|
170
|
-
- `make test` also covers the Phase 0 operational diagnostics foundation: closed safe-event schema, control/length/home sanitization, lossy stable top-level error messages with dynamic pane/title/body/transcript/config-value exclusion, exhaustive allowlisted top-level/multi-mode command classification including direct-help and explicit dry-run success exclusion with boolean `=false` safeguards, exactly-once state-changing/error outcomes with explicit zero-success/error-one coverage for the six automatic `internal agent-hook ingest`, attention arm/clear/window, session-state autosave, and recent-window record paths plus an explicit-mutation success regression, private permission repair,
|
|
178
|
+
- `make test` also covers the Phase 0 operational diagnostics foundation: closed safe-event schema, control/length/home sanitization, lossy stable top-level error messages with dynamic pane/title/body/transcript/config-value exclusion, exhaustive allowlisted top-level/multi-mode command classification including direct-help and explicit dry-run success exclusion with boolean `=false` safeguards, exactly-once state-changing/error outcomes with explicit zero-success/error-one coverage for the six automatic `internal agent-hook ingest`, attention arm/clear/window, session-state autosave, and recent-window record paths plus an explicit-mutation success regression, private permission repair, replace-existing bounded complete-record retention, corrupt-tail recovery, OS-owned inter-process lock identity/crash recovery and explicit 200 ms budget, concurrent short-process rotation, common text/JSON/tail/filter reader fixtures, and unchanged top-level runtime/usage/exitCoder output and exit semantics.
|
|
171
179
|
- `make test` also covers Operational diagnostics Phase 2 lifecycle adoption: the maintained explicit-mutation inventory (attach/current/kill/sessions/switch plus session-popup open, window recent, runtime prune, focus session switching, shell open-app, snapshot replay creation, popup-toggle cancel restore, and config apply); session create/attach/switch/kill and `config apply` start/outcome success/error tables; composite create-then-attach and prune-kill-then-attach failure-stage attribution while preserving one outer pair; shared run IDs and no duplicate top-level outcome; best-effort store/write failure injection; closed operation/code pairing; apply typed no-server versus generic runner failure classification; focus success sealing at the actual switch-client boundary; unchanged read-only/hot-path zero-success recording; typed no-write runtime health projection with latest-state and bounded failure-code semantics; Doctor runtime/log typed consumer reuse; and support-report raw socket/path/session/window/pane/name negative fixtures. `make test-integration` additionally runs real create/attach/switch/kill success/failure routing against a run-unique `-L` socket, validates exact lifecycle pairs and apply classifications without raw routing, and guards cleanup with the queried actual socket path beneath the run-unique smoke root.
|
|
172
180
|
- `make test` also covers Operational diagnostics Phase 3 Session State adoption: the closed outcome/operation/code/source/six-count schema; direct and popup manual save/delete, Settings latest/named save and current/project delete, deduplicated prune delete, and manual snapshot projection ownership; exact snapshot and item aggregates; error count omission; Settings feedback error capture; append-failure best-effort ownership; autosave session/config/source/interval/debounce/store/save/set-option failure ownership including quiet error-one and project on/off precedence over a broken global config, plus repeated autosave success/no-op zero-volume behavior; same-run coexistence with the runtime lifecycle pair; and seeded path/command/session metadata negative fixtures. `make test-integration` exercises manual save, autosave success/error, save → desired-state mutation → exact closed-Project restore projection → ordinary materialization, and aggregate delete against the guarded run-unique tmux socket and isolated XDG root, including exact snapshot cleanup and raw metadata negative checks.
|
|
173
181
|
- `make test` also covers Operational diagnostics Phase 4 notify/focus adoption: the closed transition/disposition/provider/category/route schema and cross-family rejection; explicit notify enqueue ownership with queued/deduplicated/failed outcomes; secondary attention and nested prune-reconcile enqueue ownership isolation with no-change zero volume; external-hook, Linux, WSL Toast/fallback, disabled, unavailable, desktop-dedupe, and visible-pane delivery outcomes; one shared process recorder with identical-tuple hot-path coalescing; append-failure best-effort semantics; focus direct/queue/toast request classification with unresolved, notify-only, fallback, and focused outcomes; same-run coexistence with the existing `session.switch` lifecycle pair without a generic duplicate; unchanged `PROJMUX_FOCUS_DEBUG` output; and seeded summary/body/tag/group/title/path/UUID plus support-report negative fixtures.
|
|
@@ -301,10 +309,71 @@
|
|
|
301
309
|
command, path, permission, and request content out of Registry, tmux, notify,
|
|
302
310
|
diagnostics, support, and Archive sinks.
|
|
303
311
|
|
|
312
|
+
- `make test` / `make test-integration`: Codex public turn control live-binding
|
|
313
|
+
compatibility uses one strict six-field tmux frame. Unit tests accept only
|
|
314
|
+
literal `\037` and raw unit-separator spellings without generic escape
|
|
315
|
+
decoding or value trimming; missing, extra, mixed, multiline, Registry
|
|
316
|
+
activation, runtime, Pane, thread, generation, and epoch drift all fail
|
|
317
|
+
closed with zero app-server writes. The isolated real-tmux regression removes
|
|
318
|
+
inherited `TMUX`/`TMUX_PANE`, uses run-unique `TMUX_TMPDIR` and `-L` names,
|
|
319
|
+
pins tmux 3.4's literal frame, and cleans only its immediately queried exact
|
|
320
|
+
socket path.
|
|
321
|
+
|
|
322
|
+
- `make test`: Invocation-target authority Phase 2 keeps create, topology replay,
|
|
323
|
+
resume, and native Codex on one typed exact-route Pane binder. The maintained
|
|
324
|
+
matrix crosses `-L`/`-S` with success and first/middle/last option-write
|
|
325
|
+
failure, requires complete managed/native option projection or reverse-order
|
|
326
|
+
compensation, and rejects route-less observer transport. Exact same-Window
|
|
327
|
+
create ignores inherited `TMUX_PANE` and private child anchors while retaining
|
|
328
|
+
explicit route-anchor plus socket/marker/PID guards. Observer tests pin
|
|
329
|
+
inherited-tmux environment removal, bounded connect/control fallback, exact
|
|
330
|
+
Registry/Pane/generation/thread guards, and explicit-route-only sink writes;
|
|
331
|
+
control tests require the resolved logical route plus current Pane/thread and
|
|
332
|
+
non-empty epoch before start, steer, interrupt, or approval can reach a wire.
|
|
333
|
+
|
|
334
|
+
- `make test` / `make test-integration` / `make test-e2e`: Codex observer
|
|
335
|
+
recovery Phase 0 gives native create and resume one post-commit startup
|
|
336
|
+
contract: the exact Agent/Pane/generation/runtime/route watcher must complete
|
|
337
|
+
a bounded private handshake as `provider-control-plane` with a non-empty
|
|
338
|
+
epoch and live control endpoint, or converge to `provider-hook` with an empty
|
|
339
|
+
epoch and a typed start-failed, early-exit, handshake-timeout, endpoint, or
|
|
340
|
+
control-endpoint reason. Fake-child and exact-route tests pin argv,
|
|
341
|
+
environment, ready-settle lifetime, process-group cleanup, and the full
|
|
342
|
+
startup matrix; observer/control integration pins exact active-turn steer and
|
|
343
|
+
socket removal. Binding replacement and cancellation keep old authority,
|
|
344
|
+
fallback, cleanup, progress, and sibling writes at zero.
|
|
345
|
+
|
|
346
|
+
- `make test` / `make test-integration` / `make test-e2e`: Codex observer
|
|
347
|
+
recovery Phase 2 revokes E1 control before publishing disconnect
|
|
348
|
+
invalidation, then retries only the same exact Agent/Pane/generation/runtime/
|
|
349
|
+
thread binding with 100ms exponential backoff capped at 1s and six failed
|
|
350
|
+
replacement attempts. A recovered observer publishes E2 only after a fresh
|
|
351
|
+
snapshot and control endpoint are ready; exhaustion publishes the single
|
|
352
|
+
typed `reconnect-exhausted` provider-hook fallback and exits. Unit tests pin
|
|
353
|
+
E1-to-E2 ordering, stale epoch/event zero-write, exact retry delays and count,
|
|
354
|
+
terminal cleanup, and independent multi-Agent convergence on one shared fake
|
|
355
|
+
endpoint. Public start, steer, interrupt, and approval tests require zero
|
|
356
|
+
app-server writes throughout invalidating, reconnect-gap, exhausted, and
|
|
357
|
+
identity-drift states. The isolated lifecycle E2E records fake provider
|
|
358
|
+
mutations across old -> down -> replacement and requires the same public
|
|
359
|
+
zero-write gap before a distinct ready epoch appears; the run-owned tmux,
|
|
360
|
+
proxy, control socket, and observer process cleanup contract is unchanged.
|
|
361
|
+
|
|
304
362
|
- `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
363
|
|
|
306
364
|
- `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.
|
|
307
365
|
|
|
366
|
+
- `make test`: Codex broker-facing app-server contract Phase 0 splits endpoint attach authority from daemon lifecycle authority and closes the inbound server-request, pre-turn, and experimental-capability seams the endpoint broker consumes. `TestEndpointAttachAndDaemonLifecycleAuthorityMatrix` closes the readiness x ownership x version table: a ready exact-current endpoint attaches whether or not the official daemon manager owns it, a skew, a protocol mismatch, an unknown running version, and unknown ownership each refuse with their own typed code, and lifecycle authority stays `managed` for a manager-owned endpoint, `cold-start` for a dead one, and `none` everywhere else including a ready unmanaged endpoint this process may talk to. `TestEndpointAttachAuthorityLeavesNativeActionReadinessUnchanged` keeps the existing native-action refusal and the rendered `Health` projection byte-identical, so the new axis adds no diagnostics surface. `TestAttachRefusesBeforeDialAndOpensExactlyOnceWhenAllowed`, `TestAttachToUnmanagedCurrentEndpointMutatesNoDaemonLifecycle`, and `TestAttachRefusesVersionSkewBeforeTheAttachDial` run the real path against a fake Codex whose every argv is recorded: an unmanaged exact-current endpoint initializes, resumes, and reads with a daemon start/stop/restart/kill/bootstrap/remote-control/login/config argv count of zero, while a skewed endpoint is refused after the read-only probe's single proxy open and before the attach dial, with zero thread requests. `TestServerRequestResponseIsOncePerConnectionAcrossRawScalarKinds`, `TestServerRequestRawIdentityIsPreservedFromFrameToResponse`, and `TestServerRequestResponseAuthorityDiesWithTheConnection` join the existing `TestServerRequestResponsePreservesRawScalarIDAndRejectsNonInt64` shape matrix to pin byte-faithful raw server-request identity from frame to response, keep a string id and a numeric id that render the same text as two distinct requests, refuse every duplicate answer with `ErrResponseAlreadySent` and zero wire bytes, and give a disconnected or replaced connection no pending response authority while a fresh connection starts a fresh ledger. `TestPreTurnThreadBootstrapSubscribesThenReadsWithoutTurns` and `TestPreTurnThreadBootstrapRefusesASubstitutedThread` pin the pre-turn bootstrap as exactly one explicit `thread/resume` subscription followed by one `thread/read(includeTurns=false)` snapshot against a no-turn fixture, with a substituted thread refused, a failed subscription stopping before the snapshot, and an empty id sending nothing. `TestNativeResumeUsesStoredThreadAndCreatesZeroThreads` and `TestNativeResumeRefusesAnUnnegotiatedConnectionBeforeTheWire` extend the same capability gate to `thread/resume`, whose always-on `excludeTurns` upstream also refuses without `experimentalApi`: a negotiated connection sends exactly one resume with the exact stored thread and zero `thread/start`, and an unnegotiated one is refused before any byte reaches the wire. `TestExperimentalInitializeGatesAdditionalRootsOnTheWire` is the initialize/request ordering ledger: additional writable roots reach `thread/start` and `thread/resume` only on a connection whose initialize negotiated `experimentalApi`, a plain initialize sends no capabilities at all and refuses roots with a typed `ErrExperimentalRequired`/`ErrUnsupported` before any request, and an empty root list stays off the wire. `TestStartDefaultThreadEmptyAndPromptedRequestCounts` gains the matching product-connection row: `--add-dir` roots on the current non-negotiated create connection refuse as a safe fallback with zero `thread/start` and `turn/start`. `TestBrokerFacingContractRetainsNoProviderContent` audits the added surface as closed field sets, drops the provider conversation title from the pre-turn snapshot, and keeps refusal codes and sentinels free of paths. `TestInstalledIsolatedPreTurnBootstrapSmoke` is opt-in through `PROJMUX_CODEX_BROKER_SMOKE_ROOT` with a matching contained `CODEX_HOME` and inherited `TMUX`/`TMUX_PANE` stripped; it attaches to a real unmanaged exact-current endpoint, creates a turnless thread, closes that connection, and reads the same thread's `includeTurns=false` snapshot from a second attach, recording only content-free identity and status. The explicit `thread/resume` subscription leg is recorded as typed evidence rather than asserted, because installed Codex 0.150.1 answers `thread/resume` only for a thread whose rollout already exists.
|
|
367
|
+
- `make test`: Codex hook trust-state preservation makes the managed marker block a definition-only ownership boundary rather than a byte range projmux may rewrite. `TestAIIntegrateCodexPreservesTrustStateInsideManagedBlock`, `TestAIIntegrateCodexPreservesStateInEveryMarkerPlacement`, and `TestAIIntegrateCodexDryRunOnConvergedTrustedConfigIsEmptyDiff` pin exact `[hooks.state]` and unknown-table preservation before, inside, and after the markers plus a byte no-op repeat; `TestManagedIngestConvergenceRepeatsWithoutTouchingCodexTrustState` extends the same contract to the automatic `config apply` / `make install` path with the config mode intact. `TestAIIntegrateCodexChangedHookIdentityDoesNotTransferTrust` and `TestAIIntegrateCodexRemovedHookKeepsUnknownAndUnrelatedTrustState` keep a changed or removed hook identity from inheriting or minting a hash while the untouched hooks keep theirs, and `TestProductionSourceNeverProducesCodexTrustState` audits that no production file names `trusted_hash` or `hooks.state` at all. `TestAIIntegrateCodexRecoversUnmarkedProjmuxHookWiring` and `TestManagedIngestConvergenceRecoversUnmarkedCodexHookWiring` adopt wiring whose markers a provider re-serialization dropped instead of failing `config apply`, while `TestAIIntegrateCodexStillRefusesHandWrittenUnmanagedHooks` and `TestAIIntegrateCodexRefusesUnmarkedRecoveryThatWouldRenumberHooks` keep the pre-existing refusal for hooks projmux did not author and for any adoption that would renumber a foreign hook entry.
|
|
368
|
+
|
|
369
|
+
- `make test`: Codex endpoint broker core Phase 1 adds the dark `internal/integrations/agents/codexbroker` package: one shared app-server connection, many fenced per-thread bindings, and a reconnect supervisor that reaches the endpoint only through an injected opener whose production default is the Phase 0 attach seam. `TestConcurrentBindingsRouteOutOfOrderResponsesAndInterleavedEventsToTheExactThread` runs two bindings over one in-memory endpoint whose control responses come back in the reverse order to the requests and whose notifications interleave: each response reaches exactly the caller that issued it, each notification reaches exactly the thread it names, and the shared connection ends with one connect and zero disconnects. `TestSlowBindingBacklogOverflowRevokesOnlyThatBindingAndKeepsTheSharedConnection` overruns one binding's bounded queue by exactly one event and requires `resync-required` on that binding alone, its stream closed after the events that fit rather than silently gapped, and the fast binding, the shared connection, and the open-attempt count untouched. `TestLongDisconnectReconnectsWhileABindingRemainsAndFencesTheOldEpochOut` injects a 3.75s outage through the fake clock (250ms + 500ms + 1s + 2s of capped jittered backoff across four refused attaches), reconnects while the binding remains, and then proves the retired epoch inert: its event is dropped as stale with zero fan-out and a fence minted on it refuses with `stale-connection-epoch` and zero requests on either connection, while the current fence still writes; unbinding the last binding freezes the open-attempt count and leaves no scheduled wait. `TestReconnectSnapshotAndLiveEventsMergeIntoOneOrderBehindTheBarrier` closes the snapshot/live race matrix - live events entirely before the snapshot, straddling it, and entirely after it - as one continuous snapshot-then-buffered-then-live order, and holds control authority shut for the whole barrier: `ControlAuthority` refuses `control-not-open` and a mutation attempted there writes zero bytes and enters no ledger row. `TestBarrierSnapshotFailureRevokesOnlyTheRefusedThread` splits the barrier's fault surface, so a live endpoint refusing one thread revokes only that binding with `snapshot-unavailable` while a disconnect during the snapshot leaves the binding intact for the next epoch's barrier. `TestMutationAtADisconnectBoundaryEndsIndeterminateWithZeroResends` pins the write ledger: an in-flight mutation cut by a disconnect ends `indeterminate` with one recorded attempt, the replacement connection receives zero requests, the resend counter stays at zero, and an endpoint that answers with an error stays classified `refused` rather than unknown. `TestThreadlessNotificationFansOutToNoBindingAndAttributesToNoAgent` runs the threadless fixtures - no params, a payload with no thread id, a blank thread id, and malformed params - through both the pure `AttributeNotification` seam and the live broker for zero binding fan-out and zero Agent attribution, with a following attributed event proving the stream was reachable throughout. `TestApprovalLeaseIsAnsweredOncePerConnectionAndDiesWithIt` binds the raw JSON-RPC request id, the thread, and both epochs into one lease, spends it once, refuses the duplicate, a forged thread, and an id-less lease, and shows connection replacement revoking it with zero answers on the new wire. `TestBrokerLifecycleRefusalsAndEpochsAreClosed` closes the construction and lifecycle table - unknown endpoint key, missing opener, blank thread id, duplicate bind, bind after close - and requires a rebound thread to take a fresh binding epoch that refuses the retired fence. `TestReconnectBackoffStaysInsideItsCappedJitterBounds` holds every attempt and jitter pair inside the half-base floor and the five-second cap, monotonically. `TestConcurrentBindUnbindReconnectAndSlowConsumerAreRaceAndLeakFree` runs binds, unbinds, connection replacement, delivery, and deliberate slow consumers together under `-race` and requires an idempotent concurrent `Close` with no goroutine left behind. `TestBrokerRetainsNoProviderContent` audits the retained surface as closed field sets, keeps every refusal, outcome, and origin code a bare content-free token, renders a typed refusal as its code alone over a path-bearing cause, and requires a prompt-bearing mutation and a payload-bearing event to leave no byte in the diagnostics or the ledger. `TestBrokerImportsNoRegistryTmuxOrCLIPackage` is the reverse-dependency audit: the package's only in-repo import may be `codexappserver`, so no Registry, tmux, or CLI package can reach back into the endpoint layer.
|
|
370
|
+
|
|
371
|
+
- `make test`: Codex endpoint broker runtime Phase 2 gives the dark `internal/integrations/agents/codexbroker` package an OS-level runtime: a per-state-domain singleton host, an owner-private discovery contract, and an authenticated content-free local IPC protocol, plus the minimal hidden `internal codex-broker serve|probe` entrypoint in `internal/app` that hosts one and reaches one. `TestConcurrentClientsShareOneRuntimeProcessAndOneUpstreamConnection` starts four separate client processes, holds them at one barrier, and releases them together against one empty state domain: the ledger the runtime shares with the parent must show exactly one published runtime, exactly one upstream connection, and one snapshot per client, with every client reporting the same runtime identity, because the socket bind is the singleton mutex and the exclusive startup lock keeps a losing starter from reclaiming the winner's socket. `TestDiscoveryCredentialAndPermissionFailuresBindNothingAndWriteNothing` runs the adversarial matrix - a wrong credential, an absent credential, a foreign endpoint key, a malformed first frame, a record readable beyond its owner, a record replaced by a symlink, a record naming another endpoint, no record at all, and a relative or empty state domain - and requires zero bindings, zero upstream requests, and zero upstream snapshots after all of them; an unshared protocol window gets a runtime of its own because that refusal also starts a drain. `TestStaleRuntimeArtifactsAreReclaimedOnlyByExactOwnerProof` is the ownership fixture: a live runtime's socket is reused rather than replaced with a typed `host-live`, a socket left behind by a killed runtime is removed, a regular file or a symlink at the socket path is refused as `discovery-untrusted` and left byte-identical, and the last row varies the record's pid across live, absent, and foreign values to require an identical decision every time, because a reusable pid may not be ownership authority. `TestRuntimeCrashRevokesTheOldEpochBeforeANewRuntimeRestoresAuthority` kills a real runtime process out from under a bound client: the binding revokes with `host-unavailable` before anything can present its fence, its stream closes, `ControlAuthority` and `Submit` both refuse with zero added upstream writes, and `Ensure` then proves the abandoned socket stale, starts a replacement with a different runtime identity, and restores authority only through a fresh snapshot, where a foreign epoch still refuses and writes nothing. `TestRestoredAuthorityOpensOnlyBehindTheSnapshotBarrier` holds the runtime's snapshot open and requires a bound-but-unopened client to refuse `control-not-open` with zero endpoint calls until the snapshot crosses the socket. `TestLastBindingRemovalIdlesTheRuntimeOutAndLeavesNoArtifact` keeps a held binding alive well past the idle bound, then requires the runtime to stop after the last binding is removed, remove exactly the socket and record it published, and leave no goroutine behind. `TestIncompatibleClientDrainsTheRuntimeWithoutSeveringLiveBindings` is the rolling binary replacement matrix: overlapping version windows negotiate down and reuse the running runtime in both directions, while a disjoint window is refused by name with `drain-required` without replacing the runtime or its socket, the live binding keeps delivering and keeps control authority through the drain, a new bind is refused so the drain stays finite, and only after the last binding drains does a replacement start under exact owner proof with a new runtime identity. `TestRuntimeStateAndDiagnosticsRetainNoProviderContent` drives a prompt-bearing mutation and a payload-bearing event across the socket and then searches every file the runtime persisted in its state domain for them, and pins `HostStats` as a closed set of tokens and counters. `TestRuntimeProtocolNegotiationAndFrameBoundsAreClosed` closes the negotiation table and requires an oversized frame to be refused on write with zero bytes emitted and on read. `TestInternalCodexBrokerServePublishesAndIdlesOutWithoutTouchingAnyDaemon` runs the real entrypoint against an isolated state domain: it publishes an owner-only record, reports readiness, and idles out without reaching any Codex daemon, because the broker opens its upstream connection only once a binding exists. `TestInternalCodexBrokerRouteStaysHiddenPlumbingWithNoPublicSurface` is the scope audit - no public route gains a broker spelling, the runtime stays inside the hidden `internal` namespace and out of the canonical command projection, every malformed invocation is a usage error, and `probe --no-start` refuses with `host-unavailable` while creating nothing outside the broker artifact directory. `TestCodexBrokerStateDomainFollowsTheProjmuxStatePaths` pins the singleton to the installation's state directory rather than to a working directory or a pane.
|
|
372
|
+
- `make test`: Codex native lifecycle and control cutover Phase 3 makes the endpoint broker the product's endpoint producer. `TestBrokerBoundAgentsShareOneUpstreamConnectionWithIndependentBindingEpochs` binds a prompted-create Agent and a stored-resume Agent through the real runtime host and requires one upstream connection between them, one shared connection epoch, two different binding epochs, and interleaved events that reach only the thread that named them. `TestBrokerThreadlessAndForeignEventsReachNoExactAgentBinding` runs the ambiguous fixtures - no thread id, a blank one, a foreign thread, and a foreign thread sharing the bound Agent's working directory - and requires the exact binding to receive none of them while a following attributed event proves the stream stayed live. `TestBrokerEpochRotationFencesTheOldEpochOutWithZeroEndpointWrites` replaces the connection under a live binding: the retired epoch's stream closes, its steer and its approval answer are both refused, the replacement connection records zero steer requests and zero answers from it, the binding epoch survives the reconnect unchanged, and the replacement epoch starts a turn normally. `TestBrokerRuntimeLossEndsTheEpochAndRefusesEveryLateMutation` takes the runtime away from a bound client and requires the epoch to end before any caller can present its fence, every late mutation and answer to be refused with zero endpoint writes, and a reopen against an unpublished runtime to refuse rather than start one. `TestBrokerDisconnectEndsTheEpochBeforeAnyReplacementBarrier` covers the promptness half of the disconnect contract: a binding survives an outage so the broker can keep reconnecting, so the binding's out-of-band suspension - not the ordered stream, which has nothing to say until the next barrier - retires the live epoch immediately, refuses its late mutations with zero endpoint writes, and keeps the stale barrier from being re-served while the endpoint is gone. `TestBrokerEpochBacklogOverflowEndsOnlyThatEpochStream` overruns one Agent's bounded stream and requires that Agent alone to be cut to a resync while its sibling on the same shared connection keeps receiving its own events. `TestBrokerApprovalLeaseAnswersTheExactRawRequestExactlyOnce` carries an inbound server request through the broker to the app layer with its byte-faithful raw id and its normalized rendering intact, decodes it with the existing envelope decoder, answers it once, and requires the repeat to be refused with exactly one entry in the endpoint's answer ledger. `TestBrokerEpochReadsTheExactLifecycleSnapshotThroughItsOwnFence` pins the control epoch's state input to the same fence its mutations use. `TestNativeLifecycleProducerIsExactlyOnePerActivationGeneration` and `TestBrokerProducerKeepsRecoveringPastTheLegacyAttemptCeiling` are the single-producer and persistent-reconnect audits: the producer decision is total and never yields both, and a producer that owns its own reconnect keeps recovering well past the legacy six-attempt budget without ever publishing the terminal `reconnect-exhausted` fallback, while that budget stays byte-identical for the producer that still needs it. `TestBrokerCutoverPreservesTheHookFallbackVocabulary` is the compatibility audit for the authority sources, the hook-suppression rule, and the pre-mutation convergence to `provider-hook`. On the endpoint side, `TestNativeCreateAndResumeAttachToTheExactCurrentUnmanagedEndpoint` runs the real create and resume paths against a fake Codex whose every argv is recorded and requires an exact-current endpoint the official manager does not own to accept one `thread/start`, one `turn/start`, and one `thread/resume` with a daemon start/stop/restart/kill/bootstrap/remote-control/login/config argv count of zero, while `TestNativeCreateAndResumeKeepSafeFallbackOnEveryRefusedAttachRow` keeps a skewed and an ownership-unknown endpoint refusing before the connection opens, as a safe fallback carrying its typed reason and with zero thread or turn requests. `TestEndpointAttachAuthorityIsTheOnlyWidenedProductRow` walks the availability x ownership x version table behind the product's own source and availability preconditions and requires exactly one row to differ from the retired native-action gate: the ready, exact-current, unmanaged one. `TestInstalledIsolatedBrokerNativeBindingSmoke` is opt-in through `PROJMUX_CODEX_CUTOVER_SMOKE_ROOT` with a matching contained `CODEX_HOME`, an isolated state domain, and inherited `TMUX`/`TMUX_PANE` stripped, and it is the only check that runs the cutover against a real Codex app-server: a direct `codex app-server --listen unix://` under that contained `CODEX_HOME`, which upstream reports as running with no daemon backend. It requires that endpoint to be unmanaged, exact-current, attach-allowed, and to carry no daemon lifecycle authority; runs the product's own prompted create there so the thread's rollout materializes; binds that exact thread through the product session over a runtime it publishes itself; starts one turn through the resulting broker epoch and requires the epoch's own fenced snapshot to show that exact turn in progress before steering it through the same wire; interrupts it; and then requires the runtime to leave no artifact behind. Every `codex` argv the whole path ran is recorded by a PATH shim the test installs, and the run fails unless all of them are `app-server proxy` or `app-server daemon version`, which is the zero-daemon-lifecycle-mutation half of the acceptance against a real endpoint rather than a fake one.
|
|
373
|
+
- `make test`: Codex interactive-only mode and native-required prompted create makes an unproven native authority a typed refusal instead of a silent plain-CLI Agent. `TestPromptedNativeCodexCreateIssuesOneTurnAndNeverRepeatsThePromptInPaneArgv` and `TestStartDefaultThreadEmptyAndPromptedRequestCounts` pin one `thread/start` plus one `turn/start` per payload with the prompt absent from Pane argv and from the Registry. `TestUnavailableNativeCreateRefusesInsteadOfSilentlyCreatingAPlainAgent` and `TestUnavailableNativePickerResumeRefusesInsteadOfRebindingOntoTheRolloutLane` close the pre-mutation refusal matrix for the two Agent-creating routes at zero Registry and zero tmux mutation, `TestIndeterminateNativeCreateRefusesASecondLaneAndWritesZero` keeps the post-mutation row refusing without offering any second lane, and `TestUnavailableNativeResumeKeepsTheStoredConversationOnTheProviderResumeLane` holds the boundary at `agent resume`, which creates no Agent and keeps its safe fallback to one provider resume of the stored conversation. `TestInteractiveOnlyIsTheOnlyPlainCodexLaneAndBothSpellingsAreEquivalent` pins flag acceptance, manifest and rendered help parity, and byte-equal output across `create agent --provider codex --interactive-only` and `create codex --interactive-only`, plus the exit-2 non-Codex-provider refusal at zero transactions. `TestStartDefaultThreadDeliversAdditionalRootsOrFailsClosed` is the roots capability matrix: the exact cleaned list on a negotiated connection, a fail-closed typed refusal when the endpoint cannot carry it, and nothing on the wire for an empty list. `TestDefaultNativeCodexFanOutRefusesWithZeroMutationsAndInteractiveOnlyKeepsCardinality` refuses a prompted multi-Window native create with zero threads, Panes, Registry writes, and tmux objects while keeping the `--interactive-only` and empty-prompt fan-out cardinality. `TestEmptyPromptCodexCreateIsByteForByteUnchangedByTheNativeRequiredGate` and `TestClaudeAndAntigravityLifecycleAndHookContractAreUnchangedByTheNativeGate` guard the deferred empty-prompt boundary and the unchanged Claude/Antigravity hook activation and fan-out contract, and `TestCodexNativeLaunchOutcomeTableIsClosed` keeps the closed outcome table describing exactly these rows.
|
|
374
|
+
- `make test`: Codex per-Agent native observer retirement makes the endpoint broker the only native lifecycle, control, and approval producer, and projects its content-free telemetry onto the diagnostics surfaces. `TestNativeLifecycleProducerIsExactlyOnePerActivationGeneration` is the source audit that replaced the old producer selection: `internal/app` opens no app-server proxy of its own anywhere, and the native lifecycle observer is built with exactly one connection opener, so there is no second branch a dual-write could take. `TestRetiredCodexAppServerWatchRouteIsRefused` proves the retired producer cannot be revived by argv, hook, or script: the `codex-appserver-watch` ingest route is gone and the surviving route names the broker binding it consumes. `TestBrokerProducerKeepsRecoveringPastTheLegacyAttemptCeiling` and `TestRetiredObserverRecoveryBackoffIsCappedAndNeverExhausts` replace the fixed six-attempt exhaustion budget with the whole reconnect contract: recovery is bounded only by the capped backoff and by the exact binding, the terminal `reconnect-exhausted` fallback is never published again, the reconnect gap writes zero bytes upstream, and the only exits are a replaced binding and a cancelled context. `TestCodexNativeObserversSharingOneEndpointRecoverIndependently` keeps that containment per activation: one activation reconnecting onto its own replacement projects nothing onto its sibling and cannot terminate it. `TestRuntimeTelemetryCountsOneConnectionForConcurrentBindings` is the number the retirement is measured by - three bindings, one upstream connection, one live client session, and a telemetry frame that reaches no endpoint - and `TestRuntimeTelemetryNamesEachBindingScopedFault` keeps queue eviction, a refused reconnect snapshot, and a fenced-out epoch separable instead of collapsed into one revocation count, with a voluntary unbind never counted as a fault. `TestBrokerDiagnosticsReadCreatesNothing` and `TestBrokerDiagnosticsReportTheRestingAbsentRuntime` fix the read-only contract of that projection: asking about the broker publishes no discovery artifact, reclaims nothing, starts no runtime, and reports a machine with no live native Agent as the resting `absent` state under its closed refusal rather than as a fault. `TestBrokerDiagnosticsProjectOneConnectionAndTypedFaults` and `TestCodexBrokerDiagnosticIsContentFree` pin the rendered value: one connection per effective endpoint regardless of binding count, a down connection distinguished from one that never existed, and a closed field set carrying no provider content and no location, with an unclassified failure never leaking its message. `TestDeclaredPlainCodexLaneNamesOnlyTheByDesignLanes`, `TestDeclaredLaneVocabularyIsClosedAgainstUnknownPaneValues`, and `TestDeclaredLaneReadsBackFromTheExactPaneOption` give the two by-design plain-CLI lanes - an empty-prompt default create and an explicit `--interactive-only` - a typed declaration that survives the round trip through the exact pane option, while a forged or unknown value is refused so a declaration cannot hide a real fallback. `TestManagedCodexAuthorityCensusSeparatesDeclaredFromUnexplainedFallback` is the acceptance surface: the declared lanes are counted apart from the unexplained native fallback that the native-authority contract requires to be zero, sibling providers never enter the census, and the classes partition the managed Codex Agents exactly. `TestPlainCodexCreatesWriteTheirDeclarationOntoTheExactPane` closes the write half through the real create route and the real pane binder, so a create route that stopped passing the declaration - or a binder that stopped writing it - would fail rather than silently return every by-design plain Agent to the unexplained count.
|
|
375
|
+
- `make test-e2e-shards`: the Linux e2e suite is scheduled one shard per runner. `scripts/test-e2e-docker.sh` accepts `PROJMUX_E2E_LINUX_SHARD=<shard>` and `PROJMUX_E2E_SUITE=codex-lifecycle|npm-staging` as mutually exclusive single-suite selectors that reuse the existing `E2E_SCENARIO` replay plumbing and derive their exact terminal inventory from the same `test/e2e/linux-shards.tsv` row the replay router reads; setting none leaves the default four-shard parallel run plus both suites unchanged. `test/e2e/shard-contract.sh` pins the CI topology to that manifest: `e2e-linux` matrixes exactly the manifest shards and `e2e-suite` the codex/npm pair, both `fail-fast: false` on their own `ubuntu-latest` runner with their own `timeout-minutes` and four uniquely named evidence artifacts, and both are `needs` and `--required` children of the aggregate `Test` gate so no shard result can be hidden. It also pins the thin `E2E Tests` job that keeps reporting the status-check name the branch ruleset requires: it is `if: always()` and evaluates both matrix results through the same `required-gate.py`, because a required context that is never reported stays pending forever instead of failing, so a skipped or missing job there would deadlock merges rather than turn a check red.
|
|
376
|
+
|
|
308
377
|
## When To Update This List
|
|
309
378
|
|
|
310
379
|
- A feature moves between unit, integration, and e2e coverage levels.
|
|
@@ -312,12 +381,20 @@
|
|
|
312
381
|
- Behavior changes require new parity assertions or a different e2e scenario.
|
|
313
382
|
- A target stops being authoritative and must be replaced.
|
|
314
383
|
|
|
315
|
-
- `make test` / `make test-integration` / `make test-e2e`:
|
|
384
|
+
- `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.
|
|
385
|
+
- `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.
|
|
386
|
+
- `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
387
|
|
|
317
388
|
- `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
389
|
|
|
319
390
|
- `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
391
|
|
|
392
|
+
- `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.
|
|
393
|
+
|
|
394
|
+
- `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.
|
|
395
|
+
|
|
396
|
+
- `make test` / `make test-e2e`: Sidebar popup runtime-stop anchor authority gives the `sessionizer-sidebar` popup one anchor transport end to end. The generated Alt-1 launcher already passes the origin Pane as a `switch --ui=sidebar --anchor %N` operand and supplies no `__PROJMUX_RUNTIME_ANCHOR_PANE`, so both stop consumers the sidebar reaches now read that operand: `stopManagedProjectSession` resolves its route through `resolveInvocationRuntimeMutationRouteWithAnchor`, and `executeUnmanagedRuntimeStop` takes the anchor as an explicit argument that non-popup callers leave blank. `TestSwitchSidebarKillStopsManagedProjectFromAPopupWithoutTMUXPANE` and `TestSwitchSidebarKillStopsUnmanagedSiblingFromAPopupWithoutTMUXPANE` pin one exact `kill-session -t $N` plus the fallback client handoff in the real popup shape -- a valid inherited `TMUX` receipt with `TMUX_PANE` and the private anchor env both blank -- for the managed Project row and the discovered unmanaged sibling; `TestSwitchSidebarKillWithoutFallbackRefreshesWithoutWriting` keeps a sidebar with no other live session on a plain refresh with zero writes; `TestSwitchSidebarKillWithoutAnchorOperandRefusesWithZeroWrites` and `TestSwitchSidebarKillRefusesAMalformedAnchorOperand` keep a missing or non-`%N` operand a typed route refusal rather than a silent no-op, with tmux writes at zero and the managed route refusing before the fallback handoff; and `TestSidebarPopupAnchorSupplierAndStopConsumerShareOneTransport` fails if the launcher stops emitting the operand or a consumer stops reading it. `TMUX_PANE` is never promoted to anchor authority, and the route authority rules, `session-popup`'s private env transport, and every other popup mode are unchanged. L11's two sidebar Ctrl-X drivers now run with `TMUX_PANE` unset so they exercise the operand instead of a shell export.
|
|
397
|
+
|
|
321
398
|
## E2E Required-Gate Evidence
|
|
322
399
|
|
|
323
400
|
- `make test-e2e-coverage` is the maintained AGS-OEDR inventory gate. It
|
|
@@ -362,6 +439,107 @@
|
|
|
362
439
|
Codex, and npm consumers. Local Security is the exact three-way
|
|
363
440
|
`make security` topology; CI's required branch-protection context is the
|
|
364
441
|
fail-closed aggregate named `Test`.
|
|
442
|
+
- `test/lib/smoke.sh` owns every scenario wait through `smoke_wait_until
|
|
443
|
+
<seconds> <description> <command>`; `test/e2e/` holds no raw `for _ in
|
|
444
|
+
{1..N}` polling loop. A wait that expires fails with the description it was
|
|
445
|
+
given plus the budget applied, elapsed time, attempt count, and last predicate
|
|
446
|
+
status, so no scenario can enter its next assertion with the condition still
|
|
447
|
+
unmet. `E2E_WAIT_SCALE` multiplies every budget for a slow runner.
|
|
448
|
+
`test/e2e/reliability-contract.sh` F08/F09 pin both halves: a deliberately slow
|
|
449
|
+
fixture times out with its own description and state dump and then passes once
|
|
450
|
+
the scale buys it time, and the timeout instant itself moves with the scale.
|
|
451
|
+
- `test/e2e/evidence-contract.sh` (`make test-e2e-contract`) and
|
|
452
|
+
`test/e2e/reliability-contract.sh` (`make test-e2e-reliability`) keep the persisted
|
|
453
|
+
`projmux.e2e-attempt/v1` evidence and success result hash stable while adding
|
|
454
|
+
a separate stderr-only `projmux.e2e-terminal/v1` first-failure record. Its
|
|
455
|
+
closed fields identify scenario, phase, owner, shard, status, repository
|
|
456
|
+
source/line, sanitized single-scenario command, binary/state hashes, and exact
|
|
457
|
+
replay without raw argv. Synthetic L06/L08/L16 fixtures pin eight-racer lock
|
|
458
|
+
state, bounded Registry changed paths plus pending owned state, and child/file/
|
|
459
|
+
exact tmux state plus sanitized hashed tails; denylist checks reject token,
|
|
460
|
+
raw HOME/path, full Registry, and raw command leakage. Normal scenario meaning,
|
|
461
|
+
one-build/four-shard topology, and `.bin/e2e-evidence` retention are unchanged.
|
|
462
|
+
- `test/e2e/evidence-contract.sh` also pins failure-evidence preservation. A
|
|
463
|
+
failing attempt folds the emitted terminal record and the L06/L08/L16
|
|
464
|
+
diagnostic into the persisted `projmux.e2e-attempt/v1` artifact as the additive
|
|
465
|
+
optional `terminal_line`, `terminal_status`, `terminal_source`, and
|
|
466
|
+
`diagnostic` fields; a begin/pass record keeps its exact prior field set and
|
|
467
|
+
bytes. Degraded input no longer discards the whole attribution: a zero
|
|
468
|
+
`BASH_LINENO[0]`, an out-of-range wait status, and a non-allowlisted source
|
|
469
|
+
path each drop only their own field and mark the terminal record
|
|
470
|
+
`attribution=partial` while scenario, phase, owner, shard, and replay survive.
|
|
471
|
+
The `PROJMUX_E2E_INTENTIONAL_ZERO_LINE` fixture drives that path through the
|
|
472
|
+
real harness, and CI preserves the attempt evidence subtree — excluding
|
|
473
|
+
`binary/` — as a downloadable artifact on both failing and passing e2e runs.
|
|
474
|
+
- `test/e2e/evidence-contract.sh` also pins observed failure classification. The
|
|
475
|
+
`class` field is derived, not declared: the recorder compares the attempts
|
|
476
|
+
already recorded for the same `binary_sha256` under `--evidence-root` and only
|
|
477
|
+
falls back to a caller's `--class` when they say nothing. A build seen both
|
|
478
|
+
failing and passing is nondeterministic and splits by whether the source of
|
|
479
|
+
that nondeterminism is ours — `flake` for a scenario we own, and
|
|
480
|
+
`unowned-nondeterminism` for one like `C01` that observes the real Codex model
|
|
481
|
+
— so a later quarantine policy never sets a deadline nobody here can meet. A
|
|
482
|
+
failure reproduced on the same build is `deterministic-regression`; a failure
|
|
483
|
+
nobody has retried is `unrepeated-failure` and no longer borrows the
|
|
484
|
+
reproduction claim. Where a derivation happened the record carries the
|
|
485
|
+
additive optional `class_basis` and `prior_outcomes`; where it did not, the
|
|
486
|
+
record keeps its exact prior field set and bytes. `e2e-evidence.py classify`
|
|
487
|
+
exposes the predicate as a truth table, and `e2e-evidence.py flake-rate <dir>`
|
|
488
|
+
reports per-scenario flake rate over downloaded attempt evidence plus an
|
|
489
|
+
`E2E_FLAKE_FLIP` line for every stored class the other attempts contradict.
|
|
490
|
+
L06 diagnostics are derived the same way: `attribution` names what the racers
|
|
491
|
+
were observed to do instead of always claiming lock exhaustion, `acquire` is
|
|
492
|
+
no longer unconditionally `contended`, and `lock_age_ms` is paired with
|
|
493
|
+
`lock_observed` so an unread lock file cannot report a scenario timer as lock
|
|
494
|
+
age. That renamed payload travels as `projmux.e2e-diagnostic/v2` while the
|
|
495
|
+
unchanged L08/L16 payloads stay `v1`, and evidence retained from before the
|
|
496
|
+
rename still validates and still aggregates. `PROJMUX_E2E_ATTEMPT` now defaults to `GITHUB_RUN_ATTEMPT`, so a rerun's
|
|
497
|
+
evidence is no longer written as attempt 1.
|
|
498
|
+
- `make test` / `go test -race` / `make test-e2e`: the Registry mutation lock
|
|
499
|
+
succeeds on a deadline, not on an attempt budget. `acquireLockWithDeadline`
|
|
500
|
+
blocks on `flock(LOCK_EX)` against a persistent `registry.json.flock`
|
|
501
|
+
descriptor so the kernel owns the wait queue, keeps writing the legacy
|
|
502
|
+
`registry.json.lock` O_EXCL marker for the compatibility window an older
|
|
503
|
+
install still observes, and treats a marker as stale only when its recorded
|
|
504
|
+
pid is gone rather than after 30s of wall clock. The separate recovery lock
|
|
505
|
+
(`acquireRecoveryLock`) keeps its 200-attempt budget and wall-clock stale
|
|
506
|
+
window. `TestRegistryLockFailsOnItsDeadlineNotOnAnAttemptBudget` pins
|
|
507
|
+
`ErrLockTimeout` and the absence of any attempt count in the failure,
|
|
508
|
+
`TestRegistryLockDeadlineIsMeasuredOnTheInjectedClock` spends a whole budget
|
|
509
|
+
in simulated time, `TestRegistryLockReclaimsAMarkerOnlyWhenItsOwnerIsGone`
|
|
510
|
+
reclaims a reaped owner at once while a live, empty, malformed, or unreadable
|
|
511
|
+
owner is never reclaimed, `TestRegistryLockKeepsMutualExclusionWithAMarkerOnlyInstall`
|
|
512
|
+
covers the compatibility window in both directions, and
|
|
513
|
+
`TestRegistryLockGrantsEveryWriterUnderDeliberateContention` plus the fixed-clock
|
|
514
|
+
`TestConcurrentUpdatesSerializeThroughTheRegistryLock` require every contending
|
|
515
|
+
writer to be granted with no overlapping critical sections. The degraded-mode
|
|
516
|
+
gate in front of an ordinary mutation is now a suspicion confirmed under the
|
|
517
|
+
lock rather than an immediate refusal: a commit publishes the initialized
|
|
518
|
+
marker before it renames the staged registry into place, so an unlocked
|
|
519
|
+
observer could read a healthy neighbour's in-flight commit as state loss and
|
|
520
|
+
refuse a writer that never reached the lock.
|
|
521
|
+
`TestAConcurrentWriterIsNotRefusedInsideAnotherCommitsMarkerWindow` builds that
|
|
522
|
+
window directly and requires the second writer to wait through it. The
|
|
523
|
+
`Race Tests` CI job runs the metadata, notify, and recent-window packages
|
|
524
|
+
under `-race`.
|
|
525
|
+
The real-tmux L06 burst still requires all eight exact-socket creates to
|
|
526
|
+
converge on one Window with nine unique mirrored Panes and no lock or staged
|
|
527
|
+
residue; it is a regression guard, not the evidence for the lock change.
|
|
528
|
+
- `make test` / `make test-integration`: attention omitted-target convergence
|
|
529
|
+
- `make test` / `make test-e2e`: Closed-Project startup mode selection is one adjudication shared by both entry points. `switch sidebar-open` re-execs across a process boundary, and before this change the picker-off sidebar always emitted `continue` and the re-exec trusted that token verbatim, so an unregistered root on a fresh install failed in `ContinueProject` with `continue project unavailable: no usable snapshot`. `defaultProjectStartupMode` now owns the picker-off decision -- the Settings/runtime sentinels, the `$HOME` guard, and the single `ProjectRegistered` read -- and all three closed-Project entry points call it: `openProjectTarget`, the sidebar emit point `openProjectTargetPathFromSidebar` that builds the `--mode` token, and `runSidebarOpen`, which re-adjudicates a `continue` that crossed the re-exec boundary. `TestProjectStartupModeSelectionIsOneDecisionAcrossEntryPoints` drives the (registered root) x (picker on/off) x (explicit picker choice) table through the in-process open, the sidebar emit point, and the sidebar continuation, requiring the emitted `--mode` token and the mode the continuation acts on to agree with the in-process choice. `TestSidebarOpenPromotesUnregisteredRootToFreshWhenPickerIsOff` is the regression guard, `TestSidebarOpenKeepsRegisteredRootOnContinue` keeps a registered root and its retained topology on `continue`, `TestSidebarOpenHonorsExplicitPickerChoice` proves an explicit picker-on choice is never promoted, `TestSidebarOpenNeverDemotesAnArrivingFreshMode` keeps the re-adjudication one-directional, `TestSidebarOpenSurfacesRegistrationReadFailure` refuses to answer an unreadable Registry with a mode, and `TestSidebarOpenContinueOnUnregisteredRootKeepsTheSnapshotRefusal` pins the no-usable-snapshot message for the continue that is still reachable by explicit choice. The L11 closed-startup e2e keeps its direct `--mode fresh` / `--mode continue` execution paths and adds step 9, `Closed Project startup mode selection`, which covers the arrival half of the decision: the picker-off sidebar now emits `fresh`, so the step deliberately forwards the legacy neutral `continue` token an older client can still send, against an unregistered root in an empty Registry on its own isolated socket/HOME/XDG/TMUX_TMPDIR, and requires the continuation to re-decide it so the Project is minted and its session opened rather than the snapshot refusal. The emitted half is covered by the unit tests through `sidebarEmittedStartupMode`.
|
|
530
|
+
is enforced by `TestAttentionMutationOmittedTargetMatchesExplicitPaneLedger`,
|
|
531
|
+
`TestAttentionMutationOmittedTargetRefusesWithoutExactInvocationPane`, and
|
|
532
|
+
`TestAttentionMutationHelpUsesOptionalPaneCatalogUsage`; generated/reference/
|
|
533
|
+
guide parity stays pinned by `TestAttentionMutationOptionalPaneDocsParity`,
|
|
534
|
+
and generated hook argv by
|
|
535
|
+
`TestGeneratedAttentionFocusHooksKeepExplicitPaneArgv`. The unit tables pin
|
|
536
|
+
three-verb explicit/omitted handler-ledger parity, exact `%N` targeted
|
|
537
|
+
reobservation, no targetless tmux calls, and no-write refusal for outside,
|
|
538
|
+
missing, blank, malformed, stale, or contradictory inherited evidence. The
|
|
539
|
+
isolated real-tmux integration runs omitted toggle→clear and arm against one
|
|
540
|
+
inherited Pane with a sibling unchanged, then proves outside/stale failures
|
|
541
|
+
have non-zero status, stdout zero, bounded actionable stderr, and unchanged
|
|
542
|
+
attention state.
|
|
365
543
|
|
|
366
544
|
## Review Checklist
|
|
367
545
|
- The branch stays within its stated scope.
|