projmux 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -71,7 +71,7 @@
71
71
  - `make test` / `make test-integration` / `make test-e2e`: rename/rebind transport convergence keeps Registry uid/name/root authority and writes only the corresponding exact live field after the Registry transaction: Project name → `@projmux_project_name` without session rename, Window name → `@projmux_window_name` without `displayName`/`window_name`, Pane name → `@projmux_pane_label` without `pane_title`, and Project root → `@projmux_project_path` without filesystem or uid change. Public `rename agent` adds the exact-one Agent cardinality/aliases and changes only Window-scoped `metadata.name` plus its reservation, preserving topic annotations, provider, lifecycle linkage, and managed Pane metadata with zero tmux writes. Unit tables pin field-specific call logs, offline and inventory-unavailable later convergence, duplicate UID refusal, and post-commit live-write failures that exit nonzero with the public reconcile retry while retaining durable Registry drift. Immediate projection exists only for an inherited absolute `$TMUX` socket, routes every lookup/write through exact `-S`, targets stable `$N`/`@N`/`%N` handles, and performs zero tmux calls outside tmux or for a malformed relative inherited socket. Public reconcile treats a known unique Project UID as authoritative over the stale old path after rebind, plans/guards only the path-anchor write, repeats no-op, and keeps unknown/duplicate claims fail-closed. The isolated real-tmux smoke uses inherited-environment removal plus a validated exact socket path, asserts session/window/pane runtime names and a second socket remain unchanged, exercises offline later reconcile and injected option-write failure/retry, and cleans only the contained sockets.
72
72
  - `make test` / `make test-e2e`: canonical resource create lifecycle reentrancy coverage pins the private, session-scoped create lease passed atomically through `new-session -e`, exact-socket/session lifecycle inspection, live-only deferral, stale-lease cleanup, and a second in-transaction reconciliation after explicit mirrors. Standalone `after-new-window`/`after-split-window` hooks remain synchronous. Window/Pane materialization preserves combined tmux output on non-zero exit, accepts only one exact newly observed `@N`/`%N` handle for ownership-checked reverse rollback, emits zero stdout on failure, keeps the registry byte-identical, reports ambiguous or ownership-mismatched residual drift, and never removes an unrelated object. The isolated tmux smoke exercises `--create-window`, exact returned pane IDs, registry/runtime uid agreement, and forced synchronous-hook failure cleanup; the existing Agent-create smoke covers canonical creation in a live managed Window with its stub provider.
73
73
  - `make test` / `make test-e2e`: create-window foreign-session attribution hardens the same lifecycle boundary with an atomic `Created` plus `$N`/`@N`/`%N` new-session result and exact owner verification. Before the first reconcile import, lease, mirror, or Registry write, create now requires the selected live session to carry the registered Project's exact uid and canonical root with no server-wide duplicate uid/root claimant; the check repeats against the reconciler's fresh inventory and immediately before an inner create, so outer-miss/inner-hit, first-use discovery, blank/foreign same-name, and duplicate-other-session races fail closed. New Windows accept only the exact composite `@N`/`%N` pair that is the sole global before/after addition under the stable `$N`; wrong, pre-existing, wrong-session, missing, or ambiguous handles remain unclaimed with exact residual diagnostics and byte-identical Registry/pre-existing options. Claim injection tests pin uid claim → rollback ledger → full mirror ordering for Session/Window/Pane, ownership-checked rollback, and chained name/uid selector refusal. The isolated real-tmux smoke preserves blank and foreign same-name sessions byte for byte on the exact contained socket, then runs eight concurrent same-name `--create-window` racers and requires one Window, nine unique Pane uid mirrors, exact returned `%N` handles, and unchanged pre-existing bindings.
74
- - `make test` / `make test-e2e`: Window resource deletion converges the Registry desired-topology cascade and an exact live tmux binding when one exists. Non-implicit targets (explicit references or `--all`) inventory only `-L projmux`: zero exact `@projmux_window_uid` mirrors, including a typed absent-server result, is a valid Registry-only offline cascade, while one exact mirror is killed under the owning Project's Registry-authoritative session. Duplicate, foreign, stale-session, generic inventory-failure, and preflight-to-execution race states remain fail-closed; selector-omitted active deletion still requires its exact live mirror and refuses an implicit foreign-caller socket. The plan and confirmation include the exact `@N` kill plus implicit Project-session end when present, while an offline plan explicitly reports that no tmux Window will be killed; `--dry-run` renders the complete resource/live-or-offline cascade with zero Registry transactions and zero tmux writes. Execution revalidates both resource and live signatures under the Registry lock, preserves the Project, sibling Windows, unrelated sockets, and Registry bytes on pre-mutation failure, and reports exact removed `@N`/session plus retained Registry UIDs if the store commit fails. Repeat deletion of an already-removed offline UID is a no-match with zero writes. A self-target commits and flushes the Registry result before queueing a shell-quoted exact-socket kill. The isolated real-tmux smoke covers live sibling/last-Window/self-target parity plus an offline Window with shell and Agent descendants, byte-identical offline dry-run, exact Registry-only cascade, repeat no-match, and foreign-socket preservation.
74
+ - `make test` / `make test-e2e`: Window resource deletion converges the Registry desired-topology cascade and an exact live tmux binding when one exists. Non-implicit targets (explicit references or `--all`) inventory only `-L projmux`: zero exact `@projmux_window_uid` mirrors, including a typed absent-server result, is a valid Registry-only offline cascade, while one exact mirror is killed under the owning Project's Registry-authoritative session. Duplicate, foreign, stale-session, generic inventory-failure, and preflight-to-execution race states remain fail-closed; selector-omitted active deletion still requires its exact live mirror and refuses an implicit foreign-caller socket. The plan and confirmation include the exact `@N` kill plus implicit Project-session end when present, while an offline plan explicitly reports that no tmux Window will be killed; `--dry-run` renders the complete resource/live-or-offline cascade with zero Registry transactions and zero tmux writes. Execution revalidates both resource and live signatures under the Registry lock, preserves the Project, sibling Windows, unrelated sockets, and Registry bytes on pre-mutation failure, and reports exact removed `@N`/session plus retained Registry UIDs if the store commit fails. Repeat deletion of an already-removed offline UID is a no-match with zero writes. A self-target commits and flushes the Registry result before queueing a shell-quoted exact-socket kill. The isolated real-tmux smoke covers live sibling/last-Window/self-target parity plus an offline Window with shell and Agent descendants, byte-identical offline dry-run, exact Registry-only cascade, repeat no-match, and foreign-socket preservation. Before every Registry byte-identity baseline, the smoke requires ten unchanged samples with no exact-socket hook shell or controller writer while preserving retry-exhausted startup evidence; a deterministic delayed pre-exec shell proves the observer cannot return during the generated hook's sleep, and the same boundary covers the final kill-server/no-server transition.
75
75
  - `make test` / `make test-e2e`: Pane and Agent live-binding deletion extends the same fail-closed boundary to `delete pane|agent`. One exact `-L projmux` Pane inventory joins each Registry Pane uid through its owning Agent/Window/Project graph to the exact `@projmux_pane_uid`, `@projmux_window_uid`, optional session Project uid, and Registry-authoritative session projection; missing, duplicate, foreign-Window/Project, stale-session, and foreign-caller-socket rows cause zero writes. Agent deletion derives its runtime targets only from its Registry-owned managed Panes—never process content—and removes the Agent, managed Pane resources, and exact `%N` objects while preserving sibling Panes, Windows, and owner graph; deleting the current managed Pane alone preserves the Agent as `Offline` with a cleared `paneRef`. Dry-run, confirmation, and result output list every exact Pane plus implicit last-Pane Window and last-Window session cascades. Execution revalidates both resource and live signatures under the Registry lock, kills external targets before commit, and reports already-killed exact Pane/window/session/Pane-uid tuples with retained Registry UIDs on partial tmux or store failure. A caller-containing plan writes the complete deleted-Pane tombstone set under the Registry lock before commit; a partial tombstone or store failure restores every reachable marker while the Registry identities still exist, and any incomplete rollback keeps those Registry owners authoritative. After commit and flush, exact-socket background kills are queued only from revalidated tombstones, so any unqueued survivor remains explicitly tombstoned and retryable. Both anchored import and repair matching refuse that marker, preventing a failed delayed kill from re-importing a deleted Pane as an orphan. The isolated smoke strips inherited `TMUX`/`TMUX_PANE`, validates both socket paths below its dedicated root, covers sibling, last-Pane, managed-Pane-to-Offline, Agent cascade, anti-reimport, and second-socket preservation, and cleans up only the validated exact sockets.
76
76
  - `make test-e2e`: Implicit active target selector Phase 3 runs installed-shape apply and lifecycle convergence against real tmux servers under a dedicated `TMUX_TMPDIR`, after stripping inherited `TMUX`/`TMUX_PANE`. It verifies the real `#{socket_path}` stays below the smoke root; deleted managed Window/Pane uid options return to their original registry identities after exact-socket apply; a newly created Window and a split Pane carry bindings before the creating tmux command returns and before an implicit read; repeat apply/lifecycle leaves registry bytes and bindings unchanged; a second socket remains untouched; and cleanup kills only the validated exact sockets.
77
77
  - `make test`: Implicit active target selector Phase 2 scopes selector-omitted `get windows|panes|agents` inside tmux to the Project derived from the active Window uid mirror and registry owner chain through the selector engine's single `windowScope` seam. Tables pin inside/outside/missing-binding behavior, explicit `--project` parity, registry-wide `--all-projects`, read-route rejection of bare `--all`, unchanged global `get projects`, and unchanged notification/snapshot/destructive boundaries; the generated CLI reference documents the 0.x feature-minor default narrowing and its same-release escape hatch.
@@ -88,6 +88,7 @@
88
88
  - `make test` / `make test-e2e`: Registry–tmux Phase 6 Agent interaction and launch-readiness coverage pins exact active/explicit Agent topic and semantic-status resolution, Registry-first commit plus exact live projection and public reconcile retry, Offline topic resume mirroring, lifecycle-separated closed interaction kinds with restart/stale/Offline current-`unknown` invalidation, and Agent-owned Pane plus shell-manual-attention Window badge priority without durable glyph/color/aggregate state. Provider hooks coalesce session-ref and semantic observations into one Registry transaction when both occur and never synthesize an Agent for a shell Pane. Initial-prompt create distinguishes `Running` resource creation from bounded provider activation, returns an exact Agent/Pane retry-and-cleanup diagnostic when unconfirmed, and proves prompt non-persistence plus zero pane capture. Provider-neutral `--cwd`/repeatable `--add-dir` validation rejects nonexistent, traversal/symlink escape, unauthorized sibling, duplicate, and provider-unsupported paths before runtime mutation; preserves the Window's Project owner; stores effective Agent/Pane workspace; and forwards only caller-explicit additional roots through Codex/Claude argv. The isolated real-tmux slice uses exact primary/foreign sockets to cover immediate topic/status projection, Offline topic-to-resume mirroring, committed projection failure plus public reconcile retry/no-op, raw title/topic non-identity, foreign-socket containment, and explicit cross-Project workspace with unchanged owner Project. Compatibility `ai topic|status` keeps its stdout/stderr/exit surface while forwarding managed Pane changes through Agent authority, and notify queue acknowledgement/TTL/severity plus shell manual attention remain unchanged.
89
89
  - `make test`: Declarative contract stabilization Phase 16 closes Agent launch outcomes with `TestAgentLaunchOutcomeTableIsClosedAndPrintable`, `TestDelayedProviderActivationReturnsValidatedPaneHandle`, `TestActivationAcknowledgementWinsTheTimeoutRace`, `TestLateActivationRefinesOnlyTheSameBindingGeneration`, `TestRoadmapWorkerLaunchUsesExactOwnerPane`, and `TestWindowOnlyCreateNeverGuessesAroundADeadPrimaryAnchor`. The four printable rows are pre-runtime failure, created+acknowledged, created+unconfirmed, and delayed acknowledgement. Startup readiness and acknowledgement have independent five-second bounds: exact Codex/Claude `SessionStart` evidence keeps activation `pending` and opens the acknowledgement window, while only exact `UserPromptSubmit` evidence acknowledges, so three seconds of startup plus a 2.1-second acknowledgement succeeds with rc 0 and one `%N` line without increasing the acknowledgement deadline. Never-start stops at five seconds and ready-but-never-ack remains bounded by ten seconds overall. Activation transitions are monotonic (`pending -> unconfirmed -> acknowledged`, with direct `pending -> acknowledged`), timeout cannot downgrade acknowledged, and startup or acknowledgement hooks must carry the exact Agent-owned Pane uid and supervisor-issued generation while the Registry still records the same `%N` runtime handle. Exact Project/Window/Pane selectors anchor roadmap-style worker creation on the named current owner Pane, invalid/cross-Window/foreign Pane selectors write nothing, and Window-only creation keeps refusing a dead primary instead of guessing an alternate live Pane. Unconfirmed creation remains nonzero with no stdout while preserving the Agent/Pane and reporting exact retry/delete remediation; prompt/provider free-form text remains absent from Registry.
90
90
  - `make docs`: regenerates `docs/cli.md` from the `internal/cli` command manifest through `go run ./internal/tools/gendocs`. The render goes to stdout and is captured through a temp file, so a failed generator cannot truncate the checked-in page. Run it whenever a route, summary, usage synopsis, output mode, or field projection changes; `make test` fails until the checked-in page matches.
91
+ - `make test`: the release workflow contract requires the root release-please package to keep `draft: true` paired with `force-tag-creation: true`, so the tag exists before the action computes its next release PR; keeps the release-please PAT wired so that tag push can dispatch workflows; keeps `release.yml` listening for pushed `v*` tags; and rejects the retired post-action GitHub ref creation workaround.
91
92
  - `make test`: CLI information architecture v2 Phase 8 generated CLI reference: `docs/cli.md` is rendered from the command tree in `internal/cli/catalog.go` -- the same manifest the binary renders `projmux help` from -- and the hand-maintained prose it used to hold moved to `docs/cli-guide.md`, which the generated page, `README.md`, and `README-ko.md` all link. The drift gate compares the checked-in page byte for byte against a fresh render and names `make docs` in its failure, so the CI `Unit Tests` job catches a stale page; a determinism test renders twice and rejects an embedded repository or temp path, which is what makes regeneration on an unchanged tree a guaranteed no-op. Coverage is a two-way diff rather than a count: every public route and sub-route in the tree has a section and every section in the document is a real node, with a non-vacuity guard that both a canonical and a shortcut route exist to cover. The internal boundary is held on the published page as well as in the primary listing -- no hidden route gets a section, and no `Canonical spelling:` line may point at an executable path absent from the manifest. The generator reads the command tree and never `canonical.go`; canonical-only summaries are therefore checked as an explicit divergent set rather than leaking into command help. Advertisement-honesty checks keep registry reads aligned with the outputs their handlers implement and keep restore limited to its shipped dry-run preview contract.
92
93
  - `make test`: Agent provider session binding Phase 0 persists a validated provider-discriminated `sessionRef` for Claude, Codex, and Antigravity without coupling lifecycle phase or Pane ownership to conversation identity. Metadata/store tests cover structural refusal, deep clones, pre-field byte-compatible reads, exact provider folding, and durable observation timestamps. `internal/app` drives canonical `internal agent-hook ingest` payloads for all providers against an in-memory Registry and proves correct provider-member population, option preservation, zero-write negative cases, conversation retention after Pane release, and stable `describe agent`, `get agents`, and `agent resume` behavior.
93
94
  - `make test`: Implicit active target selector Phase 0 active target for the read and rename verbs: inside tmux, a completely empty selector on `get pane`, `describe project|window|pane|agent`, `rename project|window|pane`, and `rebind project` resolves the active tmux target instead of the whole registry, through one injectable seam in the registry route layer that every one of those routes shares. `internal/app` proves the seam rather than the outcome alone: inside-tmux detection is a table over `$TMUX`/`$TMUX_PANE` that costs **zero** tmux calls whenever the process is not a client — a bare `display-message` from outside a client still answers for the most-recently-used session, so "does tmux answer" is not the test — and inside a client the recorded argv is exactly `display-message -p -t %46 -F #{@projmux_pane_uid}` / `#{@projmux_window_uid}`, read lazily so a Pane or Agent route never queries the window option and a Window or Project route never queries the pane option. Ancestors come from registry `ownerRef`, not from more tmux options: the Project is the owner of the active Window and the Agent is the owner of the active Pane, and the session-scoped `@projmux_project_uid` is never consulted because it is measurably empty on live sessions. The refusal contract is pinned as exact strings for all eight shapes (pane/window with no mirror, pane/window mirroring a uid the registry does not hold, a shell Pane with no owning Agent, a window with no registered owning Project), each asserted to be a usage error carrying neither the `want exactly one` wording nor a candidate listing, and at route level to leave 0 bytes on stdout, 0 transactions, 0 writes, and a byte-identical registry snapshot. Acceptance is measured on call counts as well as output: every explicit form — a positional ref, a `uid:` ref, a `--project`/`--window` scope, a `--selector` label, and an unmatched ref — is never handed an implicit *target*, which is proven by the outcome rather than by the count alone, since the seam resolves a resource none of those invocations address; the recorded consultation count is 0 wherever an explicit `--project` fixes the scope and 1 where the Phase 10 namespace derives it, and outside tmux all seven adopting invocations keep their byte-identical `resolve <kind>: the current selector matched N ..., want exactly one` summary and exit 2. The maintained cardinality adoption record gains a nine-route table that runs the same argv three ways against the same declared cell — mapped target satisfies it, outside tmux still violates it, unmapped target refuses with the fallback's own message — so the fallback cannot be mistaken for a relaxed exact-one. `get pane --current` is proven not to be a duplicate and is not deprecated: `-o cwd` stays byte-identical with exactly one live `#{pane_current_path}` query and zero fallback consultations, and both of its usage errors keep their exact text. `internal/core/selector` records why the fallback has no value-token spelling: `current`, `active`, and four sibling candidates all pass `ValidateName` and parse as bare `metadata.name` occurrences, so a sentinel would shadow a real resource, while `.` and `@` remain rejected as names and are therefore the documented escape hatch for a future explicit spelling. `delete` is unchanged by construction — the seam is nil on every route that does not opt in — and the rendering the provider-session-ref change added to the same file is re-pinned: `resourceSummary` keeps its `kind`/`registry` parameters and the Agent `session=<provider>:<id>` suffix, which no other kind grows.
@@ -372,7 +373,7 @@
372
373
  - `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
374
  - `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
375
  - `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
+ - `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 both workflow topologies to that manifest: `e2e-linux` matrixes exactly the manifest shards and `e2e-suite` the codex/npm pair, both `fail-fast: false` on their own bounded `ubuntu-latest` runner with uniquely named evidence artifacts. In CI they are `needs` and `--required` children of the aggregate `Test` gate, while the thin `E2E Tests` compatibility job keeps reporting the status-check name the branch ruleset requires with `if: always()` and the same `required-gate.py` reduction. In the tag-triggered Release workflow, the same children reduce through an exact `Release E2E Tests` aggregate and `Build Release` depends only on that gate, so every shard must succeed before archive construction without coupling the release workflow to the branch ruleset's required context name.
376
377
 
377
378
  ## When To Update This List
378
379
 
@@ -98,10 +98,12 @@ Release binaries, including the `darwin && cgo` native key adapter, then
98
98
  publishes each staged package with `npm publish --access public`.
99
99
 
100
100
  The GitHub release itself stays a draft until that npm job succeeds. release-please
101
- creates the release with `draft` set, `release.yml` uploads archives to the drafted
102
- release, and only the final `publish-release` job flips it visible. So by the time a
103
- user can see release `vX.Y.Z`, npm `dist-tags.latest` already resolves to `X.Y.Z`;
104
- a failed npm publish keeps the release hidden and the workflow red.
101
+ creates the release with `draft` and `force-tag-creation` set, so its release pass
102
+ creates the tag before the same action computes the next release PR. The tag starts
103
+ `release.yml`, which uploads archives to the drafted release, and only the final
104
+ `publish-release` job flips it visible. So by the time a user can see release
105
+ `vX.Y.Z`, npm `dist-tags.latest` already resolves to `X.Y.Z`; a failed npm publish
106
+ keeps the release hidden and the workflow red.
105
107
  The npm publish job uses GitHub Actions OIDC (`id-token: write`) instead of a
106
108
  long-lived `NPM_TOKEN` secret. PR CI runs `make npm-pack` so package staging and
107
109
  dry-run packing fail before release.
package/docs/testing.md CHANGED
@@ -47,7 +47,10 @@ and humans run the same entrypoints.
47
47
  them required children of the aggregate `Test` gate. It also pins the thin
48
48
  `E2E Tests` job, which exists because the branch ruleset requires a status
49
49
  check under that exact name; a required context that is never reported stays
50
- pending rather than failing, so dropping that job would deadlock merges.
50
+ pending rather than failing, so dropping that job would deadlock merges. The
51
+ tag-triggered Release workflow uses the same four Linux shards and two suite
52
+ selectors on separate runners, then reduces them through the fail-closed
53
+ `Release E2E Tests` aggregate before `Build Release` starts.
51
54
  - `make test-e2e-coverage` validates
52
55
  `test/e2e/ags-oedr-manifest.json`: executable scenario markers and shard
53
56
  assignments must match all 21 rows with orphan count zero. A matrix may move
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projmux",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "tmux project session manager",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/crevissepartners/projmux#readme",
@@ -28,9 +28,9 @@
28
28
  "package:npm:pack": "scripts/package-npm.sh --pack"
29
29
  },
30
30
  "optionalDependencies": {
31
- "@projmux/linux-x64": "0.14.0",
32
- "@projmux/linux-arm64": "0.14.0",
33
- "@projmux/darwin-x64": "0.14.0",
34
- "@projmux/darwin-arm64": "0.14.0"
31
+ "@projmux/linux-x64": "0.14.1",
32
+ "@projmux/linux-arm64": "0.14.1",
33
+ "@projmux/darwin-x64": "0.14.1",
34
+ "@projmux/darwin-arm64": "0.14.1"
35
35
  }
36
36
  }