projmux 0.15.2 → 0.16.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.
@@ -1,2123 +0,0 @@
1
- # Agent Workflow
2
-
3
- ## Parallel Work With `wt`
4
- - Start with `wt --version` and `wt list`.
5
- - Create the task path with `wt path --create <branch>`.
6
- - Work only inside the returned path.
7
- - Keep file ownership clear. If two agents need the same file, one should finish or hand off before the other edits it.
8
- - Use `wt cleanup` or `wt prune` only after previewing what will be removed.
9
-
10
- ## Branch Naming
11
- - `feat/<topic>` for new behavior.
12
- - `fix/<topic>` for bug fixes.
13
- - `docs/<topic>` for documentation-only changes.
14
- - `refactor/<topic>` for structure changes without intended behavior changes.
15
- - `chore/<topic>` for maintenance or tooling.
16
-
17
- ## Standard Development Loop
18
- 1. Sync the branch context and inspect `git status --short`.
19
- 2. Implement the smallest coherent change.
20
- 3. Run `make fmt`.
21
- 4. Run `make fix`.
22
- 5. Run `make test`.
23
- 6. Run `make test-integration`.
24
- 7. Run `make test-e2e`.
25
- 8. Update the maintained test list below if behavior or coverage expectations changed.
26
- 9. Prepare review notes with parity status, commands run, and remaining risks.
27
-
28
- ## Maintained Test List
29
-
30
- - Installed recovery receipt identity: `TestInstalledRecoveryReceiptMatchesPublicControlTurnIdentity` reproduces the public handler's quoted turn receipt versus the lifecycle snapshot's unquoted ID, then requires the fixture to decode the exact receipt and record only its validated `expectedTurn` before waiting. `TestInstalledRecoveryReceiptRejectsAmbiguousOrMalformedIdentity` requires one expected action label, one exact thread and one bounded opaque turn ID, decodes double-quoted fields with `strconv.Unquote`, and refuses missing/duplicate/unknown fields, wrong threads/actions, malformed/truncated/oversized identities and extra output. No stdout payload, completion inference or input retry is added.
31
-
32
- - Managed recovery first-input admission: `TestObserverReadyControlStartPreservesOwnedReadAdmissionAndFreshness` runs the real repo broker host, session, observer and control handler with a deterministic broker clock and owned reader. It reproduces current authority/status-ready followed by the one-second `lifecycle-retry` refusal with zero turn writes, pins the exact boundary, and requires the admitted read to observe new state before any start. Retired control still writes zero. The installed matrix waits for the same exact authority/control tuple to remain ready for that existing interval before the recovered survivor's first input, inside its unchanged 90-second authority deadline; the ledger records `waiting-read-admission`. This is fixture pacing, not a fresh-state receipt or a retry of an attempted input. `TestInstalledRecoveryAdmissionRequiresStableExactControlBeforeFirstInput` resets that wait on identity, broker/connection/binding/control, unknown/not-startable, or clock reversal changes. No product admission, freshness or timeout policy changes.
33
-
34
- - `make test`: `TestCodexNativeObserverForeignThreadSameTurnWritesNoRegistryProgressOrDiagnostics` waits for the observer's startup-ready callback after the initial progress/diagnostic flush, validates the complete baseline, then requires exact equality after a foreign-thread event. A following accepted lifecycle marker proves the foreign event was consumed; synchronization uses no sleep or expanded timeout.
35
-
36
- - `make test`: Stored Codex version-pair qualification diagnostics (C-3) read the qualification store independently of the generation journal. `TestDoctorStoredCodexQualificationWithoutJournal` requires the saved pair and verdict even without a journal or readable Registry. `TestDoctorStoredCodexQualificationTextJSONSupportParity` covers saved, absent, corrupt, filename/receipt pair mismatch, unreadable store, measured refusal, and unbacked acceptance fixtures across text, JSON, and support `doctor.json`; the existing gate supplies qualification readiness. `TestQualificationStoreListPreservesPairOrderAndLoadVerdicts`, `TestQualificationStoreListDistinguishesAbsentDamagedAndPairMismatch`, and `TestDoctorStoredCodexQualificationKeepsValidSiblingsVisible` pin deterministic enumeration, unchanged Load judgments, invalid filename visibility, non-regular file refusal, and valid sibling retention. `TestDoctorStoredCodexQualificationReadOnlyArgvAndState` requires zero execution argv and byte/mode/mtime-identical receipt/journal state across repeated diagnoses; `TestDoctorStoredCodexQualificationSupportRejectsNonDiagnosticStrings` keeps unvalidated strings redacted. Receipt schema v2 and its lack of expiry remain unchanged.
37
-
38
- - `make test`: Replacement guarantee Phase 1 makes `make install` finish the replacement it starts, for the one long-lived role that ships a drain. It adds a second entry condition to the drain the broker runtime already had -- the runtime's own image having been unlinked -- and a bounded termination condition for it. It adds no wire frame, no refusal token, and no way to end a process: the whole of the action is a socket handshake and the runtime decides. `TestBrokerRuntimeDrainsWhenItsOwnImageWasReplaced` holds the vintage trigger in both directions -- a runtime on the installed image is not drained, a superseded one is, live bindings keep delivering and keep control authority through the drain, a new bind is refused by name so the drain stays finite, the answer is read once rather than per session, and an idle superseded runtime closes as soon as it is asked. `TestReplacementRolePoliciesMatchTheContractDocument` holds the per-role disposition table and [replacement-contract.md](replacement-contract.md) equal in both directions and requires every census role to carry a stated decision. `TestReplacementSessionClientIsNeverADrainTarget` fixes the one entry that is a decision rather than an observation: the operator's attached session is report-only, and a fleet of nothing but attached sessions past the cutoff still produces no drain target. `TestReplacementDrainCutoffIsTheAdoptedValueAndBound` pins the adopted 24h cutoff -- truncation rate 9.5%, 13 of 137 residual processes by identity -- fixes the exclusive boundary, and rejects every spelling that would disable the bound. `TestInstallReplacementPassOutcomesAreFixedByFleetAndRequest` pins each fleet-and-request combination to its closed outcome token and its counters, including the refusal that says which door was closed. `TestInstallReplacementNeverFailsAnInstall` holds the property every post-publication step needs. `TestInstallReplacementOutcomeRecordCarriesNoProcessIdentity` and `TestInstallReplacementNoticeSpeaksOnlyWhenAnActionFollows` keep pid, path, and argv off the new record and the new terminal line. `TestDoctorReplacementProcessRowCarriesTheCutoffAndThePassAccount` holds the `L2` row: the bound it judged against, how many processes are past it, and what the last pass did, with an absent pass distinguished from a pass that found nothing. `TestReplacementPathEndsNoProcess` is the scope guard over the acting half -- no `os/exec`, no signal, no `Ensure` -- and `TestReplacementMeasurementPathEndsNoProcess` still holds the measuring half.
39
-
40
- - `make test`: Replacement guarantee Phase 2 makes report-only routes and delete refusals point at runnable commands. `TestReplacementRoutesNameARunnableAction` requires every per-role action step to be a `projmux` command the CLI catalog resolves to a non-refusal, non-`internal` verb, or an event from the closed lifecycle set, and proves the check rejects unknown, refusal-parent, literal-operand, and `internal` commands; `TestReplacementRolePoliciesMatchTheContractDocument` now also holds the `Operator action` column and the event table equal to code in both directions. `TestPaneDeleteLiveMirrorRefusalNamesTheExactUIDRegistryOnlyForm` pins full refusal strings: a name-selected MissingRuntime Pane, Offline Agent, or zero-Pane Offline Agent names the typed resource's exact `projmux delete pane|agent uid:<uid> --socket-path <server>` `--dry-run`/`--yes` form, while exact-uid-without-evidence, evidence-less Pane, Running Agent, and Failed Agent refusals carry no pointer. `TestDeleteByNameRefusalPointsAtARunnableUIDCommand` reaches both refusals through the real route and resolver and runs the named dry-run command with zero Registry writes.
41
-
42
- - `make test`: Replacement completion and restorability Phase 0 adds a read-only `projmux doctor` table only. [replacement-contract.md](replacement-contract.md) fixes the two axes, the closed per-layer reason-token vocabulary, and the closed signal-key inventory; nothing on this path changes install, update, or generation-pool behavior. `TestDoctorReplacementLayerVerdictsAreFixedByInputCombination` pins every reachable evidence combination of all three layers to its replacement verdict, restoration verdict, and governing token; `TestDoctorReplacementUnknownVerdictsNameTheEvidenceGap` requires each `unknown` to name which reading was silent and rejects an unreachable gap. `TestDoctorReplacementEveryReasonTokenCarriesARecoverableDiscriminant` is the negative gate: zero rows carrying a token with no discriminant. `TestDoctorReplacementSignalValuesCarryNoPathProcessOrFreeFormText` holds the serialization rule that lets the discriminant into `--json` -- every value is a counter or a closed token, and a path-shaped value becomes `unclassified`. `TestDoctorReplacementReasonTokensMatchTheContractDocument` and `TestDoctorReplacementSignalKeysMatchTheContractDocument` hold the document and the code constants equal in both directions and reject a published token no evidence reaches. `TestDoctorReplacementDarwinReportsUnsupportedPlatformForImageAndProcessLayers` fixes the `/proc`-less platform branch to `unsupported-platform` with `unknown` on both axes, with no device. `TestDoctorReplacementQualificationMissingMakesGenerationPoolNotRestorable` fixes `qualification-missing` to `not-restorable`. `TestDoctorReplacementCensusSeparatesRegistryRunningFromLiveProviderSession` reproduces the 2026-09-08 sample as a fixture: a Claude Pane's recorded process handle and a Codex Pane's composite authority are different discriminants, a Running Agent with neither is `unobservable` rather than live, and the row stays count-only. `TestDoctorRendersReplacementTableInDefaultRunAndSectionFilter` requires the table on an unfiltered run, under `--section replacement`, and in `--json`; `TestDoctorReplacementSectionCreatesNothing` runs the section against an empty home and requires it to stay empty.
43
-
44
- - `make test`: Replacement guarantee Phase 4 names the long-lived process roles that were behind the `other` bucket and makes residual survival countable per process. It adds classification and reading only: no process is ended, started, signalled, or restarted anywhere on this path. `TestProjmuxProcessRoleNamesEveryChildIncludingTheOnesItCannotName` is the route-to-role table, including the reachable roles added here (`session-client`, `agent-endpoint`, `usage-watcher`), the `--role shell` value that must *not* read as an attached session, and the unnamed remainder. `TestProjmuxProcessRoleOrderKeepsTheUnnamedRemainderLast` holds the census shape and requires every named role to be reachable from some argv. `TestReplacementProcessRolesMatchTheContractDocument` holds the role vocabulary and [replacement-contract.md](replacement-contract.md)'s `Process role vocabulary` table equal in both directions, the same guard the reason tokens and signal keys already carry. `TestDoctorReplacementProcessRowNamesTheResidualRoles` requires the `L2` row to carry `residual.role.*` in census order and to omit a role with no residue rather than print a zero. `TestProjmuxProcessVintageRecordsTheStartInstantIdentityIsKeyedOn` pins the identity key the ledger deduplicates on and requires the count-only projection `projmux doctor` renders to stay byte-identical. `TestInstallResidueSurvivalCountsProcessesNotObservations`, `TestInstallResidueSurvivalKeepsEachProcessAtItsOldestObservedAge`, `TestInstallResidueSurvivalRecordedStartInstantsCompareExactly`, `TestInstallResidueSurvivalReconstructedStartsAbsorbTheirOwnSecondOfSlack`, and `TestInstallResidueSurvivalDoesNotChainAcrossNearSimultaneousStarts` fix `projmux internal install-residue --survival`: both bases, the one-second ambiguity of a start reconstructed from an age, and the bound that keeps that ambiguity from chaining a burst of starts into one process. `TestInstallResidueSurvivalCarriesNoProcessIdentity` is the negative audit for the new report, and `TestInstallResidueSurvivalRouteAnswersOrSaysWhyItCannot` separates the census path, which must never fail an install, from the survival path, which owes a reader its reason. `TestReplacementMeasurementPathEndsNoProcess` is the scope guard: the four files of the measurement path may not acquire process termination, signalling, or restart, because this layer is one edit away from turning a census into an unplanned drain.
45
-
46
- - `make test`: Detached control-plane SIGQUIT artifact Phase 0 installs one process-local sink only on `internal agent-hook ingest codex-broker-watch` and `internal codex-broker serve`. `TestDetachedCrashArtifactPathUsesClosedRoleAndPIDVocabulary`, `TestResolveDetachedCrashStateDirFollowsStandardStateHomeOnly`, `TestDetachedCrashArtifactIsPrivateBoundedAndAtomicallyPublished`, and `TestDetachedCrashArtifactRefusesSymlinkAndNonRegularTargets` pin the standard state path, closed role-plus-PID filenames, `0700`/`0600` modes, 1 MiB bound, explicit truncation marker, atomic no-clobber publication, and hostile path/target refusal. `TestDetachedRoutesPersistActualSIGQUITWithStderrDiscarded` builds the production binary and sends actual `SIGQUIT` to disposable watcher and broker route PIDs with fd2 set to `/dev/null`, requiring an all-goroutine artifact and exit code 2 while normal startup creates no crash state. `TestDetachedCrashSinkNormalLifetimeCreatesNothing` and `TestDetachedCrashSinkPreservesQuietAndPrivateSurfaceBoundaries` keep state resolution lazy, append logs absent, hook redirection byte-identical, `supervise` stderr attached to its PTY, the sink out of public routes/config/env/schema, and crash artifacts out of support reports. Installed verification uses only an isolated `XDG_STATE_HOME` and exact broker child PID; live watcher, broker, supervise, provider, and generation-host processes are never signal targets.
47
-
48
- - `make test` / `make smoke-assert-contract`: `test/smoke-assert-contract.sh` checks only `smoke_assert_file_contains` against a real file in fresh shell subprocesses. Present and absent `--` needles and ordinary needles must return the exact expected exit status, empty stdout, and byte-exact stderr (empty on success, only the existing assertion diagnostic on failure). The ordinary absent case also pins fixed-string semantics. Grep option/usage errors and an unconditional-success helper fail this gate; the CI `Unit Tests` job runs it through `make test`.
49
-
50
- - `make test` / `make test-integration` / `make test-e2e`: Columnar profiles Phase 1 changes only the CLI default projection. Registry plural reads use `KIND NAME STATUS ACTIONS`; runtime reads use identity/containment/classification, and the seven list routes accept explicit `-o wide`. [Column profiles](column-profiles.md) records the exact matrix and diagnostic recovery. `TestColumnCatalogCompletenessBijectionAndExactProfiles` compares typed field declarations to surface capabilities, rejects orphan/duplicate IDs and fixes all nine compact/wide orders and aliases; `TestColumnConsumersHaveNoIndependentHeaderLiteralAuthority` audits all four consumers. `TestColumnProfilesExactOutputAndSnapshotParity` pins whole default/wide output, structured/scalar byte digests and both explicit picker-wide entry frames in `column-profiles.golden`; its production observation fixture includes live, offline, missing-root and ControlSession-owned resources and proves one Registry read, exactly one four-query tmux observation, zero writes and zero sibling-server calls for every mode. ACTIONS is compared directly with the existing navigation projector and fixed eligibility examples. `TestColumnActionsReuseUnavailableSnapshotAndEmptyListsStayEmpty` pins unavailable-server actions and empty list bytes. `TestColumnProfilesDoNotSelectFromWidthAndWideIsUnbounded` checks 80/120 widths and long Hangul context/session values; `TestWideOutputIsScopedToColumnarListReads` rejects wide on every other canonical route. Existing age, session reference, context, termination, selector, JSON and picker layout tests retain their assertions through the explicit wide recovery surface where necessary. The integration and E2E exit-reconciliation probes explicitly use `get panes|agents -o wide` and retain their TERMINATION/evidence and Registry-write-free assertions. Phase 2 below owns the subsequent picker default/control and bound retirement; CLI, row identity/order/search and provider semantics retain these contracts. Isolated piped CLI smoke compares default/wide/JSON/scalar bytes and read/write counters at 80/120 columns before and after installation.
51
-
52
- - `make test` / installed opt-in smoke: Codex generation-pool Phase 2 keeps a state domain bounded to two dark private endpoint generations and keys each Broker by the canonical durable `stateDomainID + endpointGenerationID`. `TestGenerationEndpointKeyRoundTripTable`, `TestBrokerEndpointIdentityValidityMatchesDurableMetadataRef`, and `TestGenerationPoolStateTableKeepsPreparingDarkAndBounded` pin key/schema vocabulary parity, third-generation zero effect, and ready-Preparing create admission at zero. `TestGenerationPoolReconnectIsGenerationLocalAndCrossRouteWritesZero`, `TestGenerationPoolBrokerRestartRestoresExactBindingsAndFencesOldRuntime`, `TestGenerationPoolBrokerRestartPreservesSparseBindingEpochLedger`, `TestGenerationPoolFailedRestartRetryKeepsLedgerExactAndFencesOldRuntime`, `TestGenerationPoolRestartFenceRejectsBindingThatMissedTheSnapshot`, and `TestGenerationPoolRestartFenceRefusesControlAndWriteUntilExactSwap` use independent fake servers and semantic barriers to pin sibling reconnect isolation, same-thread cross-route typed refusal/provider write zero, exact/idempotent including sparse binding-ledger restore, fresh broker-runtime fencing, failed-restart retry, and the bind/control/write-vs-restart races. `TestPrivateGenerationHostLaunchProofTableAndLeaseRetention`, `TestPrivateGenerationHostLivePIDSocketExecutableDriftKeepsLifecycleAtZero`, `TestEveryQualifiedBundleArtifactIsMandatoryBeforePublish`, and `TestLaunchBoundaryBundleDriftRefusesPublicationAndCleansExactChild` pin the closed server/TUI/helper bundle, immutable leased argv, Phase-2-closed lease release, distinct endpoint-host versus broker runtime authority, pre-publication revalidation, and PID/process-group/socket-change-time/executable proof with lifecycle mutation zero for every drift, including immediate socket inode reuse. `TestPrivateGenerationHostCloseWaitsForOwnedSessionDescendants`, `TestPrivateGenerationHostCloseProcessSessionBarrierStateTable`, and `TestOwnedGenerationProcessCleanupSignalsExactSessionAndWaitsForDescendantExit` pin the exact `Setsid` group signal and the leader-exit plus inherited-session-EOF semantic barriers without a fixed sleep; the private-root tests retain outside-root, symlink, and mode mutation zero. `TestInstalledPrivateGenerationHostDualListenerSmoke` is opt-in and runs exact leased 0.152.0/0.152.1 private listeners with inherited `TMUX`/`TMUX_PANE` absent, two initialized runtimes, distinct private process groups, ambient/default socket/PID identity unchanged, and descendant-complete exact owned cleanup. Deletion ledger: zero canonical test symbols deleted or renamed; single-default broker, Phase 0 qualification/bundle, and Phase 1 composite-fence/projection tests retain their unique boundaries. Scope audit: current pointer/drain, Agent create/resume, foreign adoption, Settings/background UX, consumer projection, and unrelated cleanup remain unchanged.
53
- - `make test` / `make test-integration` / installed managed recovery: `TestRecoveredBrokerRetiredBarrierCannotRepublishAuthority`, `TestDefaultBrokerEndpointRequiresProbeWireAndRouteAgreement`, `TestRecoveredDefaultBrokerRebindsExactAuthorityAndControlConsumers`, and `TestRecoveredGenerationAuthorityRejectsStaleEndpointAndActivation` cover default socket probe/initialize/version agreement, current barrier publication, exact surviving Agent/Pane/activation/thread endpoint CAS, current control consumers, old-runtime epoch-1 refusal, old writes zero, and stale/unknown/Draining refusal. `TestRecoveredBrokerConcurrentOpenSharesOneCurrentView`, `TestRecoveredBrokerOpenWaiterHonorsCancellation`, `TestRecoveredBrokerCloseCancelsPendingRouteAndEnsure`, and `TestEnsureCancellationDuringStartupLockNeverLaunches` verify shared publication, cancellable queued Open, bounded Close during route/Ensure, and no launch after startup-lock cancellation. `TestRecoveredDefaultBrokerRefusesNewRollingJournalBeforeSameEndpointReopen` verifies typed read-only admission refusal before probing or reopening the same endpoint. Existing broker disconnect, runtime replacement, owned lifecycle cancellation, and Phase 0 usage timing tests remain required. `TestInstalledManagedCodexAuthorityRecoveryMatrix` takes only an explicit `PROJMUX_CODEX_RECOVERY_INPUT` JSON file: private root, exact installed binary/SHA/source head/tree, two read-only release mounts, protected private auth input, explicit model and ten fresh validation inputs, host namespace IDs, and evidence path. It uses real installed projmux, real tmux, official managed daemon/proxy, and the repo Go client for baseline 0.151.0, upgrade 0.154.0, and three same-version restarts; one survivor and a new control complete actual turns in every row. Its content-free ledger retains failures and submission counts; it never replays work. Before each create/turn input, the same verified selection guard used by the zero-input diagnostic records exact fixture shim/release evidence and refuses a fallback CLI before incrementing submissions. `TestInstalledRecoverySelectionRefusalKeepsSubmissionsAndProviderCallsZero` reproduces execution-denied shim PATH fallback and pins create/turn refusal with zero inputs; `TestInstalledRecoverySubmissionRecordsSelectionBeforeExactlyOneInput` pins evidence-before-input ordering and rechecks every later sample. Run `go run ./internal/testutil/codexinstalled/cmd/managed-preflight </absolute/input.json>` first to verify the same general fixture helper with official start/version/stop, PID birth/artifact/socket/executable checks, and no real inputs or auth. `TestManagedRecoveryColdStartRequiresExactAbsentState`, `TestManagedRecoveryVersionMissingClassificationIsExact`, and `TestManagedRecoveryCleanupNeverFallsBackAfterUnknownStart` cover exact initial ENOENT plus owned absent state, unknown-result refusal, and zero fallback stop/signal/removal after unresolved ownership. Both runners refuse shared namespaces, ambient managers, and missing init reapers. Each turn waits for exact control publication and a successful read-only status response as well as registry authority. The matrix's unique tmux server has a fixture-private default-name socket alias for outside-tmux public commands; cleanup verifies the exact socket and waits for normal owned process exit. Linux managed conformance is opt-in and separate from Darwin compile/CI and generation qualification.
54
- - Managed recovery failure evidence: `TestInstalledRecoveryReadinessReachesProductionControlThroughCanonicalAlias` reproduces the readiness fixture's unsupported capability spelling before any tmux/control call and requires readiness to resolve `agent turn start`, then issue status only through the production exact control consumer. It tests baseline and later current authority through the outside-tmux default alias, canonical logical socket and exact physical socket/PID proof; the public turn-start handler consumes the same route/epoch. Empty optional Codex `SessionID` remains empty while the nonempty ThreadID stays exact. `TestInstalledRecoveryReadinessAndTurnRefuseCanonicalAliasDriftBeforeControl` requires both readiness and turn start to make zero control calls on route drift. `TestRecoveryErrorDistinguishesNoBrokerRefusalAndRedactsUnknownContent` requires `RefusalNone`/generic errors to stay unclassified without a false broker label, while known typed codes remain useful and unknown content is redacted. `TestRecoveryFailureEvidenceRetainsPartialIdentityWithoutContent`, `TestRecoveryReadinessDistinguishesRefusalsAndOnlyRequestsStatus`, and `TestRecoveryObserverEvidenceFiltersIdentityVocabularyAndContent` preserve each attempt/created UID before completion, bounded partial Registry and exact tmux/control observations, and terminal process/observer evidence without provider content. Unknown error/message/approval fields are omitted or classified; diagnostics never submit a turn. `TestCleanupEvidenceRetainsErrorsWithoutRetryOrFalseSuccess`, `TestCleanupEvidenceReadsOnlyOneStatForValidatedProcIdentity`, `TestCleanupStatProjectionRejectsMalformedOrDifferentPIDWithoutContent`, and `TestOwnedProcessObservationErrorRetainsCauseWithoutContent` cover both harnesses' cleanup evidence: closed stage/operation, numeric errno, validated proc PID/leaf and at most one bounded later stat birth/state snapshot. Failure diagnostics retain the original error; their optional later snapshot grants no cleanup authority or false success and never rereads environment/argv or retains raw errors/process contents. `TestOwnedProcessesPermissionDeniedZombieRemainsNonemptyResidual` reproduces environment EACCES followed by an exact zombie stat. Only after a verified official lifecycle and rechecking the same private PID/mount/network namespaces with an init reaper may the general fixture retain two matching bounded zombie stat observations as an explicit `unreaped-residual`, distinct from an `owned-executable` and never signal authority. `TestOwnedProcessesPermissionFailuresRemainErrorsUnlessExactPrivateZombie`, `TestResidualAdmissionRequiresRecordedPrivateLifecycleProof`, `TestOwnedProcessesResidualRequiresNormalReapingBeforeCleanup`, and `TestOwnedProcessesFailedCensusCannotClearEarlierResidual` require live/unknown permission failures, malformed/disappeared/reused stat, and changed namespace proof to remain errors; known residuals block cleanup until a successful later census proves normal reaping. Existing drain deadlines remain unchanged. `TestCleanupResidualHistoryRetainsUnreapedEvidenceAfterNormalDrain` pins both installed ledgers retaining bounded PID/birth/state/parent residual history even after the inventory becomes empty; evidence overflow refuses rather than dropping observations.
55
- - Installed command failure evidence: `TestInstalledCommandFailureCapturesOnceWithoutOutputOrFalseOutcome` verifies one fixture command invocation, numeric exit capture, inherited tmux environment removal, unchanged successful output, and zero raw stderr/stdout/argv in failure ledgers. Both installed runners save bounded `commandFailure`; the matrix also attaches it to the current attempt without changing submissions or completion counts. `TestInstalledCommandFailureProjectionRequiresBoundedKnownCLIShape` accepts only closed turn-start response/message pairs or established CLI wrapper prefixes, validates and discards resource-selector recovery suffixes, and refuses multiline/control-byte/unknown/oversized content. Stderr capture drains at a fixed 4096-byte memory bound. `TestInstalledCommandFailureRetainsUnknownOutcomeAfterReadinessAndControl` exercises the public Cobra bridge and exact control handler: status readiness can precede a failed fresh state read, a failed wire call, or an error recording an accepted turn. These are boundary fixtures, not proof of any actual failed invocation's cause. Every failure retains `outcome=unknown`; there is no retry, partial-output success, or provider mutation inference.
56
- - Zero-input managed connection diagnostic: `TestInstalledManagedCodexConnectionDiagnostic` accepts only an absolute `PROJMUX_CODEX_CONNECTION_INPUT` JSON file with private root, exact binary/SHA/source head/tree, two read-only releases, host namespace IDs and external evidence path. It proves the same real tmux/Registry route, then official manager ownership and production default Current/shared/owned initialization and Close for 0.151.0 and 0.154.0. There are zero Agents, threads and provider inputs; no Bind, thread read/start/resume or turn call. `TestConnectionDiagnosticInitializesAndClosesWithoutProviderOperations` and `TestConnectionDiagnosticRefusesModelAuthAndProviderInputFields` enforce those limits. `TestManagedSelectionShimAndProbeFollowCurrentLinkWithoutVersionCache` isolates the fixture environment producer and production PATH manager-probe consumer across a current-link change using explicitly synthetic manager responses: CLI, managed and manager-evidence versions follow the link, attached versions remain empty, and a running-manager/absent-wire contradiction refuses attach and lifecycle mutation with unknown ownership. `TestConnectionSelectionEvidenceDistinguishesShimReleaseAndDrift`, `TestConnectionFailureProbeCannotReplaceCurrentRefusalWithLaterHealth`, and `TestConnectionHealthEvidenceRejectsUnboundedProviderStrings` retain exact fixture shim/selected-release paths and hashes, state domain, and closed health/CLI/managed/running/wire versions. A failed connection gets one bounded later read-only probe, explicitly separate from the failed Current observation; later readiness never replaces that failure or retries Current. Unexpected selection refuses before another CLI invocation. This diagnostic does not replace the actual survivor matrix. Run with a private TMPDIR outside CODEX_HOME (for example `/artifact/tmp`) to avoid official CLI PATH-helper warnings. `TestExplicitInputIsBoundedAndCannotTraverseOrFollowSymlinks` covers the shared regular JSON input reader: absolute clean path, parent directory capability, no final symlink, maximum 64 KiB.
57
-
58
- - `make test` / installed opt-in smoke: Codex generation-pool Phase 3 routes prompted and payload-free native create through one exact current `stateDomainID + endpointGenerationID`, then keeps resume, message/reply/approval, lifecycle observation, and TUI argv pinned to the durable Agent ref even after admission-current changes. `TestCodexNativeCurrentChangePinsExistingAgentAndAdmitsNewCreateOnlyToNew`, `TestCodexNativeResumeRouteUsesDurableEndpointAndRefusesLegacyOrDraining`, `TestOfflineCodexResumeRechecksDrainingInsideTransactionBeforeProviderWrite`, and the Agent-control action table pin old/new cross-wire zero, locked-transaction lifecycle revalidation, typed missing/Draining refusal, and provider/Registry first-write zero. `TestGenerationRoutedBrokerEpochPublishesItsExactRuntimeAuthority`, `TestCodexNativeEndpointRouteTransportShapeIsClosed`, `TestDefaultCodexStateDomainIdentityUsesExactCanonicalCodexHome`, and the broker transport tests require real generation-keyed broker runtime authority, default/private transport XOR, and canonical content-free state-domain identity; a changed `CODEX_HOME` never reattaches a stored Agent. `TestNativeResumePickerRowsPreserveExactGenerationThreadAndSource`, `TestNativeResumePickerDuplicateVisibilityUsesKnownOwnerOrRefusesAmbiguous`, and `TestCodexResumePickerPinnedValueShapeIsClosed` freeze source/domain/generation/state in each native row value, visibly mark handover/unavailable rows, refuse duplicate visibility without a durable owner, and keep rollout rows unable to carry partial native authority. `TestInstalledIsolatedGenerationPinnedEmptyPromptCreateSmoke` is opt-in through `PROJMUX_CODEX_PHASE3_SMOKE_ROOT=/tmp/<isolated-root>` and runs the installed binary against a fixture-owned unmanaged direct endpoint plus unique Registry/tmux roots; it first bootstraps the fresh generated app config through the installed public `config apply` path with no live isolated server, then accepts only the exact tuple's closed Phase-7 result: durable-ready keeps the historical payload-free Agent↔Pane↔thread↔endpoint, zero-turn, pinned-TUI, broker, obligation, ambient-mutation-zero, and cleanup assertions, while typed not-ready preserves one exact Failed Agent/thread/endpoint with Pane/TUI/plain fallback zero and an unresolved exact-UID retry with second Agent/thread/lane zero. Installed Codex does not subscribe the broker until the first real input materializes the rollout, so the successful pre-turn row keeps native authority unavailable instead of adding dummy content; the provider-recorder integration owns first-real-input same-thread/one-turn proof without requiring ambient auth. Each observation records its exact CLI/app-server/platform/route tuple and is not a version-family guarantee. Phase 5 old-endpoint stop, successor same-thread resume, endpoint-ref CAS, Pane relaunch, retirement, lease release, and foreign adoption remain out of scope.
59
- - `make test` / `make test-integration` / private installed observation: Codex generation-pool Phase 4 owns explicit crash-resumable `plan|apply|resume|abort`, at-most-once admission-current, and old-generation drain. `TestRollingUpgradeStateMachineKeepsPhase5EffectsAtZeroAndReceiptsAtMostOnce`, `TestRollingUpgradeStateSpaceProperty`, and `FuzzRollingUpgradeValidStatesNeverContainPhase5Effects` pin monotonic receipts and all seven Phase-5 effects at zero, including `leaseRelease`. `TestCoordinatorExactPhase0TuplePreflightRefusesBeforeEveryMutation` re-opens both complete leases read-only and pins exact bundle/version/RoleTUI plus same-domain, direct-socket, and non-overlapping private/state/lease-root topology before any physical start. `TestCoordinatorResumeAfterLaunchBeforeProofReusesOneCandidateAndLeavesNoDuplicate`, `TestCoordinatorAbortFencePreventsStalePlannedResumeFromLaunching`, `TestCoordinatorAbortAndAdmissionRaceNeverStopsCurrent`, `TestCoordinatorEveryFailpointCrashRestartConvergesAtMostOnce`, and `TestRollingJournalAtomicWriteCrashHooksRestartWithoutDuplicateEffects` cover coordinator death after supervisor launch, all ten declared coordinator failpoints, both journal atomic-write crash hooks, exactly-once candidate-start/admission/drain/handover convergence after restart, the durable abort fence, and deterministic Abort-vs-admission schedules without fixed sleeps. `TestCoordinatorCandidateReadyAbortCleansExactCandidateAndKeepsSevenPhase5EffectsZero`, `TestCoordinatorLaunchBeforeProofAbortCleansRecoveredCandidateWithoutPhase5Effects`, `TestRemoveCandidateSocketRefusesUnknownOrReboundIdentityWithoutUnlink`, `TestCleanupDurableCandidateAfterSupervisorExitRemovesOnlyOwnedSocketAndReceipts`, and `TestCleanupDurableCandidateRecoversOnlyExactUnlockedOrphanGuard` pin exact candidate cleanup, retained bundle lease, unlocked no-socket orphan-guard recovery, and locked/foreign/rebound mutation zero. `TestExactRoleTUIArtifactRefusesWrongAbsolutePathAndPostVerifyDrift`, `TestCoordinatorWrongAbsoluteTUIPathNeverCommitsAdmission`, and `TestCoordinatorTUIDriftAfterReadyProofFailsInsideAdmissionBarrier` require the exact single RoleTUI lease artifact at publication and commit. `TestCoordinatorTwoSlotThirdUpgradeRefusesWithEveryMutationSurfaceAtZero`, `TestCoordinatorRetainedRetiredRoutesDoNotConsumeThePreparingSlot`, and `TestCoordinatorAbortedOperationRefCannotBeReplayed` close capacity/replay consistency. `TestAgentResumeDrainingGenerationFailsClosedWhenHandoverRequesterIsUnconfigured` tables both Draining and HandoverPending with an unconfigured requester, while `TestAgentResumeDrainingGenerationReusesOneGenerationWideOperationRef`, `TestCoordinatorRepeatedDrainingResumeReusesOneGenerationHandoverOperation`, and `TestDrainingOldCodexAgentKeepsExactTurnControlTUIAndPaneBinding` cover exact operation-ref reuse and direct old-generation turn/control without endpoint/TUI/Pane mutation. `TestPrivateRollingAdmissionReceiptRendersAllSevenPhase5EffectsAtZero` and opt-in `TestInstalledPrivateRollingAdmissionReceipt` cover content-free receipts, private process groups, ambient/default read-only observation, exact old Draining thread read plus successful no-turn-compatible listing, payload-free new Current create/read, assertion-safe exact target teardown, and ambient-preserving cleanup. Deletion ledger: zero canonical tests deleted or renamed and zero Phase 0–3 boundary suites merged; the canonical graph baseline only adds the four explicit upgrade leaves. Phase 5 effects remain zero in the unchanged Phase-4 receipt and are implemented only by the distinct linked handover journal.
60
- - `make test` / `make test-integration` / opt-in installed private observation: Codex generation-pool Phase 5 owns `agent app-server handover plan|apply|resume|abort` and the generation-wide `stop → resume → snapshot-all → endpoint CAS → same-Pane relaunch → terminal retirement → lease release` journal. `TestGenerationHandoverOrdersStopResumeSnapshotCASRelaunchTerminalLease`, `TestGenerationHandoverCannotCASUntilEverySnapshot`, and `FuzzGenerationHandoverNeverAcceptsUnsafeReceiptOrder` pin monotonic per-effect intents, the global snapshot barrier, and terminal lease ordering. `TestCoordinatorGenerationWideOrderingAndEveryFailpointResumeConvergesWithoutDuplicateEffects`, `TestCoordinatorRecoversExactSameGenerationBeforeHandoverAndCrashRetryIsIdempotent`, and `TestCoordinatorUsesQualifiedFallbackWhenExactSameGenerationRestartIsUnavailable` cover every coordinator effect gap, journal-authoritative repeated Apply, same-generation-first cold recovery, and exact-qualified fallback without a fixed sleep. `TestExternalOwnersRemainAwaitingOwnerStopWithLifecycleArgvZero`, `TestIncompleteManagedOldRouteBlocksBeforeEveryEffect`, `TestSuccessorPreloadedTargetStopsBeforeOldStop`, `TestPlanIgnoresSameGenerationIDFromAnotherStateDomain`, `TestAppliedNoTurnChoiceIsForwardOnlyAndCannotClaimAuthorityRestored`, and the abort-fence tests pin the ownership matrix, exact state-domain tuple, reversible-only abort, explicit no-turn choice, pre-stop loaded-thread refusal, and foreign lifecycle argv zero. Production-effect tests revalidate the pinned Agent/Pane/runtime/generation/thread tuple before successor absence, old stop, resume, snapshot, and same-Pane relaunch; `TestHandoverResumeReceiptSurvivesSuccessorRestartWithoutSecondResumeWire` pins the content-free operation-qualified at-most-once resume receipt. `TestInstalledPrivateJournaledGenerationHandover` is opt-in through `PROJMUX_CODEX_PHASE5_SMOKE_ROOT=/tmp/<isolated-root>` and uses exact private 0.152.0→0.152.1 binaries plus isolated XDG/tmux roots for the public handover and no-turn negative. Enforcement ledger: prompt/turn/approval resend, provider-content replay/inspection, duplicate same-thread ownership, automatic no-turn migration, foreign kill/adopt, live-old same-thread resume, Phase-6 consumers/Doctor, and unrelated cleanup remain zero. Deletion ledger: zero canonical tests deleted or renamed; Phase 0 qualification, Phase 1 lifecycle fencing, Phase 2 host/pool, Phase 3 generation-pinned routing (including the empty-prompt migration receipt), and the Phase-4 seven-effect-zero suite retain their unique symbols and boundaries; the command graph adds only four handover leaves.
61
- - `make test` / `make test-integration` / opt-in installed private observation: Codex generation-pool Phase 6 projects notification, sidebar, statusbar, and native reply from `codexgeneration.ProjectConsumers`, whose one endpoint plus five-axis authority digest is copied into content-free notification metadata. `TestDerivedConsumersUseOneEndpointFenceAndStaleActionsAreZero`, `TestGenerationConsumersShareExactEndpointFenceAndStaleActionZero`, `TestGenerationObserverRefreshesDurableDrainingOperationForConsumerProjection`, `TestGenerationNotificationProducerCarriesCanonicalFence`, `TestAgentControlCLIStaleCanonicalConsumerFenceCallsNoControlTransport`, and `TestGenerationAwareListerRegistryReadErrorMakesExistingNoticeStaleAndActionZero` pin exact Agent/Pane/runtime/thread ownership, durable planned-operation refresh, Current/Draining parity, quiet/state-only policy, pre-control fence revalidation, read-error fail-closed behavior, and stale endpoint/fence action zero; `TestNotifyListSidebarEnterOnGroupStaleRoutableHasZeroAction` and `TestStatusbarClickNotifyStaleHeadHasZeroAction` retain only their unique picker/statusbar transport assertions. `TestDoctorCodexGenerationActionabilityTable`, `TestDoctorCodexGenerationPinnedTupleSkewFailsClosed`, `TestDoctorCodexGenerationMissingBundleIsActionableBlocked`, `TestDoctorCodexGenerationBundleDriftIsBlockedAndReadOnly`, `TestDoctorCodexGenerationJSONAndTextAreContentFree`, `TestDoctorCodexGenerationHandoverTimelineUsesReceiptOrder`, `TestDoctorCommandProjectsGenerationPoolInJSONAndText`, `TestDoctorCodexGenerationOrphanObligationClassificationTable`, and `TestDoctorOrphanObligationsCannotBlockOrUnblockThePoolVerdict` cover expected Current+Draining health, invalid admission and pinned tuples, qualified 0.152.0→0.152.1 versions, owner/bundle/state-domain inventory, no-turn close-or-replace, Draining handover-required, ordered receipt actionability, Doctor mutation zero, and byte-exact content-free JSON/text goldens. A journal obligation whose Agent no longer exists in the Registry is reported under its own `orphaned` / `await-obligation-reprojection` / `agent-record-absent` triple and a separate per-generation `orphaned_agents` count: it stays listed, leaves `pinned_agents` and every live obligation's classification untouched, and can neither block nor unblock the pool verdict. AgentObligation carries no ThreadID, so only a fresh obligation reprojection can retire such a row; Doctor never writes one. The existing opt-in `TestInstalledPrivateJournaledGenerationHandover` now owns the complete release qualification without changing the Phase-5 journal: its pre-handover barrier pins one existing N Agent and one new N+1 Agent to distinct exact Pane/thread/endpoint tuples, materializes the new thread's provider-required rollout once before assignment, waits for both generation observers' semantic ready handshakes, starts both public `agent turn start uid:<agent>` message paths before awaiting either command or observer-owned terminal projection, requires exact Agent/Pane/runtime/generation/thread/turn/authority pairing plus post-baseline provider-control completion with cross-wire zero, and proves the Phase-5 journal bytes unchanged before continuing the unchanged no-turn block, explicit close, old-stop→resume→snapshot→CAS→same-Pane relaunch→retire→lease-release receipt and exact cleanup. No secondary provider connection observes either active turn. It also negatively refuses selectorless Agent resume with Registry/tmux/old-endpoint mutation zero. The post-close remediation adds `TestManagedCurrentActivationAfterDefaultUpgradePreservesOldAgentAndForeignLifecycle`, `TestManagedCurrentActivationReopenAfterAdmissionConvergesDrainBeforeCreate`, `TestOrdinaryCodexCreateSpellingsActivateRollingManagedCurrent`, `TestProductionManagedActivationBuildsExactDefaultUpgradeRequest`, `TestManagedCodexRuntimeLocationPortableBoundRefusesBeforeMutation`, `TestManagedCodexActivationHostRefusalsNameExactCorrectiveAction`, `TestProductionManagedActivationUnsafeStateDomainNamesExactActionWithZeroMutation`, `TestLeaseStandaloneReleaseRetainsClosedRequiredBundle`, `TestDoctorManagedActivationShowsCurrentDrainingPinnedTruthReadOnly`, and opt-in `TestInstalledDefaultUpgradeOrdinaryCreatesActivateManagedGeneration`: an installed-CLI/default version skew now requires the exact canonical shared Codex state domain to satisfy the existing owner-private host gate, leases the complete new standalone release, preflights a deterministic collision-resistant private socket against the portable Unix path bound, resumes the exact durable activation through post-admission crashes, and admits both ordinary create spellings only after the managed Current plus external Draining projection converges. The installed witness takes `PROJMUX_CODEX_PHASE6_REMEDIATION_SMOKE_ROOT=/tmp/<isolated-root>` and `PROJMUX_CODEX_PHASE6_REMEDIATION_OLD=/absolute/old/codex`, seeds an old live Agent/Pane/thread before activation, and preserves every Phase-6 managed-Current, old-unmanaged-Draining continuity, foreign-lifecycle-zero, Doctor-read-only, and cleanup assertion unconditionally; its two ordinary payload-free spellings then accept only one uniform exact-tuple Phase-7 result, either the prior durable-ready Pane/broker path or two exact Failed identities pinned to Current with new Pane/broker/false-Running/plain-fallback zero. The old/default endpoint receives zero stop/restart/kill/adopt calls, its live thread/Pane/approval state stays pinned, unsafe shared-state or runtime-path gates return an exact operator action before bundle/journal/runtime mutation, and Doctor reports stored Current/Draining versions, pinned obligations, missing-qualification action, and mutation zero without fabricating a YES receipt. Deletion/mutant ledger: no Phase 3 empty-prompt or Phase 0–5 canonical symbol was deleted or merged; two stale-action tests were renamed in place from permissive focus expectations to transport-owned zero-action expectations, lifecycle/actionability equivalence moved to the ninth pure `internal/core/codexgeneration` model file, and `TestPhase0ModelImportsNoMutationAdapter` is the mutant-killing ownership receipt for that new canonical owner. Phase 0 supersedes only this witness's payload-free functional assertions: the fixture is retained as skipped historical negative evidence, and prompted generation activation remains covered by the unit route.
62
- - `make test`: A Codex generation with nothing left to hand over can retire without a version-pair receipt. `ActivateManagedCurrent` starts a rolling activation automatically on a version skew and carries no qualification guard, so an ordinary user reaches `draining` without one; deleting the Agents pinned to that generation then removes the only production caller of the rolling handover request (`agent resume` on a live pinned Agent), leaving both `version-pair-not-qualified` and `exact-rolling-handover-not-requested` permanently unclearable. `codexgeneration.EvaluateRetirementVacancy` defines the one case where the receipt has no subject, and `TestEvaluateRetirementVacancyOpensOnlyForACompleteZeroCensus` plus `TestRetirementVacancyReportsOneCauseInAStableOrder` pin its total, order-stable verdict, including that a census which never ran is never read as an empty one. `TestVacantGenerationRetiresWithoutAVersionPairReceipt` is the acceptance fixture for both unmanaged and Projmux-private ownership: the plan reports `request-handover` with a vacant census and zero targets, Apply fires the exact rolling request through the declared requester seam, the old route reaches `retired`, its obligation rows disappear, the pool falls back to one live route, the post-retirement journal still validates, and the shared state domain's thread files are byte-for-byte the same set before and after. `TestOccupiedGenerationStaysFailClosed` tables the fail-closed axes -- one live obligation, a fresh projection that disagrees with an empty journal ledger, an endpoint-bound Agent carrying no ThreadID, a Pane activation outliving its Agent, and an unreadable shared state domain -- and requires both original blockers, an unmutated journal, a still-`draining` route, and effect zero. `TestVacantRetirementLeavesTheDrainingCapAndSlotArithmeticAlone` keeps `multiple-draining` and the two-slot arithmetic unchanged, `TestQualifiedGenerationDoesNotNeedAVacancyCensus` keeps the ordinary qualified path free of the state-domain read, `TestVacantRetirementRefusesWithoutARequesterSeam` refuses rather than improvising the request, `TestVacantRetirementIsIdempotentAcrossEveryFailpoint` holds one retirement effect across all five coordinator failpoints, and `TestEnumerateStateDomainThreadsReadsNamesOnly` pins that the census reads rollout file names only and reports a missing domain as an error. `TestPhase0ModelImportsNoMutationAdapter` counts `vacancy.go` as the tenth pure model file, so the Registry projection and the filesystem census stay outside that package.
63
- - `make test` / version-qualified negative evidence: Codex generation-pool Phase 7's zero-turn `thread/start → independent resume/read` probes remain lower-layer safety fixtures only. `TestPayloadFreeNativeCreateClosesCreatorBeforeIndependentDurableResumeBarrier`, `TestDurableResumeBarrierUsesFreshIndependentExactResumeReadUntilReady`, `TestDurableResumeBarrierDeadlineIsBoundedAndRetriesOnlyTheExactThread`, `TestDurableResumeBarrierFailureOutcomeTableIsClosed`, and `TestPayloadFreeDurableResumeBarrierKeepsDefaultAndPrivateRoutesExact` still prove that the experimental path uses no dummy turn, never crosses routes, and classifies failure content-free. They are not functional `projmux create codex` success. `TestPhase7PayloadFreeReadinessFailureIsNegativeSafetyEvidenceOnly` and `TestInstalledPayloadFreeCreateOutputClassificationRequiresFunctionalPlainSuccess` enforce that reclassification; `TestInstalledIsolatedGenerationPinnedEmptyPromptCreateSmoke` and `TestInstalledDefaultUpgradeOrdinaryCreatesActivateManagedGeneration` are retained as skipped historical negative fixtures, while `TestInstalledPayloadFreePlainFallbackOutcomeSmoke` owns the functional installed result.
64
-
65
- - `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`.
66
- - `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.
67
- - `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.
68
- - `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.
69
- - `make test` / `make test-integration` / `make test-e2e`: Window anchor and primary-shell separation Phase 4 closes projection and startup over final-v2 desired state. `TestSnapshotProjectionFinalV2RefsAndOpenFreshGolden`, `FuzzSnapshotProjectionIsScopedValidAndIdempotent`, and `FuzzOpenFreshKeepsOnlyCanonicalProjectShell` pin metadata-bearing and legacy snapshot projection, Agent-anchor/default-shell policy, target-subtree replacement, source-snapshot preservation, and fixed points. `TestProjectFreshStartPruneScope` preserves an existing canonical Project/Window/shell UID chain and exact transaction-level Registry bytes across repeated Open fresh, while `TestProjectFreshStartAgentAnchorCreatesExactMinimumShellAndRepeatsNoop` covers its one permitted shell allocation; `TestRestoredOfflineAgentAnchorContinueConvergesLazyShellAgentAndRepeatNoop` covers offline restore through lazy default-shell/Agent materialization. `TestAuthorshipPromotionPreservesSiblingProjectSocketAndOtherHostDesiredState` pins exact-socket positive convergence while sibling-socket and other-host-only desired refs/status plus Registry order remain byte-identical. The isolated smoke covers snapshot/Open fresh closed repeats with the exact UID chain, lifecycle Window survival, create/resume/materialize/focus, failure/repeat paths, and exact-socket sibling isolation.
70
- - `make test`: Install handover Phase 1 keeps every Window anchor writer inside the validator's anchor predicate. `TestWindowAnchorEligibilityPredicateBranches` pins the membership, role, and managed-Agent-Pane clauses of `windowAnchorEligibility`; `TestDeletePaneReanchorSkipsReleasedAgentPaneForEligibleShell`, `TestTransitionAgentReleasingAnchorPaneMovesAnchorToEligiblePane`, `TestAnchorWritersCloseOnNewDefaultShellWhenNoPaneIsEligible`, and `TestAttachAgentPaneMovesAnchorFromPreviousBindingToNewManagedPane` cover delete reselection, termination release with an empty `paneRef`, the default-shell close with preserved ineligible rows, and rebind including the snapshot-projection Offline binding; `TestAnchorWritersPreserveValidationOverRandomWindowConfigurations` (seed 20260913, 512 iterations) requires `Validate` after every writer; `TestTopologyAgentContinueAdmitsTerminatedAnchorAgentWithEmptyPaneRef` keeps the terminated anchor Agent out of the `still records a current paneRef` refusal.
71
- - `make test`: `TestStampProjectSnapshotSkipsUnboundPanesInRuntimeIDDuplicateCheck` pins that snapshot save's Pane runtime id duplicate check skips only a `MissingRuntime`=True/`RuntimeUnbound` Pane: its retained tmux id reused by a live Pane stamps the live Pane's UID, leaves the unbound Pane's `Status.Activation.RuntimeID` intact and absent from the snapshot, while two live Panes, a False condition, or another reason sharing the id still refuse with `share runtime id`.
72
- - `make test`: `TestStampProjectSnapshotSkipsUnboundWindowsInRuntimeIDDuplicateCheck` pins that snapshot save's Window runtime id duplicate check skips only a `MissingRuntime`=True/`RuntimeUnbound` Window: its retained tmux id reused by a live Window stamps the live Window's UID, leaves the unbound Window's `Status.RuntimeID` intact and absent from the snapshot, while two live Windows, a False condition, or another reason sharing the id still refuse with `Registry Windows ... share runtime id`.
73
- - `make test` / `make test-integration` / `make test-e2e`: Codex native control plane Phase 3 projects a closed, content-free app-server lifecycle into Agent interaction, badges, attention, and notifications. Unit and fuzz event sequences pin exact thread/turn/item/request approval identity, waiting-plus-unresolved gating, resolution and leave-waiting clears, successful completion once, failed/interrupted non-completion, and old-epoch/foreign-identity writes at zero. Fake proxy tests pin observation-only server requests with no approval response, snapshot convergence, invalidation-before-fallback ordering, reconnect replacement, and all Codex hooks writing zero through pending/healthy/invalidating authority. Settings and delivery tests pin the independent `approval_required`/`response_complete` Notify/State only/Quiet matrix across app-server and hook-fallback epochs, explicit raw fallback-only precedence, exact non-Notify queue clearing, raw hook-byte preservation and no inference, mixed multi-Pane source/reason/epoch diagnostics, Quiet aggregate-badge suppression, and content-free `describe agent` rows. Isolated tmux integration and e2e cover exact Registry/Pane/generation/thread binding, native/fallback/reconnect behavior, queue/desktop outcomes, sibling containment, and a privacy/scope inventory that excludes prompt/control, progress/diff, catalog/session-state, provider configuration, schema, and release changes.
74
- - `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.
75
- - `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.
76
- - `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. Installed lifecycle evidence is now owned by the hermetic topology qualification below rather than a second daemon-only setup/readiness assertion.
77
- - `make test`: Bounded lifecycle snapshot transport opens one owned, peer-birth-witnessed direct app-server connection and incrementally projects complete `thread/read` metadata before the broker's generic 1 MiB IPC materialization boundary. `TestEndpointPeerWitnessRejectsSamePathReplacementAndSyntheticPIDReuse`, `TestOwnedLifecycleReadPreservesSharedHistoryAndApprovalAuthority`, and `TestLifecycleSnapshotFivePointTwoMegabytesCrossesClientBrokerIPCBodyFree` pin same-process route proof, shared-vs-owned history equivalence, unchanged approval/event authority, literal 5.2 MB upstream success, and body-free typed IPC. `TestLifecycleTransportAccountsForSimultaneouslyRetainedProjectorState` adds the whole-state retained boundary: 32 nested 64-field objects keep wide decoded keys, fixed duplicate/projection storage, maximum captured flags, and every scalar needed for the body-free summary live together; its independent 64-bit ABI oracle requires the exact 2 MiB boundary on JSONL and WebSocket, rejects +1 and the former key-byte-only boundary, and makes omission of any body/header/capacity/reserve term observable. `TestLifecycleTransportReservesEscapedScalarBeforeValidation` reaches 33 live objects after retaining all nine possible metadata bodies and 64 maximum flag bodies, then forces the largest escaped-string working state before its decoded-size refusal: 6,146 encoded scratch bytes, a 6,152-byte unquote buffer, and a 6,143-byte decoded string. An independent boundary/+1 oracle pins the corrected 108 KiB fixed reserve within the unchanged 2 MiB retained-state cap for both transports, including management state and a conservative later, non-overlapping summary allowance. The other lifecycle JSONL/WebSocket boundary tests pin JSON/wire/chunk/depth/value/field/turn/scalar/identity/summary/frame/control limits, malformed UTF-8 and fragmentation, cancellation and late-result zero. `TestLifecycleSidecarRolesTokensAndOldNewCompatibility`, `TestLifecycleAdmissionRetryAndIdleLedgerReclaimAreBounded`, `TestEveryOwnedFailureLeavesSharedSiblingAuthorityUnchanged`, and the publication-fence tests pin session-scoped sidecars, old/new bind-submit-answer compatibility, endpoint-two/thread-one/queue-zero admission, disconnect/rebind retry fencing, bounded idle reclamation, sibling survival with shared epoch unchanged, and stale result zero across every route/activation axis. The shared broker connection remains the sole notification, approval, bind, submit, and answer authority; the upstream complete response remains large while no turn body crosses local lifecycle IPC. The 2 MiB guarantee is retained projector state, not cumulative allocation, allocator peak, goroutine stack capacity, whole-heap peak, or RSS.
78
- - `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. The official daemon-version observation matrix starts each fixture behind an explicit process-ready signal, runs the unchanged read-only argv only after readiness, and requires bounded clean process termination; the never-ready negative fixture fails with a readiness-specific deadline diagnostic, and the same four observations are required under `go test -race`. 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 without fixing private per-surface call counts, 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.
79
- - `make test`: Installed Codex app-server qualification Phase 0 centralizes clean-root validation, tmux stripping, contained socket/PID ownership, PR #857 readiness observations, terminal classification, exact cleanup, and the semantic command ledger in `internal/testutil/codexinstalled`. `TestInstalledHermeticTopologyQualification` emits validated version-tuple/topology/stage/class/reason JSON for bounded direct ready/close and managed payload provision/start/reuse/retire; payload absence is typed unsupported and blank/unknown/skip terminal classes are rejected. `TestInstalledIsolatedConversationCatalogSmoke` owns only the real `thread/list` canary, while `TestInstalledIsolatedPreTurnBootstrapSmoke` owns `thread/start`, second attach, and exact `thread/read(includeTurns=false)` without endpoint-not-ready skip. The deletion receipt is: `TestInstalledIsolatedDaemonLifecycleSmoke` → the hermetic topology test → direct/managed lifecycle → payload-missing classification; repeated catalog and pre-turn root/readiness/socket blocks → the shared fixture → their distinct protocol canaries → terminal startup/attach failures; app-local `installCodexArgvLedger` plus its raw argv allowlist → `codexinstalled.Ledger` → the unchanged model/broker assertions → semantic ambient/lifecycle mutation zero. `TestFailureCleanupRemovesOnlyExactOwnedArtifacts` and `TestManagedIdentityMismatchNeverAuthorizesForeignPID` prove foreign artifacts and mismatched PID/socket/backend evidence are never cleanup targets.
80
- - `make test`: Installed Codex app-server qualification Phase 1 adds a daily/manual, non-required `0.152.0` real-binary matrix without adding another protocol implementation. The hosted setup copies the official Linux x64 optional dependency's native standalone subtree into an isolated release root, verifies its exact package manifest and executable, and puts only that release's `bin` first on `PATH`. Its three matrix entries merge into the surviving Phase 0 lifecycle, `thread/list`, and pre-turn second-attach canonical tests; their unique boundary is invocation plus typed reduction, with no scheduled protocol body to delete or maintain. The runner reduces all component observations to exactly one terminal record per primitive, requires the declared version in every tuple slot, and turns install/version verification failure, missing/invalid output, cleanup refusal, or missing matrix artifacts into terminal non-pass evidence rather than a skip. The closed artifact contains only schema version, CLI/managed/app-server tuple, topology, primitive, canonical terminal stage, class, and enumerated content-free reason; reflection and strict decoding audit the schema structurally instead of scanning serialized bytes. Each child and the separate `Installed Codex Qualification` aggregate retain only typed JSON for 14 days, stay absent from `E2E Tests` and `Test`, and fail closed on unsupported/fail/infra-error while required fake C01 remains unchanged.
81
- - `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, indeterminate post-create refusal without a second lane, and zero-write rejection for another Pane, generation, or thread. Generation-pool Phase 3 above supersedes the old unavailable/unsupported automatic plain fallback; exact late hooks may refine only the current turn, native-bound hooks fail closed before tmux remap or Registry writes, and legacy/unmanaged hooks plus bounded late acknowledgement retain their previous behavior.
82
- - `make test`: Codex payload-free create now chooses a managed plain-interactive lane before any native controller or transaction-side provider mutation. `TestEmptyPromptCodexCreateUsesOnePlainCLILaneAndNoNativeBinding` and `TestEmptyPromptCodexSplitProducersKeepOnePlainCLILane` retain their symbols and now pin canonical/shortcut/default-intent functional success with zero Current/Resolve/create/resume calls; `TestPayloadFreeCodexFallbackCarriesContentFreeDeclaredAuthority` pins the `provider-hook` / `native-fallback` / `payload-free-fallback` runtime tuple. `TestCodexFanOutKeepsCurrentPlainCLILaneWithoutNativeCreate` remains the explicit `--interactive-only` guard, and `TestDeclaredPlainCodexLaneNamesOnlyTheByDesignLanes` closes payload-free fallback, interactive-only, and rollout-catalog resume as the three declared plain lanes. Prompted create, native catalog resume, stored Agent resume, and generation-pinned routes keep their existing native tests and no-second-lane rules.
83
- - `make test` / opt-in private installed conformance: Codex payload-free Phase 1 qualifies only an exact RoleTUI/RoleAppServer digest, app-server/protocol, bound socket route, state domain, and platform/arch tuple. `TestExactPayloadFreeCapabilitySeparatesReadVisibleFromStoredResumable` owns the independent zero-turn start/read/stored-resume reduction, while `TestRemoteNewCapabilityRequiresExactFirstRealInputThreadAndTurn` rejects TUI liveness and unrelated first-turn identity. `TestCapabilityCacheInvalidatesEveryExactTupleAxis` and `TestCapabilityCacheCorruptFutureAndTrailingRecordsResolveUnknown` pin immutable fixed points, endpoint/socket/state drift misses, and invalid-cache unknown/plain fallback. `TestCodexPayloadFreeDoctorAndCreatePlannerShareExactRecordProjection` requires byte-semantic Doctor/create parity, and `TestCodexPayloadFreeUnknownCapabilityCannotBypassPhaseZeroFallback` keeps Phase-2 routing closed. `TestInstalledExactPayloadFreeCapabilityMatrix` takes only explicitly available 0.152.0/0.152.1/0.153.0 binaries, copied private state, private sockets and tmux, separates creator-live/closed read/resume evidence, and starts the exact remote-new TUI without sending any prompt, input, or turn. The installed remote-new row therefore remains `unknown`; only the separate unit/conformance seam can reduce an externally supplied matching first-real-input thread plus exact one-turn observation to support. Unset versions are `unavailable`, never synthesized; cleanup makes the private route non-current, outbound input/turn and ambient/default lifecycle mutation counts are zero, and the old liveness-only `codex-installed-capabilities.json` is historical `infra-error/evidence-incomplete`, not capability authority.
84
- - `make test-integration` / `make test-e2e`: Provider-stub fixtures that deliberately test supervisor wait status, resource materialization, rename, startup/topology replay, exit reconciliation, or ControlSession placement rather than provider protocol request the explicit `--interactive-only` lane for their Codex rows; their shell stubs neither provision app-server authority nor become a native-create fallback. L06/L10/L11 move only their provider-neutral resume/replay witnesses to Claude instead of manufacturing an endpoint for a legacy Codex ref, while L11's clean no-resume Codex row remains an explicit endpoint-free lane. L19's Codex shim reports app-server unavailable before selecting its existing rollout-catalog/plain-resume row, which still gains no native authority. C01 remains the native exception: it supplies one isolated canonical `CODEX_HOME` state domain and fake app-server current generation, and its create rows retain native authority.
85
- - `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 catalog smoke is opt-in through `PROJMUX_CODEX_CATALOG_SMOKE_ROOT=/tmp/<isolated-root>` and uses the canonical disposable direct endpoint fixture; it owns only the `thread/list` compatibility canary, while exact `thread/read(includeTurns=false)` installed evidence stays with the pre-turn second-attach smoke.
86
- - `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. 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. The corrective provider projection now maps each provider to pre-global-cap discovered count (including zero), search failure, or Settings-disabled, with localized EN/KO footer text and no native/rollout/fallback transport vocabulary; disabled providers perform no discovery read and enter no selectable row. 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` on one localized status line, including independent EN/KO clipping at 80 columns. The recovered footer row extends only the AI Resume down `SelectionDetail` dock by exactly one row, leaving the popup, provider row, and pre-compaction MoreNotLoaded list limit fixed; generic pickers and `MoreNotLoaded=false` retain their prior budgets. Turn enrichment and page continuation do not write the current list. Focus routes 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. Its label authority is exact sessionRef-bound Agent `metadata.displayName` → provider-owned non-ID title → exact Agent topic → exact stable name → localized `Untitled/제목 없음 · …last4`; full provider/conversation identity stays in Value, SearchKey, routing, and selected detail, while native/rollout source and fallback reason stay only in selected detail. `TestNativeAIResumeTrustFramesLocaleAndSizeGolden` pins actual 80x24/120x40 EN/KO frame rows and widths; the AI Resume-only detail limiter requires three real nonblank preview lines before overflow, uses the recovered row for one more real content line when continuation is present, and cannot count post-preview metadata as content. Geometry goldens and a 20-row round-trip cursor sweep pin identical 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.
87
- - Visibility clarification: native/rollout source is not rendered in a row or footer; it remains in the frozen hidden SearchKey for the existing routing-search contract and is visibly inspectable only in selected detail. Fallback reason is absent from both rows and SearchKey and is visible only in selected detail.
88
- - Label-source clarification: Codex rollout and Claude transcript labels are prompt-derived by the existing summary scanners, so they are not provider-owned title authority and remain absent from both primary labels and SearchKey. No extra transcript read is introduced. The Project-wide naming closure Phase 1 entry below supersedes the historical Agent metadata fallback chain: exact Agent topic/provider `Context` or the localized untitled suffix supplies the label, while the stable Agent name remains search-only.
89
- - `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.
90
- - `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.
91
- - `make fmt`: repository formatting for Go, shell snippets, and generated docs where applicable.
92
- - `make fix`: safe automatic fixes such as `go fix` and repository-approved cleanup steps.
93
- - `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.
94
- - `make security`: pinned govulncheck, gosec, staticcheck, gitleaks history/current-tree, actionlint, and shellcheck gates. Reviewed gosec/staticcheck findings use checked-in fingerprint/count baselines so only new findings fail.
95
- - `make test` / `make test-integration` / `make test-e2e`: Agent Usage HUD provider/window visibility coverage pins the `UsageSupported()` provider order and explicit Claude/Codex `5h`+Weekly versus Antigravity Weekly-only capability matrix; missing/invalid defaults of off for Codex `5h` and on for every other leaf, with explicit saved `on` overriding that compact default; atomic 0600 leaf persistence; the full overall/provider/window saved-effective parent matrix and restoration; en-US/ko-KR Settings View navigation; ambient-only filtering after collect/load/cache/backoff; weekly-only official-window width shedding; zero hidden label/bar/separator/staleness residue and all-provider-off empty text; lossless explicit table/JSON and CachedState popup; unchanged cache/backoff/provider-enablement byte/hash/inode/mtime sentinels; and Settings save to generated/live exact source-file apply.
96
- - `make test` / `make test-integration`: Settings IA Phase 4 row-0 HUD visibility coverage pins default/saved/invalid effective-source projection; independent Notifications HUD and Agent Usage HUD persistence; byte-identical default app/standalone output; exact four-combination range/alignment/full-width behavior; all-off one-line (`status on`) convergence with the structural Window row, quiet autosave, and no stale row residue; Settings-generated config source reload; unchanged notify queue and usage snapshot state; and production exact-socket live tmux apply across all four combinations under the integration smoke's guarded socket cleanup.
97
- - `make test` / `make test-integration`: Settings IA Phase 5 row-1 segment controls cover Project, Working directory, Git, Resources, Clock, and Settings launcher independently: five visibility files reuse the Phase 4 missing/invalid→on default, saved/effective/source, atomic 0600 persistence, generated-config no-write, and live exact `source-file` apply contracts; Resources has no duplicate visibility file and uses `live-resources` alone to remove its range/job and guard the sampler/cache mutation path; cwd/Git detail Views own separate Visible and Icon controls so icon off preserves their text; app/standalone exact mixed output removes hidden ranges and owned spacing with retired Kube ranges/jobs absent; and Settings launcher off retains the CLI/default-keybinding Settings entry.
98
- - `make test` / `make test-integration` / `make test-e2e`: Settings IA Phase 6 retires the hidden Project recipe and Effective merge handlers/catalog/i18n plus every Kube-specific product projection. Named regressions pin the Project tree to Automation and Snapshots, reject public and internal Kube status routes, prove app/standalone generated configs contain no Kube range/job/binding, remove Kube from popup/sidebar switch previews and hook/session environments, preserve project hooks/trust plus `[startup]` and arbitrary generic `[env]` keys, and make a legacy `[kube]` update fail with the exact documented diagnostic while preserving original bytes and mtime. The integration smoke checks the same generated/live tmux absence on an isolated exact socket.
99
- - `make test`: CLI information architecture v2 Phase 0 Cobra command catalog and help boundary: the `internal/cli` manifest holds every public route plus the 2 hidden internal helpers with exactly one canonical/shortcut/compatibility/internal disposition each (34 public, 9/7/13/5 tally, the ninth canonical being the `get` node the selector Phase added) and zero orphan routes in either direction against the canonical route manifest, which pins `rebind project`, `prune project`, `agent usage`, and the Pane-read `cwd` field projection alongside the closed shared `-o uid|name|ref|metadata|json|pane-id|none` catalog that deliberately excludes `cwd`; every graph route also carries exactly one validated identity/address/topology/desired-state/runtime/focus/cardinality allowed-effect record plus a closed nullable domain-effect discriminant, runtime help and generated `docs/cli.md` project the same tuple without handler/runtime access, the schema-v3 handler outcomes, full route-effect manifest, and mismatch ledger are golden-pinned, and non-route fixtures reserve same-root reparent and `agent-delivery` semantics without adding public routes; a golden fixture freezes the byte-identical top-level listing now rendered from the manifest and a command-tree golden freezes the Cobra surface (36 route nodes, hidden flags, `DisableFlagParsing` on every bridge, no injected `help`/`completion`/`__complete` command); a raw-argv table proves each bridge forwards `--`, positional arguments, unknown flags, and payload tails untouched, that handler-returned `flag.ErrHelp` and exit coders survive the Cobra boundary, and that unknown commands keep `unknown command: <token>` on stderr with exit 1 rather than a usage error; and the help no-side-effect negative tests prove every root, nested, and hidden internal help invocation exits 0 with stdout-only manifest help across every `flag`-equivalent spelling — both dash prefixes of `help`/`h`, bare and with any `=value`, matched on the flag name and pinned against the real `flag` package, with a guard that no leaf parser defines a competing `help`/`h` flag, reaches no handler, writes nothing into an isolated home, records zero operational-error journal rows through the same policy the process entrypoint uses and zero phantom state-changing success rows at any route depth (the diagnostics direct-help classifier walks positional sub-route words but stops at the first flag, so `upgrade --ref --help` and a literal `pin add help` path stay conservatively mutation-capable), skips the legacy hook filesystem migration, and — with a bounded timeout regression guard — no longer blocks on the `popup-wait-key` tty read, while a help flag after `--` stays payload and a bare nested `help` word still reaches its handler.
100
- - `make test`: CLI information architecture v2 Phase 1 resource metadata foundation: `internal/core/metadata` unit and table tests cover the closed `Project`/`Window`/`Pane`/`Agent` kind set under `apiVersion: projmux.io/v1alpha1`, the one-time name-base seed priorities for every kind (Agent topics and raw pane titles are excluded everywhere), name-key validation, the persisted `nameReservations` suffix allocator proving the lowest free suffix is scan-order independent and that an explicit collision fails with `ErrNameConflict` and zero reservations written, offline Project bootstrap topology with a resolvable `spec.defaultShellPaneRef`, uid immutability across runtime creation, teardown, and rebind, zero heuristic uid merges (same-basename roots stay distinct; only an exact saved root reuses its uid), rebind and root-missing/root-returning `MissingRoot` condition lifecycle with preserved first-observed timestamps and name reservations, reverse-order operation rollback restoring a byte-identical registry, the closed Agent phase set with its exit classification and resumable Offline/Failed transitions, `schemaVersion` classification that fails closed on any newer envelope and on an unversioned document (production registers no migration step, since v1 is the first shipped envelope), a `testdata/registry-v0-source.json` → `testdata/registry-v1-migrated.golden` migration pair driven by a test-injected older step with reservation backfill that never renumbers existing names, full registry structural validation, and Session State round-tripping through additive `omitempty` `metadata` blocks at the unchanged snapshot `version: 1` including deterministic positional reconciliation of pre-metadata snapshots. `internal/integrations/metadata` covers the locked `<state>/projmux/metadata/registry.json` store: a newer `schemaVersion`, malformed JSON, and a content-bearing document with no `schemaVersion` are all refused byte-for-byte with no write, no temp file, no backup, and no quarantine on `Load`/`Update`/`Migrate`, while an absent, empty, or whitespace-only file still yields a fresh empty registry; migration atomicity is proven against a test-registered older step by injected failures after the backup, after the staged temp write, and just before the atomic replace, each leaving the original bytes and no leaked temp; a successful migration keeps a verifiable pre-migration backup and is idempotent; a failing operation writes nothing; a bootstrap registry golden freezes the camelCase file format and 0600 mode; concurrent registrations serialize through the lock with unique allocated names; and an end-to-end test round-trips an offline snapshot through both the registry store and the session-state store, reconciling every Window and Pane back by uid. The tmux mirror tests pin the exact session/window/pane option writes, the new window-scoped options, `automatic-rename off` on managed windows, `rename pane` touching only `@projmux_pane_label` and never `pane_title`, uid ↔ target resolution in both directions, and write-free legacy observation. `internal/app` and `cmd/projmux` prove the metadata usage-error seam: explicit name and rebind-root collisions reach exit code 2 through the existing `UsageError` path with zero mutations, while a newer registry schema stays a runtime error at exit code 1.
101
- - `make test` / `make test-integration`: Registry-first control plane Phase 0 durable recovery envelope: `internal/integrations/metadata` proves the initialized boundary in both directions -- before the first commit every read answers the empty registry and creates nothing under the state dir, the first commit publishes `registry.json` plus the `registry.initialized` marker at 0600 below a 0700 directory, and after that a missing, empty, or whitespace-only registry is `ErrRegistryStateLost` on `Load`, `LoadReadOnly`, `LoadSnapshot`, `Update`, and `UpdateConvergent` instead of a fresh identity domain; a registry written before the marker existed still reads and gains the marker on its next write; each same-version semantic write copies the prior verified bytes into bounded `recovery/` copies whose chronological order survives retention, skips the copy for structurally invalid prior bytes, and a migration keeps its versioned `.bak` without spending a recovery slot; a convergent no-op leaves registry bytes, mtime, inode, marker, and copies untouched; an injected failure at the staged fsync, the staged write, staged validation, the recovery copy, the marker, the pre-replace directory sync, or the atomic replace leaves the prior registry byte-identical with the copy rolled back, no staged temp file, no lock, and a retry that still converges, while a failed first commit leaves neither a registry nor a marker so the next read is still first use; missing-after-initialization, malformed, too-new, and unreadable stay four distinguishable causes that create no registry and no uid; and the integration smoke repeats init -> updates -> no-op -> loss -> diagnostic -> marker-removal remedy against the installed binary under an isolated `XDG_STATE_HOME` below the smoke root, asserting the bounded five retained copies hold the bytes their writes replaced.
102
- - `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 1 registry recovery boundary: `projmux reconcile registry` plans with zero writes and restores only an explicitly named verified source. `internal/integrations/metadata` proves the plan classifies the current registry and every bounded candidate -- `valid`, `first-use`, `missing`, `empty`, `malformed`, `schema-too-new`, `invalid`, `unreadable` -- with a `sha256:` digest of the exact bytes and the resource/reservation counts, newest first over both the write-copy and replaced-copy families, while inspecting a first-use state dir creates nothing under it and inspecting a lost one leaves every file's mode, size, and mtime unchanged; malformed, empty, too-new, dangling-`ownerRef`, and duplicate-uid sources are refused as `ErrRecoverySourceRejected` with the live registry and the whole state dir byte-identical, including when the only candidate is a too-new envelope; files this store did not write are never offered as candidates; a valid current-v4 restore publishes the verified bytes verbatim so uids, owner relations, and name reservations round-trip through `LoadReadOnly`, while a v3 source keeps an exact versioned backup and checksum-bearing migration report before deterministic canonical-v4 publication, and a repeat restore compares the canonical publish checksum as a byte no-op that takes no preserved copy and leaves the lock removed; a restore into a marker-less state dir establishes the boundary so a later loss reads as state loss; the bytes a restore replaces are preserved -- malformed originals included -- at 0600 as their own bounded family that is offered back as a candidate and never consumes the write-copy history; a stale `--expect-source-checksum` or `--expect-current-checksum`, a source rewritten under the lock, and a registry replaced under the lock all refuse as `ErrRecoveryRaced` with nothing published, no preserved copy, and no staged temp file, an ambiguous or unknown selector is never ranked or guessed, and an injected staged fsync or directory sync failure leaves the registry and marker byte-identical. `internal/app` proves the command boundary: a dry-run is byte-identical output over two runs with the whole state dir unchanged and zero tmux calls, a sourceless run only ever plans and prints the exact guarded next command, every refusal still emits the report and exits non-zero with the current registry untouched, usage refusals happen before the store is opened, and the human projection carries the same state, candidates, reasons, and next command as the JSON. The mirror diagnostic is evidence only: it appears exactly when recovery is needed and no verified copy exists, resolves containment from stable tmux ids, counts uid-less panes as unattributed rather than recoverable, counts provider-carrying panes as proof of Agents whose uids are not mirrored, always states the six unrecoverable scopes, makes only read-only calls on the one exact socket, and reports a missing transport or a dead server as a reason without probing another server or generating a registry. The integration smoke repeats state loss -> preview -> guarded restore -> resource reads -> repeat no-op -> corrupt/too-new/ambiguous/raced refusal against the installed binary under an isolated `XDG_STATE_HOME` below the smoke root.
103
- - `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.
104
- - `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.
105
- - `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.
106
- - `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.
107
- - `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.
108
- - `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.
109
- - `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.
110
- - `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.
111
- - `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.
112
- - `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.
113
- - `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.
114
- - `make test` / `make test-integration` / `make test-e2e`: Declarative contract stabilization Phase 15 projects one explicit snapshot into one exact closed Project Registry subtree. `FuzzSnapshotProjectionIsScopedValidAndIdempotent` and metadata/legacy/collision tables pin pure planning, target Project identity, unrelated Project and ControlSession preservation, owner/kind collision refusal, and a zero-diff repeat; commit tests require pre-commit zero writes, byte-identical source snapshots, ordinary materialization only, committed desired state plus an item notice after runtime refusal, and exact `-L projmux switch-client -c` as the final handoff. Closed-Project startup exposes exactly `Continue project` and `Open fresh`, with Esc navigation and picker-failure Continue fallback. `FuzzOpenFreshKeepsOnlyCanonicalProjectShell` is the production Fresh planner: the canonical Project Window and one Window-owned shell Pane UID/recipe survive when available, an exact minimum shell is created when absent, and every other target descendant, reservation, and conversation pointer is removed. Invalid anchors refuse with zero writes, repeat planning is a Registry zero-diff, and snapshot bytes are unchanged. Negative audits keep direct snapshot replay, global Registry replacement, snapshot deletion, retired startup labels, and hidden runtime fallback out of the startup path.
115
- - `make test` / `make test-integration` / `make test-e2e`: Declarative contract stabilization Phase 10 closes managed runtime mutation behind printable typed plans. Named coverage keeps the verb inventory and product-surface table closed, proves deterministic bytes and successful execute → exact effect ledger → empty replan, rejects unknown observation with zero Registry deletion/runtime kill, audits every non-test app and tmux/mux/metadata/replay source (including typed helpers, argv callbacks, and variable argv), and bijects generated catalog, Pane-menu, popup, hook, and native provider/resume picker producers to planned or exact semantic-exemption rows. The executor validates printable Socket/PhysicalSocket/server-generation authority before pre-effect reobservation can declare a repeat empty, then validates every pending semantic guard before the first write. Focused controller tests cover Project/ControlSession Window create/rename, Project/sidebar and Session-picker stop, first-use Project and attach Home bootstrap, typed automatic layout operands, batch Pane/Window delete, app-owned path/pid/app/logical guards, exact inherited standalone `TMUX=path,pid,index` plus producer-anchored `$N`/`@N`/`%N` authority, and explicit controller `--socket-path` authority with PID/blank markers plus action UID/session/window guards and no inferred Pane. Partial-marker/forged/recycled-server refusal, fresh bootstrap generation binding, full-tuple owned reverse rollback, and self-target commit/flush ordering are pinned. Generated popup/menu actions carry their verified Pane anchor into the child without synthesizing `TMUX_PANE`; Settings persists config but refuses live source/retirement on standalone authority. The isolated tmux suites remove inherited `TMUX`/`TMUX_PANE` where independence is required, use a unique socket root, assert physical `-S` writes, and clean only exact observed sockets.
116
- - `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.
117
- - `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.
118
- - `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. Required L06 on the isolated real-tmux socket now holds the persistent `registry.json.flock`, starts one real CLI waiter, and releases through held/released/settled FIFO barriers with no sleep; it requires the waiter to succeed with exact Registry owner/runtime bindings, tmux UID/owner mirrors, unchanged pre-existing bindings, the persistent flock present, and zero transient residue. The former eight-way full-create burst is an explicit non-gating stress observation invoked exactly as `PROJMUX_E2E_REGISTRY_STRESS=1 make test-e2e E2E_SCENARIO=L06`; it gates all eight real CLI processes into actual overlap and emits a typed PID/status/outcome/start/finish record for every racer without making all-success throughput or host capacity a required guarantee.
119
- - `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.
120
- - `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.
121
- - `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.
122
- - `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.
123
- - `make test-e2e`: Implicit active target selector Phase 2 extends the isolated real-tmux registry smoke with multiple Projects, proves a synthetic current client sees only its Project's Panes by default and every Project with `--all-projects`, preserves outside-tmux global inventory, and refuses a missing active Window binding with zero stdout. The smoke strips inherited `TMUX`/`TMUX_PANE`, uses a dedicated `TMUX_TMPDIR` and unique socket, guards the queried actual socket root, and cleans up only exact validated sockets.
124
- - `make test`: CLI information architecture v2 Phase 3 public verb-to-kind and runtime-domain parity: `internal/cli` grows six canonical top-level nodes (`delete`, `describe`, `rebind`, `rename`, `restore`, `runtime`) alongside the existing `get`, taking the manifest to 40 public routes plus the 2 hidden helpers with a 15/7/13/5 disposition tally and zero orphans in either direction; a written-out inventory table proves all 33 originally inventoried routes survive with an unchanged disposition and hidden flag, the added kind tokens do not shadow an existing subcommand under `pin`/`tag`/`prune`/`attach`, and both goldens are regenerated together. `internal/core/selector` adds the Project and Agent resolution pipelines through the same three-stage engine — a Project list read over the whole registry, an Agent set scoped by `--project`/`--window` with Window-scoped name uniqueness — and keeps `displayName`, `spec.root`, and tmux handles structurally unmatchable; Agent references render without a `--agent` flag spelling because the grammar has none. `internal/app` covers the routes: the plural reads are 0..N with an empty result as a success and a `<Kind>List`/`<Kind>MetadataList` envelope for the structured modes, `describe` is exact-one per kind with a key/value description block, and a maintained adoption table asserts every route resolves against the declared `<verb, kind>` cardinality cell rather than a per-route rule. `focus project|window|pane` resolves an already-live exact-one target from read-only `list-sessions`/`list-windows`/`list-panes` and exits 2 on an offline Project, Window, or Pane, on a prefix-match fallback, and on an ambiguous live match, with zero session/window/pane-creating tmux verbs in the call log, while the legacy `focus --target` spelling keeps its prefix-match and index fallbacks. `attach project` forwards to the Project open path only from outside tmux and returns a usage error naming `projmux focus project <ref>` from inside a client, reaching the open path zero times on every refusal. `rename` changes only `metadata.name` and its scope reservation, and a collision, an ambiguous target, or a missing `--name` commits zero writes. `rebind project` rewrites only `spec.root`, measured by walking a real temporary directory tree before and after, refuses a non-existent, relative, or already-bound root with zero writes, and leaves two Projects that come to share a basename as two uids. `prune project` requires `--missing` and `--older-than`, lists candidates bounded to 5 rows plus an omitted count without opening a write transaction, excludes recovered roots, live runtimes, and freshly observed missing roots, deletes only under `--yes`, and short-circuits an empty registry. `delete` preflights the full uid set and the whole descendant plan, prints it under `--dry-run` with zero transactions, requires confirmation for any cascade or multi-target run while exempting an exact-one leaf Pane, exits 2 without `--yes` in non-interactive mode, aborts when the cascade plan changes between preflight and execution, and leaves a validating registry in which a Window cascade preserves its Project, an Agent cascade preserves its Window and sibling Panes, and deleting an Agent-owned current Pane leaves the Agent alive as Offline. The runtime domain and the remaining canonical spellings are parity aliases proven by an argv-forwarding table that pins the exact tail handed to `sessions`, `attach auto`, `kill tagged`, `tag`, `runtime prune`, `notify list|ack`, and `session-state status|delete|restore`, relays their streams and errors untouched, and reaches no other handler. `get` and `describe` stay off the pre-dispatch legacy-hook filesystem migration while the mutation routes keep the historical behavior.
125
- - `make test`: CLI information architecture v2 Phase 4 Agent namespace and lifecycle parity: `internal/cli` grows two canonical top-level nodes (`agent`, `create`), taking the manifest to 42 public routes plus the 2 hidden helpers with a 17/7/13/5 disposition tally and zero orphans in either direction; the written-out inventory table still proves all 33 originally inventoried routes survive unchanged, `ai`, `usage`, and `status usage` included, and both goldens are regenerated together. The Agent provider enum is derived from the provider catalog rather than hand-written, so an aggregate table proves `selective`, `resume`, and `shell` are members of neither the enum nor the create-shortcut set while `selective`/`resume` stay a named closed picker-adapter concept; the `create <provider>` shortcuts are opt-in through an explicit catalog flag, collide with no resource kind or shared verb, and render in their own `Provider shortcuts` help group instead of among the resource kinds. `internal/app` covers the routes: a written-out argv table proves `create agent --provider`, `create codex|claude|antigravity`, and `create pane` normalize onto the exact `ai split` argv the current spelling parses — `--placement` for the bare positional, `-o pane-id` for `--print-pane-id`, and the `--` payload forwarded untouched — while a missing `--provider`, a picker spelling, `shell`, an unknown provider, a shortcut that respells its own provider, a `left` placement, and the legacy `--force-agent`/`--agent`/`--print-pane-id` flags are all usage errors that reach the split handler zero times, and the projections needing the resource-backed create composition fail as runtime errors so exit 2 keeps meaning invalid input. `agent status|topic|integrate` forward raw argv to the handler that owns them and `agent usage` forwards to the top-level usage handler instance with no prefix at all, which is measured as byte-identical stdout/stderr/exit against `usage` for six flag spellings. `agent resume` resolves exactly one existing Agent through the shared selector engine and the newly declared `<resume, Agent>` matrix cell, accepts only Offline and Failed, refuses a Running Agent with a usage error that names `focus pane` rather than performing it, and on every path forwards to no other handler and opens zero registry transactions. The Agent lifecycle table pins the `Pending -> Running -> Offline/Failed` transition set and the exit-classification mapping, and both a normal managed-Pane exit and an explicit `delete pane` through the public route leave the Agent alive as Offline with its uid, name, and owner intact and its `paneRef` cleared.
126
- - `make test`: CLI information architecture v2 Phase 5 internal plumbing isolation: `internal/cli` adds the hidden `internal` namespace, keeps plumbing out of public listings, and proves zero catalog/canonical/handler orphans. Generated standalone/app config and popup/hook payloads are exhaustively extracted so every emitted command uses an executable `internal` route. Raw tails, unknown flags, tmux formats, direct help, diagnostics classification, and bounded TTY helpers remain covered through their canonical internal spellings; newly generated artifacts contain no pre-namespace plumbing route.
127
- - `make test`: CLI information architecture v2 Phase 6 detached Project runtime materialization and Window/Pane create: `internal/cli` makes `create window` a real executable node and gives both resource-backed create nodes the full shared `-o uid|name|ref|metadata|json|pane-id|none` catalog, with the public route count, disposition tally, and both goldens unchanged because no top-level route is added; a help-honesty test resolves every advertised `-o` token against the canonical route, runs each one end to end, and rejects any usage synopsis naming a flag the parser does not define. `internal/core/selector` adds the `<create, Project>` exact-one cell beside the existing `<create, Window>` 1..N and `<create, Pane>` exact-one cells, and the maintained adoption record gains all three. `internal/app` covers the routes against an in-memory tmux server that models sessions, windows, panes, scoped options, and the escaped format separator: a Project whose `status.session` is absent or not live is materialized in the background and its brand-new session's initial window and pane are adopted onto the Project's first stored Window and its `defaultShellPaneRef` instead of being left as an orphan; the result kind follows the route rather than the side effects, so `create window` emits a `WindowList` even though it also creates a Pane and `create pane --create-window` emits a `PaneList` even though it also creates a Window; a missing `--window` name is a plain no-match with zero mutations and only `--create-window` promotes it to an atomic Window-plus-initial-Pane ensure inside the same single transaction, while an already-existing name is reused rather than duplicated; every fan-out target splits its own anchor exactly once, an explicit `--pane` resolves exact-one inside each target Window, and a missing or stale `spec.defaultShellPaneRef` is exit 2 with 0 stdout, 0 registry writes, and no `split-window` at all even when a live pane is available to fall back to; a pre-create hook refusal leaves zero registry mutations and zero runtime objects while a post-create hook warning is a logged success; no path issues `switch-client`, `select-window`, `select-pane`, or `attach-session`, reads any focus format, or omits `-d` from a window or pane creation; `--placement right|down` maps onto the closed `-h`/`-v` split axis; the runtime ledger rolls back in reverse order and only removes objects still carrying the uid this operation mirrored onto them, leaving a bystander session and a re-bound window untouched; the negative table proves `displayName`, bare uids, commas, `--create-window` without an exact name or with `--selector`, `left` placement, the Pane-read `cwd` projection, and a `MissingRoot` Project are all usage errors with zero mutations; and `create pane` without `--project` still forwards the byte-identical legacy shell-split argv, with the discriminator pinned across every flag spelling and proven not to fire on payload text. The registry's first real production use is exercised against the actual locked store on a real filesystem: a create from a completely empty state creates `registry.json` at 0600 below a 0700 directory with no leaked lock, registers every discovered workdir with a bootstrap Window and a resolvable `defaultShellPaneRef`, and validates; a second create extends it without renumbering or re-identifying a Project; six concurrent `--create-window` runs on one name serialize through the `O_CREATE|O_EXCL` lock and converge on exactly one Window uid with one Pane per racer and one live tmux window; and a legacy-state fixture proves the first real import allocates `window`/`window-1` independently of runtime attributes, projects the observed `window_name`s into displayName, exposes the separation through `describe window`, turns `automatic-rename` off, mirrors the allocated uids back onto exactly the tmux objects it imported, and does not create new identities on a second pass. `internal/integrations/metadata` pins the tmux format separator contract that first use exposed: formats carry the escaped `\037` spelling because tmux 3.5a renders a raw `0x1F` as its octal escape while tmux 3.6 passes it through, and row parsing folds the escaped spelling back before splitting.
128
- - `make test-e2e`: CLI information architecture v2 Phase 6 detached create against a real tmux server on a dedicated exact socket under the smoke root, with inherited `TMUX`/`TMUX_PANE` stripped from every call, the real `#{socket_path}` proven to be inside that root before cleanup, and only that socket killed: a pre-create hook refusal creates neither the registry nor a session and writes nothing to stdout; a post-create hook failure still commits and prints its result with the warning on stderr, and the registry lands at 0600; the legacy naming migration gives an `automatic-rename on` Window the stable name `window`, retains its observed `window_name` as the separately described displayName instead of deriving identity from its user Pane label, turns automatic-rename off, and mirrors the Window and Pane uids onto the live objects; `--placement right` and `--placement down` produce the two split axes measured from real `pane_left`/`pane_top` geometry, run from inside a real pane whose completion signal is an exit-code marker file rather than pane contents or `send-keys`, and leave the session's active window and pane unchanged with neither new pane active; `--create-window` converges on exactly one Window and still returns a `pane/` ref; a stale `anchorPaneRef` is exit 2 with 0 stdout, a byte-identical registry, and no new pane; and an injected `split-window` failure rolls the operation back to the exact window count and registry bytes it started from.
129
- - `make test`: CLI information architecture v2 Phase 7 Agent create composition: `create agent` and the `codex`/`claude`/`antigravity` shortcuts gain a resource-backed detached route behind the same `--project` dispatch discriminator `create pane` uses, so the `--project`-less invocation stays the byte-identical `ai split` bridge while the canonical route allocates a Window-owned Agent plus its Agent-owned `<agent-name>-pane` managed Pane, reuses the Phase 6 anchor resolution, `--create-window` ensure, operation ledger and rollback, and enforces a new `{create, Agent} = 1..N` fan-out cell that four tests falsify when it is weakened; a matrix covers provider-required refusals on every canonical spelling, shortcut-to-canonical registry-snapshot equivalence, non-reuse against a Window that already owns a running Agent of the same provider (`codex` → `codex-1` → `codex-2`, first Agent's `status.paneRef` untouched), Window-scoped explicit `--name` (legal across two Windows, exit 2 with zero mutations inside one), payload-after-`--` reaching the provider launch but never the Agent or Pane name, all seven shared `-o` modes end to end with `AgentList`/`AgentMetadataList` envelopes for a single result and `-o pane-id` resolving to the managed Pane rather than the anchor, multi-window fan-out hitting each Window's own anchor exactly once, four failure stages each leaving the registry byte-identical with no `Failed` Agent behind, six racers converging on one ensured Window uid under the real on-disk lock with `-race -count=10`, the Settings enabled-agents gate applying to the canonical route as a plain error without promoting `--force-agent`, and a help-honesty audit that runs every advertised `-o` token and every advertised `--flag` of the Agent node and all three shortcuts; the identity projections of the compatibility bridge become exit 2 naming `--project` as the fix instead of exit 1 claiming the composition is unwired.
130
- - `make test-e2e`: CLI information architecture v2 Phase 7 Agent create against a real tmux server on the same dedicated exact socket under the smoke root, with inherited `TMUX`/`TMUX_PANE` stripped from every call and only the verified `#{socket_path}` killed: a stub provider binary proves the launch actually ran in the created pane with the project root as its cwd, the managed pane carries both the Projmux Pane uid mirror and the `@projmux_ai_managed`/`@projmux_ai_agent` options, the active window and pane are unchanged and the new pane is not active, the `codex` shortcut allocates `codex-1` instead of reusing the first Agent, a `--` payload reaches the provider argv while the name stays `codex-2`, a missing `--provider` and an explicit name collision are each exit 2 with zero stdout and an unchanged registry md5, and the `--project`-less bridge still refuses its identity projections with a message naming `--project`.
131
- - `make test` / `make test-e2e`: canonical create even-axis parity shares the legacy AI split geometry parser, row/column peer selector, and deterministic remainder distributor across `create pane`, `create agent`, and every provider shortcut; route tests pin split → Window-local geometry read → axis-only resize ordering for explicit/default anchors, right/down placement, fan-out, ensured Windows, one-pane no-op, malformed/unreadable topology, unrelated rows/columns, and silent post-split observation/resize failures with zero false rollback. The shared geometry format carries the materializer's escaped `\037` row separator rather than a raw tab, and a dedicated test pins both that the format holds no raw tab and that a `_`-sanitized row parses as no geometry: tmux 3.5a rewrites a raw tab in list output to `_` while 3.6 emits it verbatim, so the tab spelling parsed every row as a single field and made equalization a silent no-op on 3.5a — the version the e2e image runs. The isolated real-tmux create smoke repeats horizontal and vertical canonical creates to four panes, measures a maximum one-cell size difference, preserves raw pane IDs, Registry mirrors, focus, and an unrelated Window's exact geometry, and retains exact socket-contained cleanup.
132
- - `make test` / `make test-e2e`: resource scope short-option parity registers `-p` for Project and `-w` for Window on the shared selector, standalone Pane read, resource-backed create, and canonical focus seams, while `-A` remains exclusive to `get windows|panes|agents`. Deterministic matrices compare long-only, short-only, and mixed argv for selector results, cardinality/error bytes, positional interleave, repeated Window order, stdout/stderr/exit, Registry mutation, and tmux call plans; explicit negatives keep Pane without a shortcut, reject `-A` elsewhere, and leave `agent usage --window` without `-w`. Help-honesty and generated-reference checks require every advertised alias to parse and preserve the root-help and command-tree goldens byte-for-byte. The isolated real-tmux smoke exercises short-form create agent/pane, read, focus, and all-Project escape on its exact guarded socket, with the first `--` preserving all following scope-looking payload bytes.
133
- - `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.
134
- - `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.
135
- - `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.
136
- - `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.
137
- - `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.
138
- - `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.
139
- - `make test` / `make test-integration` / `make test-e2e`: Picker-created Agent session continuity Phase 0 records the resume picker's already-normalized provider and exact conversation in the new Agent's existing durable `status.sessionRef` inside the canonical create transaction, before success and without waiting for a provider hook. Hook-0 success, same-conversation and cross-provider hook no-write behavior, Claude/Codex/Antigravity provider shape, ordinary fresh nil-ref behavior, exact-resume-only Continue (`PlanAgentResume` once and `PlanAgentLaunch` zero), visible resume-preparation refusal, and activation/commit rollback with no Agent, Pane, ref, or runtime orphan are pinned. The picker still stores no transcript, cache, turn, or new schema field; provider hooks retain their existing last-observation authority, and neither picker selection nor Continue silently falls back to a fresh conversation.
140
- - `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.
141
- - `make test`: Implicit active target selector Phase 1 destructive verb containment: an omitted selector on `delete window|pane|agent` no longer means the whole registry. The measured baseline is that it did, everywhere -- `delete pane --dry-run` with nothing else on the argv planned all 5 fixture Panes, and a 1..N cell is satisfied by that, so no cardinality rule was ever going to catch it. The route now sets the shared active-target seam plus a `wholeSetFlag`, and `resolveQueryFlags.resolve` refuses the empty selector outright whenever the fallback resolved nothing, before `selector.Enforce` is reached. Acceptance is measured per kind on the plan rather than on the exit code, because the pre-change whole-registry fan-out also exited 0 under `--dry-run`: inside tmux the plan header is exactly `would delete 1 pane and 0 descendant resources` / `1 window and 4` / `1 agent and 1`, the one target line is asserted verbatim, and every uid outside the plan is asserted absent from the output. Outside tmux all three kinds refuse with `resolve <kind>: no selector was given and no active tmux target resolved, so nothing was selected; ...` at exit 2 with 0 bytes on stdout, 0 transactions, 0 writes, and a byte-identical registry snapshot -- including under `--yes`, which answers the confirmation and has never been allowed to answer which resources, and including for `delete window` and `delete agent` whose baselines were 33-line and 11-line whole-registry plans. The refusal is asserted to be neither the 1..N cardinality error nor the seam's unmapped-target refusal, so all three failures stay distinguishable; an unmapped active target keeps the seam's own message and is asserted never to advise `--all`. The whole-registry fan-out survives only under an explicit `--all`, which is a flag and not a value token because `ValidateName` leaves `all`, `current`, and `active` legal resource names that a sentinel would shadow; it is proven byte-identical in and out of tmux with the active-target seam consulted **zero** times, and it is refused next to any selector. Every string the route prints says "in the registry" rather than a bare "all", pinning which scope this `--all` is while projmux has only one. The confirmation carve-out for an exact-one leaf Pane is narrowed by exactly one condition -- the operator has to have named the target -- so the implicit and `--all` deletes always prompt while `delete pane log --project alpha --window main` still does not. Regression is measured as identity, not resemblance: eight explicit argv shapes (positional ref, `uid:` ref, multi-ref fan-out, `--project`/`--window` scope, `--selector` label, an unmatched ref) each run twice against the same fixture, once with the active target pointing at a different resource and once outside tmux, and both runs must produce identical stdout and identical error text with **zero** seam consultations, with `resolve pane: --pane zzz-nonexistent matched no panes, want at least one` pinned verbatim. The declared `<delete, kind>` cells stay 1..N deliberately: cardinality answers how many targets a resolved selector may address, and the containment changes which resources an empty selector resolves in the first place.
142
- - `make test`: Agent provider session binding Phase 1 resume materialization: `agent resume <ref>` stops at a stub no longer and rebinds the existing Agent, building the provider's *resume* argv from `status.sessionRef`, splitting a detached managed Pane on the target Window's `spec.defaultShellPaneRef` through the create routes' own transaction, ledger, rollback, and materializer, and attaching it to the Agent that was already there -- the success test asserts the Agent count is unchanged, the `metadata.uid` and `metadata.name` are the ones that were already stored, `status.phase` is `Running`, `status.paneRef` resolves to a Pane the Agent owns with `role: agent`, the stored conversation pointer is unchanged, exactly one registry transaction commits, the Settings enabled-agents gate ran exactly once, and the one `split-window` argv actually issued carries the stored conversation id, with no client-moving command anywhere. The no-fallback contract is measured as a count rather than as an error string: a twelve-row refusal table -- an Agent with no `sessionRef` at all, a conversation the provider's resume builder rejects, a missing provider binary, a provider disabled in Settings, a `Running` Agent, an Agent that still owns a Pane, a ref contradicting `spec.provider`, a `MissingRoot` Project, a Window with no `spec.defaultShellPaneRef`, an ambiguous reference, and a no-match -- each requires **zero** new Agents, **zero** registry transactions and writes, **zero** `split-window` calls, zero tmux calls of any kind, zero managed-pane bindings, and a byte-identical registry and tmux snapshot; a separate rolled-back-runtime test injects a failing split and requires zero commits, the Agent still `Offline` with no Pane, and exactly one launch attempt which was the resume rather than a fresh start; and a reflection guard requires the resume launch seam not to satisfy the create launch seam, not to expose `PlanAgentLaunch`, and to take the conversation id as a required input, so no fresh-start argv is reachable from the route at all. The four Phase 0 handoff items are adjudicated with tests: the tie-break is that **the conversation is never a selector** -- three Agents sharing one conversation are run across three registry permutations and the referenced Agent is always the one rebound, the siblings never change phase or pointer, and the uid-ordered stderr disclosure and the launched argv are byte-identical every time; `observedAt` is **not** a gate, proven by two refs identical except for an `observedAt` a decade apart producing identical streams and identical argv, and by the stored value surviving the rebind unrewritten; Codex's turn id stays unstored and unimplemented, pinned by a per-provider resume-argv table requiring exactly three elements with no turn slot (`claude --resume <id>`, `codex resume <id>`, `agy --conversation <uuid>`) plus four unusable-id refusals; and an Agent with **no ref at all** fails as a non-usage state error naming `create agent` rather than performing it. A permanent-exclusion guard runs the plan twice with a Claude transcript path pointing at a nonexistent file and at nothing, requiring identical results, so the transcript is proven to be a stored path and never a read. `agent resume` remains a read-only refusal on every ineligible path, the `Running` refusal stays byte-identical with and without a stored ref because the phase gate runs before the ref is read, and the wiring test pins that the route shares the create command's runtime instance rather than a second materializer.
143
- - `make test`: Agent liveness transition Phase 0 a dead managed Pane releases its Agent: when the tmux pane behind an Agent's managed Pane is gone the Agent moves to `Offline` and drops `status.paneRef`, so `agent resume` reaches its normal path without an explicit `projmux delete pane` first. The subject is an inventory diff rather than an event handler and the tests state it as one: `after-kill-pane` fires with an empty `#{hook_pane}` so no hook can name the pane that died, and `#{pane_dead_status}`/`#{pane_dead_signal}` are empty at hook time for a clean exit, a non-zero exit, and a SIGKILL alike -- they populate only under `remain-on-exit on`, which suppresses `pane-exited` entirely and changes pane lifecycle -- so a hook cannot classify a death from tmux at all -- which is why the classification now comes from the durable receipt the supervisor or the control action wrote instead, and only a death with no receipt at all is treated as unproven; the Phase 9 entry below supersedes this paragraph's `AgentExitNormal`-for-everything rule and its `deadAgentPaneReason` spelling. `internal/integrations/metadata` gains `Mirror.LivePaneUIDs`, the mirrored-uid half of the diff, sharing its one `list-panes -a -F '#{@projmux_pane_uid}\037#{pane_id}'` query with `PaneTargetForUID` so the two can never disagree about which Panes still have a transport binding. `internal/app` covers the sweep: the core case asserts `Offline`, a cleared `paneRef`, a deleted managed Pane, the recorded liveness reason, and a `status.sessionRef` preserved pointer-identical, because the conversation pointer is exactly what makes a released Agent resumable; a five-row phase table cross-checks the selection against `CanTransitionAgent` itself rather than against a hardcoded list, so a phase outside the closed table is skipped with its Agent, `paneRef`, and managed Pane all untouched instead of forced to `Offline`, and the table is never widened; and an Agent holding no `paneRef` is never handed a fresh reason or transition timestamp. Cost is measured in transactions rather than in an unchanged registry, because both tmux pane-exit hooks fire on every pane exit in every session: a sweep that finds every mirrored uid live queries tmux exactly once and opens **zero** write transactions, an unreadable inventory releases nothing, reports no error, and opens zero transactions -- fail closed, since an unreadable inventory is indistinguishable from an empty one and treating it as empty would offline every managed Agent on a machine whose tmux server simply is not up -- and a real death opens exactly one. Both triggers are proven end to end: the reconciler path releases a dangling `paneRef` through a real mutation route while leaving a mirrored Agent `Running` on its own Pane, the ordering is read off the recorded tmux argv so the inventory query must follow the last `@projmux_pane_uid` write of the same pass (a freshly imported Pane must never look dead, which is also why there is no one-shot migration -- the first pass resolves every pre-existing dangling ref and the second finds nothing left to do), and the hidden `internal tmux release-dead-agent-panes` route dispatches, releases, opens one transaction, prints nothing, and refuses arguments because it is a sweep and not a per-pane handler -- it is now the widest-legal-event entrypoint into the shared reconciliation below rather than a second implementation. The generated `pane-exited` and `after-kill-pane` hooks are pinned as one shared backgrounded body that runs `rebalance-panes` first under its own `|| true` and then the sweep under its own, so pane layout never depends on whether the registry sweep succeeded and the sweep still runs when there was no layout to rebalance; the theme config byte-identity goldens move with that shared addition. The pre-existing explicit `delete pane` -> `Offline` transition is re-pinned unchanged and still recorded as `deleted`, so the two paths stay distinguishable in `describe agent`. A pane-exit sweep that waits behind a newer create now refreshes the exact mirrored Pane UID inside the Registry transaction before releasing anything, preserving the newer Agent as `Running` with its Agent-owned Pane; a failed locked refresh aborts with zero writes while a true death still commits one release.
144
- - `make test`: Registry runtime observation Phase 0 observed status for Window and Pane: `Window`/`Pane` status is no longer inherited from the owning Project's stored `status.session.live` bool -- a bool written `true` at legacy import, refreshed only by the reconciler, and never seen by the read verbs at all, so a registry object bound to nothing live reported `live` forever. `internal/core/selector` gains `ObservedStatus(missingRoot, bound)` as the single derivation rule every kind goes through, pinned by a four-row truth table plus a resolver table that reads one unchanged registry against three different machines (the alpha project running, one pane of a live window closed, and nothing running at all) and requires the reported status to follow the observation every time; the negative guard flips `status.session.live` both ways against one fixed observation and demands byte-identical Window and Pane statuses, which is the falsifiable form of "zero remaining stored-value paths", and it is repeated at route level over real `get windows`/`get panes` output. `MissingRoot` precedence is re-pinned on all three kinds at once -- Project, plus a Window and a Pane the observation says are demonstrably live -- so the preservation contract still outranks a stale runtime. `internal/integrations/metadata` gains `Mirror.LiveWindowUIDs`, the Window half of the registry-versus-machine diff, sharing its one `list-windows -a` query with `WindowTargetForUID` exactly as `LivePaneUIDs` shares `list-panes -a` with `PaneTargetForUID`; both inventories are proven to drop unclaimed tmux objects, to issue exactly one read command each, to write nothing, and to propagate a query failure rather than flatten it into an empty inventory. `internal/core/metadata` gains `RuntimeObservation`, a `MissingRuntime` condition with reason `RuntimeUnbound` on `Window.status` and `Pane.status`, and `Mutator.ObserveRuntimeBindings`, covered for set/clear symmetry, a preserved `firstObservedAt` across repeat observations, and the preservation contract itself: no resource deleted, no uid or name changed, no name reservation released; `Window.status` is declared `omitzero` and a serialization test proves a condition-free Window still round-trips byte-identically to a pre-observation registry, which is what keeps the addition inside `schemaVersion: 1`. `internal/app` covers the observation seam and the reconciler: the snapshot is taken once per process invocation and memoized (five resolutions, one `list-panes -a` and one `list-windows -a`), a failed query fails to *offline* per inventory half and never back to a stored value, and a nil lookup is the empty observation. The reconciler integration test fires **no hook at all** -- panes and windows simply stop existing on the fake server -- and the next pass records `MissingRuntime` on exactly the vanished uids, keeps every resource queryable, preserves `firstObservedAt` on a repeat pass, and clears the condition when the runtime returns on the same uids; an unreadable inventory records nothing, matching the dead-pane sweep's fail-closed tolerance. The acceptance table drives the real `intmetadata.Mirror` over a fake tmux server through the real `get` route: the reported `live` count is asserted as an inequality against the actual tmux pane count while the server is torn down one pane at a time, closing one pane offlines exactly that Pane on the next query with no hook and no write while its siblings stay live and it stays selectable, and a server that is up but mirrors no `@projmux` uid at all -- the tmux-restart shape, and the exact machine state that produced the defect -- reports zero live Windows and zero live Panes while still listing every one of them.
145
- - `make test`: Registry runtime observation Phase 1 binding reapply and adoption: the `@projmux_window_uid`/`@projmux_pane_uid` options were written once, at legacy-session import time, and never again -- a tmux server restart or an option reset left every live window and pane carrying no uid at all, which is what made `projmux delete pane` with no selector fail with *the active tmux pane carries no `@projmux_pane_uid`* in almost every pane on the measured machine, and the import guard `!result.ProjectReused || len(reg.WindowsOf(projectUID)) == 0` made that permanent by *avoiding* duplicate Windows instead of repairing the drift. `internal/core/metadata` gains `BindingMatcher`, the single structural matching rule: Project scope first, then ordinal alignment (the session's tmux windows in `window_index` order against that Project's registry Windows in creation order, panes the same way inside a matched Window), returning exactly one of rebind/adopt/unmatched/refused. Two table tests, one per kind, pin every decision the rule may make against a two-Project fixture that deliberately carries the Window name `one` and the Pane name `zsh` in both Projects -- so a name-based match would cross and be caught -- and every refusal has its own row: an unresolved session scope, a uid the registry has never heard of (never adopted, reported as its own `AdoptionForeign` outcome so the anchored import path may mint rather than reuse -- projmux itself produces unknown uids, because a reconcile rolled back by a pre-create hook refusal has already written them onto non-transactional tmux options), a uid another Project owns, a Pane of a sibling Window, a uid two live objects both carry, a candidate already bound to a different live tmux object (skipped, never stolen), and a candidate already claimed earlier in the same pass; `paneUIDsInWindowOrder` is pinned to registry insertion order against a Window that mixes shell and managed Panes, because borrowing `snapshotPanesOf`'s shell-then-managed grouping would shear the alignment. `ImportLegacySession` takes a pass-scoped matcher and always walks `legacy.Windows`; `ImportedWindow`/`ImportedPane` gain an `Origin` of created/adopted/rebound, and adopted objects are reported to the adapter for mirroring but never recorded in the transaction ledger, proven by an empty `Created` on a pure-adoption re-import and by a single-entry ledger when an adopted Window still imports one extra Pane it has no candidate for. Import-path negatives cover zero duplicate Windows across two passes with every uid wiped, zero cross-project adoption for two same-shaped sessions, and and a foreign uid that mints a fresh Window instead of re-identifying the unbound one sitting right next to it, while a uid another Project owns produces nothing at all. `internal/integrations/metadata` extends `ObserveLegacySessionTargets` to read the uid each live object already carries (5-field window rows, 9-field pane rows), pinned for a bound window and a blank one in the same session. `internal/app` wires two binding steps against one pre-pass live-uid observation taken through the Phase 0 read seam: the anchored import path, and a new `reapplyUnresolvedBindings` for sessions with no `@projmux_project_path` -- the shape every session on the measured machine had -- which resolves through the Project<->session-name edge `refreshSessionProjections` maintains and creates nothing. The reconciler suite runs the measured fixture directly: one live blank window, a Project reachable only through `status.session.name`, two unbound registry Windows, and a stored `status.session.live` of `false` that nothing is allowed to consult; a **single** pass writes `win-first`/`pan-first` onto the live objects together with `automatic-rename off` and the name mirror, creates and re-identifies nothing, and leaves `MissingRuntime` on exactly the Window and Pane that really are unbound. A second pass leaves both the registry and the whole fake tmux server byte-identical. The refusals are covered at route level too: a session no Project claims and a session two Projects both claim each write nothing, two Projects with two sessions each reach only their own objects, a foreign uid is left exactly as found with none of its panes bound, a Window already bound to another live tmux window is not stolen (the blank window takes the next candidate instead), and a binding write that fails is skipped rather than failing the create that triggered the reconcile.
146
- - `make test`: Registry runtime observation Phase 2 stable Window identity and runtime display projection: the `LegacyWindowNameSeed` table supplies tmux `window_name`, both automatic-rename modes, Pane label, provider, known shell, topic, and raw title and requires every row to return only `FallbackWindowNameBase`; an isolated two-Window import allocates `window`/`window-1`, records the observed `window_name`s as duplicate-allowed `metadata.displayName`, and proves `describe window` renders stable `Name` and runtime `DisplayName` as separate rows. A re-import after an explicit `rename window` keeps every existing uid, `metadata.name`, ownerRef, and name reservation while updating only displayName, and the unanchored binding-reapply fixture repeats that negative guard at the reconciler seam. `internal/integrations/metadata` pins the split transport: `@projmux_window_name` continues to mirror stable metadata.name while tmux `window_name` is driven by displayName, with an empty legacy displayName falling back safely to metadata.name. The explicit resource rename test proves `projmux rename window ... --name` does not change displayName; Pane `pane`/`pane-1` import naming and provider-base Agent naming/linkage/status remain covered by their existing Phase 3/4 tests.
147
- - `make test`: Registry runtime observation Phase 3 orphan live pane import: Phase 1 taught the binding-repair path to *adopt*, and adoption needs an existing registry Pane to adopt into -- so a live pane created outside the Registry had no counterpart, stayed permanently unbound, and kept `projmux delete pane` with no selector refusing with *carries no `@projmux_pane_uid`* in the operator's own active pane; on the measured socket exactly one live pane out of seven carried a binding and the active one was not it. `internal/core/metadata` gains `Mutator.ImportOrphanPane`, which mints one shell-role Pane owned by an already-paired Window through the same `addPaneTx`/`storePaneStatus` primitives the legacy import path uses rather than a second write convention, and its unit tests pin the two fixed decisions as the things that would otherwise go wrong: the name is `FallbackPaneNameBase` uniquified by the registry allocator (`pane`, then `pane-1`) and is asserted to be neither the `claude` command nor the `@projmux_pane_label` that `LegacyPaneNameSeed` would have produced, because `metadata.name` is contractually never derived from a runtime attribute while the runtime reading still lands in `status.displayTitle`; and a pane whose observed provider, topic and command all say `claude` mints **zero** Agents, because deciding registry topology from pane content is the inference direction the track refuses. The cwd table covers the observed value and the fallback to the owning Project root, and an unknown Window is `ErrNotFound` with a `reflect.DeepEqual`-identical registry, which is what lets the caller treat the error as "skip this pane". `internal/app` opens the one gap `reapplySessionBindings` had -- `if !paneMatch.Matched() { continue }` -- by threading the `Mutator` and operation id into the repair path and resolving each pane through `paneBindingFor`. The end-to-end reconciler test registers the orphan, mirrors its uid *and* its name onto the live tmux pane through the existing whole `MirrorPane` write, leaves the paired sibling on `pan-first`, keeps `MissingRuntime` off the pane this pass bound, and then runs a **second** pass that leaves the registry and the whole fake tmux server byte-identical -- the regression that would appear without the `binder.Claim` on what was just minted. The registration rule is a table with a row per outcome: a blank pane with no candidate left is registered, a uid the registry has never heard of is registered (never adopted -- projmux itself produces unknown uids after a rolled-back reconcile writes them onto non-transactional tmux options), while a uid a sibling Window owns, two live panes carrying one Pane's uid, a pane under a Window carrying a foreign uid, a pane under a live window with no registry Window left (the Window layer still creates nothing), a session no Project claims, and a session two Projects both claim all register **zero**; every registration is additionally asserted to land under a Window owned by `prj-alpha`. The cross-project negative runs two Projects, two sessions and an orphan in each against Window and Pane names that collide across them, and requires each registration to sit under its own Project's Window -- the boundary is structural, since the owning Window was paired earlier in the same walk. The diff audit is the preservation contract: a uid/name/owner/spec snapshot of every pre-existing object must come out identical, no name reservation may be released, and the object count may only grow. Tolerance is pinned by injecting a `NewUID` that fails for `KindPane`: reconcile returns nil, nothing is mirrored, and the adoption work that did succeed is kept.
148
- - `make test`: Registry runtime observation Phase 4 agent runtime linkage and observed status: three defects that only look separate. Phase 3 registered every orphan live pane as a Window-owned *shell* Pane whatever it was running, nothing ever wrote `status.paneRef` for a pane it had not itself launched, and `agentMatch` inherited Agent status from the owning Window (`status = windowMatch.Status`) -- so on the measured socket `get panes` printed an empty AGENT column for every row while `get agents` showed two finished conversations as `live` and hid the three that were running, and `claude-1` reported `Phase: Offline` next to `Status: live`. `internal/core/selector` derives Agent status from the Agent's own runtime object -- the managed Pane its `status.paneRef` names -- through the same single `ObservedStatus(missingRoot, bound)` rule, with the owning Window contributing only its Project's `MissingRoot` flag and specifically not its liveness; a five-Agent fixture under one live Window (running with a live pane, running with a dead pane, released, pending, failed) pins the table, an invariance test flips the owner Window's observation with every other input held fixed and requires byte-identical Agent answers (the falsifiable form of "zero inheritance paths", with the Window itself asserted to have moved so the invariance is not vacuous), an exhaustive sweep over all 8 live-pane subsets proves no reachable observation reports `live` for a non-`Running` phase, an empty observation against a stored `status.session.live` of `true` reports every Agent offline, a dangling `paneRef` is offline rather than live, and `MissingRoot` still outranks a live managed pane on every Agent at once. `internal/core/metadata` gains `Mutator.LinkAgentPane`, whose evidence rule is authorship rather than content: `pane_current_command` is never read and `@projmux_ai_agent` -- the option the AI routes write when projmux itself launches an agent -- is the sole marker, which is the same option `bindLegacyPaneTx` already trusted on its create path, so a pane the operator typed `claude` into is refused exactly as Phase 1's rule requires. It resolves an already-Agent-owned Pane to its Agent, otherwise attaches to an Agent of the *same Window* whose `status.sessionRef` records the identical provider conversation as the pane's `@projmux_ai_session_id`/`@projmux_ai_thread_id` (an exact identifier equality with `provider` compared too, so a Codex thread id is never equated with a Claude session id), and otherwise mints one; ambiguity mints rather than guesses because two Agents recording one conversation is legal state, and a candidate already claimed this pass or holding another `paneRef` is not a candidate at all. Eleven unit tests pin every branch plus the preservation contract: the linked Pane keeps its uid and its name while its reservation follows it into the Agent scope with no leftover Window-scoped entry, the Pane count cannot move, an unknown Window or Pane is `ErrNotFound` with a `reflect.DeepEqual`-identical registry, an unrecognized provider spelling still yields an Agent on the `agent` name base, two live panes reporting one conversation cannot claim one Agent, and a second pass rebinds without rewriting anything. `internal/integrations/metadata` reads the two conversation ids on the existing `list-panes` query (11-field pane rows) so linkage costs no extra tmux call. `internal/app` calls the linkage on the Pane `paneBindingFor` just settled on, discarding the error rather than escalating it *or* skipping the binding write that already succeeded; the reconciler suite runs two live agent panes and one plain shell through one pass and requires a populated AGENT column and `Role: agent` on both agent panes, an untouched shell pane, an Agent count equal to the live agent pane count and a `live` Agent count that matches it, a byte-identical registry *and* fake tmux server on the second pass, a four-row authorship table (projmux-launched, hand-typed `claude`, a shell, an unrecognized provider), tolerance under a `NewUID` that fails for `KindAgent` (reconcile returns nil, zero Agents, and the pane keeps the binding and the shell role it already had), an Agent that goes offline on the next read when its pane closes with no hook and no reconcile while staying queryable, and the one visible consequence stated rather than left implicit -- a promoted Pane now joins the managed-Pane lifecycle, so the dead-agent-pane sweep removes that Pane row while the Agent itself survives with the same uid, name, and `sessionRef`, still resumable.
149
- - `make test` / opt-in `PROJMUX_RESOURCEGRAPH_TMUX_SMOKE=1`: Project-wide naming closure Phase 1 replaces stored presentation reads with one typed invocation-scoped `registryview.Context{Value, Source, Observed}` projector. `TestContextProjectorNoTransportPriorityMatrix`, `TestContextProjectorLiveOverlayRequiresExactUIDBinding`, and `TestContextSourcesCannotChangeAddressSelectionReservationOrOwnerRef` pin Project root-basename, ControlSession session, Window anchor topic/provider/command/fallback, Agent topic/provider, and Pane owner-topic/live-title/command priority while proving context changes cannot alter durable names, selected UIDs, reservations, ownerRefs, or Registry bytes. `TestGetPaneAmbiguityUsesOneExactRuntimeContextSnapshotWithoutChangingSelection` gives cardinality diagnostics the same exact live projector before enforcement without a second observation. Human `get` tables use `CONTEXT`, `SOURCE`, `OBSERVED`, and separate stable `NAME`; `describe` uses `Context`, `ContextSource`, and `ContextObserved`. The EN/KO golden and empty-context tables keep terminal-cell alignment and never copy stable name into an absent context. JSON/metadata tests retain the v3 durable bytes/schema, including compatibility fields, and reject ephemeral context. `TestStoredPresentationProductConsumerInventoryIsZero` plus its closed compatibility-site inventory permit only v3 writers, migration, mirror, and divergence convergence to read `metadata.displayName`/`status.displayTitle`; sidebar, recent Window, resume labels, Registry view, tables, descriptions, and ambiguity diagnostics all consume the projector. The isolated real-tmux smoke strips inherited identity, confines two exact sockets below a temporary root, and requires only exact UID-bound primary `window_name`/raw `pane_title` to overlay with `Observed=true` while the sibling remains unchanged.
150
- - `make test` / `make test-e2e`: Columnar view Phase 0 exposes that same invocation-scoped context without making it durable: only Registry-backed plural `get projects|windows|panes|agents -o json` wraps each stored resource with a top-level `context{value,source,observed}`, and all three keys remain present for empty context. This supersedes the prior historical entry's plural-JSON rejection while leaving its Registry-schema and `-o metadata` guarantees intact. `TestGetListJSONCarriesContextFromTheResolutionSnapshot` pins the exact whole List document for all four kinds and compares the stored resource prefix byte-for-byte before the additive sibling; `TestGetListJSONLiveContextRequiresExactUIDBinding` admits `observed=true` only for an exact UID-bound Window/Pane and keeps Project/Agent/unmatched runtime false while requiring one injected snapshot read and zero Registry transactions/writes; `TestGetListJSONContextDoesNotChangeMetadataOrRegistry`, `TestResourceContextJSONIsConfinedToPluralGet`, and `TestGetListJSONEmptyAndReadFailureAreAtomic` freeze metadata/scalar/default/singular/create-fan-out bytes, Registry bytes, the exact empty `items: []` envelope, and zero partial JSON on Registry or stale-UID read failure. L06 `create-materialize` adds the real-tmux witness on its guarded isolated socket: the same Window/Pane are unobserved outside tmux, exact inherited-socket reads project the UID-bound live `window_name`/raw `pane_title`, every context has exactly three keys, and Registry bytes remain unchanged.
151
- - `make test` / `make test-integration` / copied-v3 installed smoke: Project-wide naming closure Phase 2 advances the Registry to schema v4, changes descendant reservations to `(Project|ControlSession root UID, kind, name)`, and gives every automatic Project/ControlSession/Window/Pane/Agent its exact full UID name. `TestRootWideReservationSameDifferentRootAndKindMatrix`, `TestEveryAutomaticallyCreatedResourceKindUsesItsExactFullUID`, `TestAutomaticUIDNameExhaustionIsBoundedAndZeroWrite`, and `TestAutomaticNumericSuffixProducerInventoryIsEmpty` pin the namespace matrix, all five create kinds, bounded 100-remint refusal, and zero semantic/numeric automatic name producers. `TestV3ToV4CanonicalizationUsesDuplicateAllMembersAndDestinationFixedPoint` plus its byte golden covers every duplicate-group member, multi-step destination closure, already-canonical mappings, and preservation of unique names outside the set. Store tests require an exact-byte backup, checksum-bearing content-free field-removal report (including a present-empty key with SHA-256 of empty content), staged validation/fsync failpoints, downgrade refusal, and byte/count-identical repeat migration. Legacy import and v3 snapshot projection use exact UID names and the same root-wide canonicalization; a current-v4 collision is refused before Registry, tmux, provider, trust, or snapshot writes. Explicit multi-target `--name` returns `explicit-name-cardinality` before UID or external writes, rename collisions are total zero-write, and same-root Pane-to-Agent reparent preserves the exact reservation bytes. `metadata.displayName` and persisted `status.displayTitle` are decode-only v3 migration inputs; invocation-scoped `registryview.Context` remains non-identifying.
152
- This v4 row supersedes the automatic short-name, direct-owner reservation, and stored-presentation assertions in the earlier historical Registry observation and selector entries; those entries continue to document the defects and seams that produced the current tests, not the current wire contract.
153
- - `make test-e2e`: Project-wide naming closure Phase 2 automatic-name witnesses against real tmux. L06 `create-materialize` registers alpha with an explicit `--name alpha` and beta with no `--name` at all, then proves the automatic Project name is the exact created uid: beta's uid is read back through the read-only `get projects -o json` projection selected by beta's exact root -- never by listing order, and never by re-issuing the create, whose reuse projection a later Phase owns -- the root-selected uid is confirmed to be the Project bound to that exact root, `describe project uid:<uid> -o name` equals the uid byte-for-byte, and the create result line is `project/<uid> created` rather than any semantic stem; every later beta reference in that scenario is therefore a `uid:` reference, because the automatic name is no longer the word `beta`. L12 `first-open` proves the same for an implicitly registered Project, then separates the two mirrored fields whose v4 values coincide: `@projmux_project_name`, `@projmux_project_uid`, and `@projmux_project_path` are each asserted on the freshly opened `work-gamma` session, and because an automatic name *is* the uid there, two explicit `rename project uid:<uid>` round-trip steps on the inherited absolute socket make the fields diverge -- the name mirror follows every address change while the uid mirror, the path mirror, and the session name stay exactly where they were, with the failure message naming which of the three drifted. L07 `binding-convergence` adds the cross-kind half: an alpha Window and an alpha Pane both named `shared` coexist in the one root and `describe window shared -o uid` / `describe pane shared -o uid` each return their own exact uid. L06 also proves the automatic Agent name is the exact Agent uid -- the payload-free `create codex` shortcut allocates exactly one new `agent-<uid>` identity rather than reusing the provider-named first Agent, a `--` payload reaches the provider and never the name, no numeric suffix exists to append, and an explicit `--name` colliding with the one human-chosen Agent name in the owning root is still exit 2 with zero writes and no new pane. L14 `registry-navigation` keeps its imported shell Pane automatically named and renders it offline, which is covered by the compact-default picker contract below.
154
- - `make test` / `make test-e2e`: Columnar picker Phase 2 supersedes the naming Phase 2 NAME/RESOURCE workaround. `TestPickerCompactFullUIDAndHangulFits80Columns` fixes the compact `KIND NAME STATUS ACTIONS` first frame inside 75 cells with full UID names, Hangul context and real action lists; only repeated provider/phase and role display adornments move to wide. `TestPickerCompactNativeFramePreserves76CellRegression` keeps the prior 66-cell fixture and the observed 76-cell clipping regression, proves the latter clips under the real renderer pipeline, then checks every full compact field after picker-local one-cell spacing (63/73 cells). `TestPickerWideNativeFrameViewportMatrix` runs the same native frame/scrollbar/pointer pipeline at 80/120/180/184 columns: the historical 142-cell row clips through 180 and is complete at 184; wide retains its two-cell gap. `TestPickerWideRetainsUnboundedValuesAndDetailRecovery` checks full NAME/RESOURCE/REASON/raw-target values, per-field row detail recovery and Runtime CLI semantic parity. `TestPickerProfilesUseExactCatalogFieldOrder` pins both catalog projections. `TestColumnPickerProfileSettingsAndNativeStateParity` covers the two canonical catalog actions, default Alt-W and a plain single-key Settings override, effective footer keys, query/selected-value restoration, compact-wide-compact transitions, reopen reset and zero keymap writes. `TestColumnPickerToggleDoesNotObserveOrWriteAgain` exercises both command loops, including empty filtered results, and proves unchanged row cardinality/order/search plus no extra tmux observation or runtime writes. L13 and L14 drive both toggles through attached real tmux clients and retain exact popup-process completion and zero-Registry-write checks. Installed isolated 80x24/120x30/180x40 plus 184-column smoke observes compact visibility, expected narrow wide clipping and detail/JSON recovery. The historical 142-cell Registry and 120-cell Runtime rows are representative measurements, not arbitrary-data upper bounds; current actions and free text may widen them. The former picker truncation helpers and all NAME/RESOURCE/RUNTIME/REASON bounds are removed; the source PROGRESS policy, footer rendering, provider vocabulary, CLI/JSON/scalar projections and identity authority are unchanged. [Column profiles](column-profiles.md) describes the open-lifetime control and recovery contract.
155
- - `make test` / `make test-integration` / `make test-e2e`: Default resource AGE Phase 0 extends only the four Registry CLI compact lists to `NAME STATUS ACTIONS AGE`. `TestGetListDefaultAgeMatchesWideAtFixedClock` compares default/wide cells on the same row and injected clock for every kind, covering second/minute/hour/day boundaries, long ages, future creation stamps, missing `createdAt`, and repeated reads. `TestResourceTableDefaultAgeUnavailableIsEmpty` keeps absent resources and absent clocks as empty AGE cells in both profiles. `TestGetListDefaultProjectionIsColumnar`, `TestResourceTableColumnsAreTheCanonicalContract`, and `TestGetReadFamilyResolvesEveryKindWithListCardinality` pin the default table/header contract. `TestColumnProfilesDoNotSelectFromWidthAndWideIsUnbounded` retains AGE at COLUMNS 40/80/112/120 with byte-identical default output; resource CLI tables retain content width and can wrap on narrow terminals. `TestColumnProfilesExactOutputAndSnapshotParity` changes only four compact golden sections and freezes wide, JSON, metadata, scalar and runtime output; `TestColumnCatalogCompletenessBijectionAndExactProfiles` preserves the other three surfaces. Existing empty-list and scoped-row guards remain active. Integration and E2E compact recovery assertions accept the fourth AGE column. AGE still uses the existing `resourceAgeCell` and `i18n.FormatDuration` calculation.
156
- - `make test` / `make test-integration` / `make test-e2e`: Route-implied kind omission changes only the four Registry CLI compact lists to exact `NAME STATUS ACTIONS`. `TestColumnCatalogCompletenessBijectionAndExactProfiles` and `TestGetListDefaultProjectionIsColumnar` reject compact KIND reinsertion and wide/mixed KIND removal. `TestCompactResourceKindOmissionPreservesScopedRowsAndRecovery` covers all four routes with unscoped, all-projects, Project, Window and exact-Pane selectors where supported, including empty results, ordered UID sets, full NAME/STATUS/ACTIONS parity and wide/JSON kind recovery. Each fresh read runs `assertColumnReadCounters` (one Registry load, four existing tmux queries, zero Registry transactions/writes and zero sibling calls). `TestColumnProfilesExactOutputAndSnapshotParity` keeps the wide/JSON/metadata/scalar/runtime byte goldens and ACTIONS snapshot fixed; only four compact golden sections change. `TestMixedPickerProfilesRetainKindCells` verifies every mixed Registry/Runtime row retains KIND in both profiles; the existing toggle, query/selection, 80-column and detail-recovery guards remain active. Integration checks the restored Window's compact/wide/JSON cells; L06 repeats four-kind nonempty/empty default/wide/JSON recovery on real tmux and compares Registry bytes. This stdout change moves NAME, STATUS and ACTIONS one position left and can break positional parsers; wide and JSON retain kind.
157
- - `make test`: Resource timestamps surfacing Phase 0 timestamps in read surfaces: the registry has always stored `metadata.createdAt` on every resource, `Condition.firstObservedAt`/`lastTransitionAt` on every observed condition, and `Agent.status.lastTransitionAt` on every Agent, and `-o json` has always emitted them -- but no human-facing surface printed a single one of them, so `describe pane` carried zero time rows and the columnar `get` carried no `AGE`. Both now render, and nothing new is collected: no tmux query, no schema field, and no second relative-time implementation. The plural reads gain an `AGE` column, last on all four kinds because that is the one column whose width moves with the clock and last is the only position where growing it cannot walk an owner-chain column sideways; it is rendered through `i18n.FormatDuration`'s compact form, the same single implementation `notify list` already renders its own AGE through. `internal/app` pins the arithmetic as a boundary table asserted from both sides of every unit rollover (`59s`/`1m`, `59m`/`1h`, `23h`/`1d`, up to `900d`), plus the three cases that render an empty cell rather than a number -- a uid that has left the registry, a resource stored before `createdAt` was stamped whose zero Time would otherwise date it to year 1, and a caller that passed no clock -- and a whole-table golden spanning `9s`/`47m`/`6h`/`11d` in one read, which is also the alignment case because the widest cell sets the column. Determinism is stated as a two-sided claim rather than a single assertion, since only the pair is falsifiable: the same injected clock twice must be byte-identical (which a renderer ignoring the clock would also satisfy) **and** a clock advanced three days must produce a different table (which a renderer reading `time.Now` would fail), so only a renderer reading the passed-in clock and nothing else survives both. The clock reaches exactly one projection: `writeResourceProjection` takes it as an explicit parameter and the singular call sites (`get pane`, `describe`, `rename`, and the create fan-out, whose default projection is the `<kind>/<name> created` line and never the table) pass the zero Time, which `resourceAgeCell` renders as no age at all rather than as "created exactly now". `describe` gains absolute UTC instants instead: a `CreatedAt` row beside the identity rows on all four kinds, `PhaseSince` under an Agent's phase, and the second half of a condition's stored pair (`lastTransitionAt` beside the `firstObservedAt` that was already there), each omitted when the registry file predates the field so a pre-`createdAt` registry keeps its exact current block. The timestamp machine-consumer contract is frozen the whole way: `-o uid|name|ref|none|metadata` remain byte-identical, and the durable portion of plural `-o json` remains exact around the later Columnar Phase 0 context sibling -- both current whole documents are pinned rather than substring-matched, so a timestamp key anywhere still reddens. The Columnar track's `describe pane` byte-golden moves by exactly one row and keeps its own purpose intact: what it guards is that the columnar table is reachable only with `list` true, and a header line or a padded column in a singular route still reddens it.
158
- - `make test`: Settings IA re-cut Phase 0 full navigation cutover: the visible Settings tree is declared once in `internal/app/settings_nav.go` and frozen by `internal/app/testdata/settings-nav-tree.golden`, so an IA change is a golden diff rather than a shape spread across the section loops. Structural tests prove the catalog is a tree (unique ids, resolvable parents, a scope root at the top of every path), that every control hangs off exactly one owning `[View]` and that only a View owns children (row-affordance exclusivity), and that each rendered picker value maps onto exactly one navigation node whose owning loop the closed reachability table actually handles (control-owner cardinality); the drift guard runs in both directions, so a section loop cannot grow a row the golden does not show. The removed-path negative guard proves `Labs`, the separate `Theme` root, `Project recipe` and `Effective merge view` are neither rendered, nor catalogued, nor routable through `runSection` — the retired handlers, action metadata, and i18n entries are absent rather than hidden redirects — and the legacy-copy guard rejects the retired vocabulary (`AI Settings`, `Project Picker`, `Project Root`, `Workdirs`, `Enabled agents`, `Default split mode`, `Resume picker`, `Session State`, `Kill Session`, `New window`, `Hook quiet policy`, `Delivery sources`, `Labs`, `Usage HUD`, `Path icon`, `Notify icon`, …) across navigation labels, rendered rows and keymap display labels, in both locales. Display renaming is proven display-only: the keymap action IDs (`new-window`, `Sidebar:KillSession`, `ai-split-right`, …), the `sessionstate:`/`project-hooks:`/`live-resources:`/`statusbar-decoration:`/`workdir:`/`projdir:`/`switch:`/`keymap:`/`theme:` action spellings and their owner contracts are all unchanged behind the renamed rows. Keybinding navigation is category-based: an exhaustiveness test requires every catalog action to carry exactly one explicit category (never an ID-prefix inference), every surface action to sit in the sidebar/picker category with a display label for its surface, one display label per action, and each action to render exactly once across the rendered categories, while the category rows carry their members' action IDs, labels, chords and state so search still crosses the category boundary. The action detail projects the product semantics the target IA asks for — target kind, result kind, placement and anchor — and pins that every interactive right/down split declares the explicit current-Pane anchor and a right/down placement, that `Create <Provider> Agent` is always-new, and that resume names the existing `Offline`/`Failed` Agent it rebinds. Navigation is proven side-effect free (opening the root, every section and every container writes no config and runs no tmux command), every `[State]` row carries the catalogued passive value so Enter can never fall through, and the promoted rows keep their old state at the new route (Resources reads the same `live-resources` file it read from Labs, the project automation policy the same `project-hooks` file, Theme the same global `[theme]` block through `Appearance > Theme`). The pinned Project item view keeps Project identity separable (display name, unique name, uid, bound root, condition, missing-since, runtime) and offers same-uid remediation through the canonical `rebind project` route instead of re-pinning, with the unregistered-path case rendering a disabled rebind row carrying its reason. The Agent Usage HUD ownership guard keeps `agent usage` canonical: no Settings row spells a `Usage` resource or a `get usage` read, and `docs/settings-ia.md` names the owner.
159
- - `make test` also covers Settings directional hierarchy parity: every catalogued navigation/actionable/passive/Back kind has a Right/Left outcome; Right opens only forward-navigation rows and never executes mutation, passive, or Back rows; child and grandchild Left return exactly one View while root Left stays in place; Esc and the Settings popup-toggle close action remain available at root and every visited depth; en-US/ko-KR frames advertise the arrows; and typed-query, color-grid, scope-chip/chord, and key-recorder input retain their existing native semantics. A native root → child → grandchild → back → root interaction and a config-tree mutation-negative audit pin the shared policy without changing global picker arrows.
160
- - `make test`: Settings IA re-cut Phase 1 Automation and Notification ownership: the two containers are proven to be different product concepts by their labels alone. Automation names only Projmux-owned user scripts and the policy that gates them (`Projmux session lifecycle`, `After notification queued`, `Project automation policy`) and never a Provider or an integration; Notifications names only delivery and projection and never a lifecycle script or a `[hooks.*]` key. The Provider inventory is pinned to the Claude/Codex/Antigravity enum: the tmux bell producer is filtered out of Provider Integrations by producer identity and rendered flat at its own `tmux event source` View, whose wiring row says in words that the bell is not an Agent Provider. `Agent event behavior` renders the same Provider enum, so a Provider can never be wireable in one destination and missing from the other, and a provider x event matrix walks every event to its four-way `default`/`notify`/`state`/`quiet` choice and asserts each choice is addressed to the provider and event that rendered it. The two Provider destinations are proven disjoint: install/remove/conflict/config-path/`Check integration` rows exist only in the wiring detail and the `ai-hook-set:` choices only in the behavior view, with neither surface rendering the other's action prefix. `Check integration` (and `Check` for the bell) is an observable read-only action rather than a placeholder: it re-reads the shipped doctor diagnostic, reports status/config path/conflict as Settings feedback, shells out to nothing, leaves the config tree byte-identical, and errors on an unknown id. The `[hooks.send-noti]` + `PROJMUX_NOTIFY_HOOK` parity test sets both at once and proves they are a supported combination rather than an either/or: each keeps its own resolved value, the desktop row disclaims the fan-out and the Automation row disclaims the sender, neither row renders the other's value, and the three session lifecycle events stay empty. An automation event x scope matrix walks `pre-create`/`post-create`/`post-attach`/`send-noti` in both scopes, keeps the technical `[hooks.<event>]` spelling in the state row, requires the project scope to name its missing-context reason instead of falling through to a global value, and pins `send-noti` as a sibling of the lifecycle list rather than a fourth member of it. The attention guard keeps live Pane badges out of every Notifications surface while the badge style keeps its Appearance home, and the retired `Hook quiet policy` vocabulary is gone from the shared UI text catalog and from the rendered rows in both locales while staying on the negative-guard list.
161
- - `make test`: interaction-first Keybindings details keep action state, current single keys/sequences, and add/manage/remove/reset/test actions ahead of passive explanation. `TestSettingsKeybindingActionDetailMatrix` verifies semantic/handler/`internal/cli` manifest parity directly against the internal models while separately proving every rendered selectable row is handled; `TestSettingsKeybindingActionDetailPrioritizesStateBindingsAndActions` freezes the default/custom/unbound row order and mutation reachability; `TestSettingsKeybindingKeyDetailRowsAreAllHandled` keeps replace/remove/test routes and requires an unavailable test row to carry both reason and next step; and `TestSettingsKeybindingRetiredContainerCopyIsGone` renders Action/Key/Sequence surfaces in en-US and ko-KR and rejects visible Target/Result/Placement/Anchor/Handler, canonical storage/delivery, cancellation teaching, and Advanced/Details/Delivery/About-style containers. Delivery outcome, one-reader, unavailable/cancel/no-write, capture/typed normalization, feedback-stage recovery, protected-action, reserved-key, sequence storage/runtime, and distinct create/resume parity tests remain in the maintained suite.
162
- - `make test-e2e`: Settings IA re-cut Phase 0 native navigation smoke against the real attached tmux client: a deep search from the Settings root walks `Appearance > Status Bar > Git > Icon` — proving search lands on the owning View rather than executing a control, and that reaching the icon chooser writes no decoration file — then returns to the root through the Back rows, and the keybinding recorder flow now reaches its action through the `Launch & popups` category, searching for the action's own name at the root to prove search crosses the category boundary.
163
- - `make test`: lifecycle hook context coverage verifies that the production client supplies `projmux` socket metadata, standard persistent and ephemeral `post-create` receive the exact non-empty pane id returned by session creation, `pre-create` receives no pane id before creation, and empty socket/pane values are omitted from the hook environment.
164
- - `make test`: atomic project and private-global config writes preserve existing regular-file metadata, resolve valid relative and multi-hop symlinks to the final target without replacing link inodes, retain `0644`/`0600` defaults for missing destinations, recover dangling symlinks as regular files, and preserve original content with no temp artifacts across resolve/stat/create/write/close/chown/chmod/rename failures. Source-level global and Settings project writer tests exercise the production entrypoints with isolated home/repository paths.
165
- - `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.
166
- - `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.
167
- - `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.
168
- - `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.
169
- - `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.
170
- - `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.
171
- - `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.
172
- - `make test` also covers usage staleness at the statusbar width: a width-parameterized table over the budgets the generated tmux config can hand the renderer (100/120/140/160, the share a 120/140/160-191/240-column client leaves after the notify reservation) x fresh/stale(>10m)/very-stale(>1h) pins that a stale provider carries a marker at every one of those widths, that width pressure can no longer evict the `~`/`~~` staleness signal together with the purely cosmetic level-0 `(3m)` indicator, and that the three states stay mutually distinguishable at each width; one usage-budget table golden freezes the exact bytes (color escapes included) at 40/60/80/120/160 for all three levels and for both the three-provider fixture and the shape an actual install renders, its 120-cell rows are byte-identical to the `status-usage-w120-*.golden` files it replaced, and the two narrow budgets get their own test that pins the marker as present at 60 and the `~~`/`~` collapse under hard truncation at 40 as a ladder limitation rather than a budget one; and a fresh-state parity golden generated by the pre-change tree (98ec3b1) across widths 0/40/50/60/80/100/120/160/200 proves a healthy install is repainted at no width, backed by a direct assertion that the stale-only tiers collapse byte-exactly onto their neighbours when nothing is stale. The plain `~`/`~~` marker the colorless text tiers now emit is asserted colorless and confined to providers that opted into the age signal.
173
- - `make test` also covers the statusbar row-0 width budgets: the generated tmux config derives both `--max-width` arguments on `status-format[0]` from `#{client_width}` instead of the former hardcoded 80/120, splitting the row as notify=min(client_width/2, clamp(client_width-140, 20, 80)) and usage=client_width-notify, reserving notify's measured 20-cell floor rather than its 80-cell design cap so a 191-column client gets 140 usage cells instead of the 111 PR #624 left it, so a miniature tmux-format interpreter — modelled on measured tmux 3.6 behaviour, including that a bare `#{<:a,b}` is a string comparison and only `#{e|<:a,b}` is numeric — evaluates the emitted formats against the widths measured on a real server, and pins that the two budgets sum to exactly the row at every width from 2 to 400, that neither segment is ever budgeted to zero (which `--max-width` reads as unbounded), that notify keeps exactly its historical 80 on every row at least 220 wide and is never squeezed below its 20-cell reservation on a row that can host it, that the usage budget is monotone in the client width and reaches the old 120 constant already on a 140-column terminal, that it never comes out below min(client_width-20, 120) at any width (the corrective regression guard PR #624 failed), and that it clears the measured 124 cells Claude's weekly bar needs at the reported 191-column width and first does so at 144, that every nested conditional uses the fail-safe numeric `e|<` form and the innermost false branch is the literal 80 so an unevaluable comparison degrades to the historical reservation, and that neither budget is a literal cell count; a product-shape usage fixture pins that the old 120-cell budget dropped Claude's weekly bar and that the second-bar tier returns at 124 cells with the staleness marker intact; and the generated-config digest goldens for the fallback theme and every preset move together with the one intended line.
174
- - `make test` also covers the usage segment's element-priority degradation: `usageShedOrder` in `internal/app/usagecmd/usage.go` is the single definition of the drop order (cosmetic age text, then stale age text with the `~`/`~~` marker kept, then a provider's secondary window bar, then bars segment-wide, then long labels), per-provider rules run tail-first over the canonical provider order, and `docs/statusbar.md`'s `Usage element drop order` section is read by the test rather than duplicated so the two cannot drift; a width-sweep table pins the exact element set every fixture renders at 40/60/80/100/120/160/200 for three shapes — the installed shape, plus two shapes where more than one provider is eligible for the per-provider rules, which is the only way to tell element-priority degradation apart from the whole-segment `first tier that fits` ladder it replaced (at 120 cells the dual-window shape now renders 118 cells keeping Claude's second window and shedding only Codex's, where whole-segment selection drew 92); the ordering guarantees are asserted as width relations derived from the renderer rather than at hand-picked widths, so an age indicator is always shed at a strictly wider row than any official window bar and the `~`/`~~` marker outlives every official window bar; every reachable shed state is walked directly to prove no step removes a provider, its official window (5h, otherwise weekly), or its staleness marker, and that each step strictly narrows the segment; and a wide-width parity golden generated by the pre-change tree (`main` 6dfed76) plus the unchanged `status-usage-budgets.golden`, `status-usage-fresh-widths.golden` and the three staleness goldens prove a row wide enough to shed nothing is byte-identical to what whole-segment selection produced.
175
- - `make test` also covers Settings > AI Settings > Enabled agents Phase 0 config behavior: missing config enables Claude, Codex, and Antigravity, unknown saved provider names are ignored without disabling known providers, Settings toggles persist Claude/Codex/Antigravity only, shell/selective are absent from enabled-agent rows, and a saved default split mode warns when its provider is disabled.
176
- - `make test` also covers the Settings information-density/feedback contract: compact About producers and removed static-guide negative guards, canonical `setup` / `setup terminal` / read-only `doctor` destinations, single-source no-project guidance with project-context action preservation, Appearance/Theme/About ownership copy, and popup-visible passive mutation success/failure/validation feedback with replacement/clear and rendered-entry reachability guards. Dedicated Welcome, key-capture/probe/diagnostic, and read-only viewer outputs remain outside generic mutation feedback.
177
- - `make test` also covers native Alt-1 sidebar `Sidebar:KillSession` mutable refresh: the action kills through the focused-session safety path, keeps the native picker session open, refreshes rows and preview with `picker.Action.Mutate`/`DeferredUpdate`, preserves the previous-live-session guard, and invalidates the private origin Pane only when Ctrl-X stopped that exact origin session so a later Continue rebinds to the still-live client's Pane. Linux L11 drives that popup-receipt cross-transition on a dedicated pseudo-TTY (`Open fresh → Ctrl-X → Continue project`) and requires the same fresh Project/Window/Pane UID graph plus final client handoff.
178
- - `make test` also covers native Alt-1 sidebar project-open failure propagation: trust denial remains a normal picker outcome, while topology/materialization failures reopen the sidebar with the prior query, selection, and bounded error status before returning the operational error to the command journal and caller; the detached continuation preserves the popup launcher's exact private Pane anchor for runtime-route authorization, then its `tmux run-shell` wrapper absorbs only the already-recorded exit status so tmux cannot replace the picker feedback with the raw quoted continuation command.
179
- - `make test` also covers Notify sidebar pane-grouped read-model rows and interactions: collapsed pane inbox group construction, fixed three-line group cards with stable project/provider, topic/context, aggregate metadata, and latest-preview slots including topic-with-project cases, child-count badges that match expandable child rows without rendering `+0`, childless group headers without strong fold affordance, selected/expanded marker-only row shape changes, duplicate label/preview reduction, worst-severity aggregation, inactive/gone group display and child-count rendering, pane-less fallback grouping, compact newest-first child event rows with warn/critical severity preservation, Right/Left fold mutation plus childless Right no-op refresh, folded and expanded live group Enter focus+ack with mixed-severity critical rows, inactive-but-routable group Enter focus+ack, gone group Enter cleanup without focus including critical rows, target-gone race cleanup, transient focus-failure no-ack refresh, child Enter focus/ack preservation, explicit group acknowledgement including mixed-severity critical rows, and grouped deferred refresh output with expanded-state pruning.
180
- - `make test` also covers the installed-surface coherence contract: localized Session State summaries and Project Picker Back/open guidance; single-label Resource search plus disabled empty shared-bucket feedback; en-US/ko-KR Sessions row/preview labels with the concise open/close footer visible in actual 80x24 projmux and 112x30 daylight native frames; Notify empty read-only close-only chrome; and INFO/WARN/CRIT Clear All confirmation with Esc/Cancel no-write and explicit confirm feedback.
181
- - `make test` also covers the AI provider metadata registry for Claude/Codex/Antigravity launch providers: registry-derived enabled-agent defaults and ordering, Settings enabled-agent rows, AI picker visibility for disabled providers, ambient usage model scope derivation for currently registered adapters, hook diagnostic provider metadata, Antigravity session-state support metadata, and explicit diagnostics for disabled providers.
182
- - `make test` also covers the Antigravity v1.1.12 hook contract: authoritative CLI `--event` selection with legacy payload fallback, sanitized camelCase common/invocation/post-tool/Stop fixtures, empty/absent/populated `workspacePaths` handling with inherited-pane-first attribution, explicit-error-centric Stop severity including unknown-reason diagnostics, the official five-event catalog with disabled permission-changing `PreToolUse`, and valid non-permission-changing hook response JSON.
183
- - `make test` also covers the Antigravity v1.1.12 statusline bridge: official stacked managed-object golden, settings install/reinstall/remove byte and mode preservation, custom-command conflict and malformed/symlink/permission preflight, empty custom stdout, official snake_case identity/agent/confirmation/context-token/quota fixture parsing, busy/quiet state mapping including Stop → late busy → idle completion preservation and next-PreInvocation reset, stable approval dedupe identity, structured conversation-local context sidecar writes with the legacy string-percent fallback retained as private hook/notify diagnostics, opaque quota bucket ordering/identity and collision guards, remaining-fraction validation/conversion, independent absolute/relative reset persistence, and context-only/empty/null/disabled/malformed quota degradation without context usage snapshots.
184
- - `make test` also covers usage HUD/all-model filtering from Settings > AI Settings > Enabled agents: ambient `status usage` and `usage --model all` scope collection/rendering to enabled Claude/Codex/Antigravity providers, all-disabled all-model output shows a Settings fallback without refreshing adapters, disabled cached rows/backoff do not leak into ambient output, exact fixed-window versus lossless named-quota filtering, official-window status projection with exact Antigravity `gemini-weekly` aliasing and unknown quota exclusion, weekly-only provider preservation through long/primary/text tiers, legacy context suppression in text/JSON/popup, data-driven popup absolute-count columns without percent-derived counts, Claude typed `limits[]` aggregate-only/aggregate-plus-model/null/unknown/malformed parser tables with nullable scope/model ID/surface and bounded injection-safe labels, exact named identity/reset/freshness store/text/JSON/popup round trips, aggregate-only replacement plus malformed/backoff last-known-good preservation, Claude named-row status exclusion, and explicit `usage --model claude|codex|antigravity` still collects/renders the requested provider even when disabled.
185
- - `make test` also covers Codex rollout rate-limit window classification by `window_minutes`: old-schema primary 5-hour plus secondary weekly snapshots, new-schema primary weekly plus null secondary behavior, unknown-window skipping, and golden snapshot output for both rollout schemas.
186
- - `make test` also covers AI semantic badge renderer aggregation for window-list, sidebar rows, switch window tabs, popup pane summaries, and pane-border topic badges using prompt-required > response-complete > in-progress priority while preserving blank no-state lanes, response-complete live badge consume on attention clear without idling action-required/progress badges, plus Settings > Appearance AI badge style selection for dot/emoji/off pane-border, Alt-1 sidebar window/preview tabs, and tmux window-status compatibility with dot as the persisted/config fallback.
187
- - `make test` also covers AI semantic badge Phase 3 theme-role hardening: `@projmux_ai_badge_kind` storage values and aggregate priority stay unchanged, `minimal` remains a read-time alias for the `off` badge style, progress/success/action-required badge roles stay distinct, permission/input status badges do not use critical red, and critical notify queue severity does not drive live AI status badge colors or desktop notification urgency.
188
- - `make test` also covers keybinding surface tier catalog rules, action-centered `keymap.toml` `keys = [...]` multi-alias parsing/writing, quoted internal `Surface:Action` tables, legacy popup action ID aliases, canonical popup toggle names, AI split popup-vs-direct action labels, generated tmux config multi-alias rendering, global/direct conflict detection, surface-scoped native picker command conflict detection, Settings Keybindings list/search/detail surface-aware picker-local labels, compact action-list state copy, flat single-key/sequence action detail rows, key detail remove/test/replace rows, one visible Add binding flow, and one typed binding surface; the unified native recorder's 0–4 stroke reducer, length-driven single-versus-sequence save, empty Enter/Backspace no-op, last-stroke pop, Esc zero-mutation cancellation, four-stroke cap, continuous native byte-stream comma draft rendering with canonical space-form results, conflict retry, and one-reader/no-event-leak lifecycle; capture/typed comma-or-legacy-space normalization and byte-identical save/apply output, atomic single↔sequence replacement, comma list/detail/diagnostic display with space route/disk/runtime parity, and negative guards against the retired Add sequence, Record next stroke, selectable stroke-remove, and Sequence Editor authoring routes; the reserved Enter/Escape/Tab/Backspace/Delete/navigation base-key classifier across tmux aliases and modifiers, identical capture/typed/final-save rejection before schema/file/config/live-tmux writes, later-printable and first-stroke/raw/conflict parity, shipped PlainChord/PlainChords/PrefixChord/Sequences protected-action inventory, reasoned whole-editor read-only rendering, forged dispatch/direct-helper no-write, retained delivery tests/navigation, and existing schema-v2/default/runtime parse-render-bind parity; collapsed Troubleshooting entry copy, Settings delivery diagnostics that split logical key/raw bytes/tmux received key while distinguishing missing, ambiguous, adapter-needed, and delivered states, safe direct key pool and risky/reserved diagnostic-only copy, Projmux-owned Ghostty/Windows Terminal advanced delivery copy, raw escape/CSI-u/xterm modified/UserKey/UserSequence rejection from primary keymap storage, Labs compatibility redirect to the Keybindings root, existing macOS capture/add-key flows, unsafe raw capture, reset behavior, stale guide docs guards, and welcome/runtime footer copy that avoids hardcoded launch-key guides.
189
- - `make test` also covers known retired live no-prefix cleanup for the old `C-t` pane-label binding: cleanup renders before current binds, explicit current `C-t` reassignment wins, repeated apply is byte-idempotent, and unrelated bindings remain unchanged. `TestTmuxApplyRetiresStalePaneLabelChordAndMigratesKeymapOnce` additionally pins that the first apply migrates the keymap to `schema_version = 2` and the second writes no bytes and leaves exactly one backup.
190
- - `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.
191
- - `make test` / `make test-integration` / `make test-e2e`: Orphaned Registry Window Phase 2 managed close keys route the app config's `prefix x` and `prefix &` through canonical delete. `TestManagedDeleteKeyBindingsRenderMirrorGuardedCanonicalRouteWithStockFallback` pins the app-scoped `pane.delete`/`window.delete` catalog actions and their exact `if-shell -F "#{@projmux_pane_uid}"`/`"#{@projmux_window_uid}"` bindings: the mirrored branch is only `internal tmux delete-confirm`, the else branch is tmux 3.6's stock `confirm-before` body byte for byte, and the standalone snippet keeps both keys stock. `TestManagedDeleteKeymapChangeAndDisableReachGeneratedConfig` moves and disables them through `keymap.toml` `prefix`, adds a root key through `keys`, rejects two managed actions on one prefix key, and proves a vacated stock key is unbound and then re-bound to tmux's stock body before any managed bind. `TestManagedDeleteCatalogSurfaceRowsMapBothDirections` ties `catalog.pane.delete`/`catalog.window.delete` to `mutationKillPane`/`mutationKillWindow` in both directions. `TestTmuxDeleteConfirmIssuesOneLocalizedPromptToTheExactClient` issues exactly one en-US/ko-KR `confirm-before -b -t <client>` whose command is the Pane menu Kill route or `internal tmux window-delete` with the press-time anchor, performs zero deletes before the answer, and resolves to one guarded route and one runtime ledger row; `TestTmuxManagedDeleteIntentsRefuseMalformedArgvWithZeroTmuxCalls`, `TestTmuxWindowDeleteIntentReportsCanonicalResultToTheExactClient`, and `TestTmuxWindowDeleteIntentRefusalIsShownWithoutRawFallback` cover the canonical Window intent. `TestDefaultCatalogManagedDeleteConfirmParity` holds en/ko parity for the Registry deletion and the Agent-does-not-return-on-Continue impact. `TestGeneratedWindowLifecycleActionsReachTypedHandlersWithoutRawManagedVerbs`, `TestGeneratedCatalogMutationAndNavigationArtifactsHaveOneSurfaceRow`, and `TestFullRenderedTmuxConfigsHaveClosedGeneratedMutationSurfaces` now admit `kill-pane`/`kill-window` only as those exact stock bodies, counted per rendered config including a close-key override config. The integration smoke reads both applied `list-keys -T prefix` bindings. The e2e pane-menu leg presses `C-b x`/`C-b &` with `send-keys -K` on the attached client for a managed Pane, an unmanaged Pane, a managed Window, a stale-mirror Window refusal, and the Project's last Window. Raw-kill `killed` retention, Continue, and teardown tests are unchanged.
192
- - `make test` / `make test-integration` / `make test-e2e`: Orphaned Registry Window Phase 3 routes tmux's stock menu Kill entries for managed resources through canonical delete. The app config unbinds and replaces tmux's stock `prefix <`, `MouseDown3Status`, `M-MouseDown3Status` Window menus and `prefix >`, `M-MouseDown3Pane` Pane menus; the standalone snippet keeps all five stock. `TestManagedStockMenusRenderInAppConfigOnlyAfterUnbindingTmuxStock` pins that scope, unbind-before-bind, the unchanged `MouseDown3Pane` in both configs, and that a keymap-assigned `prefix <` still wins. `TestManagedStockMenusKeepTmux36ItemsExceptManagedRoutes` parses tmux 3.6's stock menus spelled as literals and requires every generated item to equal them except Kill (`if-shell -F "#{@projmux_window_uid}"` to `internal tmux window-delete` with no `delete-confirm`, or `"#{@projmux_pane_uid}"` to the Pane menu Kill route, each with tmux's stock `kill-window`/`kill-pane` as the else branch), Rename (`internal tmux window-rename`), New At End (`internal tmux window-create`), Horizontal/Vertical Split (`internal tmux pane-menu split-right|split-down`), and the removed Respawn and New After. `TestManagedMenuKillCarriesTmuxKillOnlyAsTheMirrorAbsentBranch` holds all six Kill items, including `MouseDown3Pane`, whose unmanaged Kill now runs tmux's kill instead of refusing. `TestManagedWindowMenuRunsTheCatalogCreateAndRenameCommands` ties New At End and Rename to the catalog renderer and proves the Rename prompt template is `##`-escaped: measured on tmux 3.6, a menu item is format-expanded when the menu opens, and an expanded `%N` Pane handle in a `command-prompt` template is a response placeholder. `TestGeneratedPaneMenuArtifactsHaveOneClosedSurfaceRow` maps every item of all six menus to exactly one `window-menu.*`/`pane-menu.*` surface row and back, admits `swap-window`/`swap-pane`/`resize-pane` only as the exact stock presentation items and `kill-window`/`kill-pane` only as the Kill else branch; `TestFullRenderedTmuxConfigsHaveClosedGeneratedMutationSurfaces` counts those per rendered config. `TestGeneratedPaneMenuGolden`, `TestGeneratedPaneMenuOmitsRawTmuxMutationVerbsAndRespawn`, `TestTmuxPrintConfigBindsPaneContextMenu`, and the `TestBrightPhase2GeneratedConfigByteIdentity` digests change only for the Kill guard and the new menus. The run-shell ledger adds a `window-menu` surface. The integration smoke reads the applied `list-keys` of the five menus and `MouseDown3Pane`. The e2e pane-menu leg drives the real menus on the attached client with `send-keys -K` and SGR right-clicks: `prefix <` New At End, `MouseDown3Status` Rename and Kill on a non-current managed Window, `M-MouseDown3Status` Kill on an unmanaged Window with the Registry unchanged, a stale-mirror refusal with no raw kill, `prefix >` Horizontal Split and `M-MouseDown3Pane` Kill on a non-active managed Pane, `MouseDown3Pane` Kill on an unmanaged Pane, and a second Project's last Window leaving a zero-Window Project.
193
- - `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.
194
- - `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.
195
- - `make test`: Go updater exact-target atomic replacement. `update apply` on the `go` backend no longer runs `go install` and then converges with the path it resolved beforehand: it stages a scratch `GOBIN` beside the active executable, publishes into it, checks the artifact is a regular executable file, replaces the exact active executable through the same `atomicBinaryReplacer` the github-release backend uses, and runs the post-publication `config apply` against that replaced target. `TestUpdateApplyGoReplacesTheExactActiveExecutable` requires the target file to hold the published bytes for both `apply` and `--no-apply` and names the replaced path in the output; `TestUpdateApplyGoPublishesIntoAScratchGobinBesideTheTarget` pins that the publication carries exactly one environment entry — an absolute `GOBIN` that is a sibling of the target, so the user's real `GOBIN` is never written and the rename stays within one filesystem; `TestUpdateApplyGoRefusesArtifactsThatAreNotRegularExecutables` covers a missing artifact, a directory, a symlink and a non-executable file, and requires the target untouched, the scratch removed, and no post-publication convergence; `TestUpdateApplyGoCleansScratchAndLeavesAUsableBinaryOnEveryFailure` walks a missing toolchain, a failing `go install`, a failing replacement and a failing convergence, requiring the recovery command in every message and either the old or the new binary at the target, never a partial write; `TestUpdateApplyGoDryRunReplacesNothing` pins the preview lines and zero execution; and `TestRunUpdateExternalWithEnvLayersOverTheCallerEnvironment` holds the memoization boundary by requiring `PROJMUX_PROJDIR` to reach the child alongside the `GOBIN` override. `TestUpdateApplyRunsGoUpgradeInPublicationOrder` and `TestUpdateApplyRunsGoUpgradeNoApply` keep the published stage order unchanged.
196
- - `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.
197
- - `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.
198
- - `make test`: Prerelease rc channel Phase 1 release channel axis. Update judgment now carries a release channel orthogonal to the install path, defaulting to stable in the strong sense -- unset, empty, and unrecognised values all resolve to stable, so no misconfiguration opts an install into prereleases (`TestReleaseChannelDefaultsToStableForEveryUnrecognisedValue`, `TestReleaseChannelEnvOptsIn` for the `PROJMUX_RELEASE_CHANNEL` opt-in and the resolver seam a stored setting will drive). `TestDefaultReleaseChannelNeverSeesAPrerelease` is the negative fixture for both install paths at once: npm is handed a `dist-tags.rc` pointer it must ignore, and github-release fails the test if it so much as requests the endpoint that lists prereleases, with no `u` offered in either case. `TestRCChannelAnswersWithTheNewerOfStableAndRC` pins the opted-in authority for npm (`dist-tags.rc` beside `dist-tags.latest`) and github-release (the release list, drafts skipped, ordered by semver precedence rather than creation) and requires the newer of the two lines, so an rc install rejoins stable the moment that line ships; `TestRCChannelReadsBothNPMDistTagsInOneRequest` holds the shell gate's request budget by proving the rc pointer comes out of the abbreviated packument the stable judgment already fetched. `TestPrereleaseVersionsCompareByPrecedence` covers the defect itself -- `updateVersionPattern` used to discard the prerelease suffix, so rc.1 read rc.2 and its own line's stable as `current` -- across rc-to-rc, rc-to-stable, numeric identifier ordering, and the stable-outranks-its-own-rc direction. `TestUpdateCacheIsKeyedByInstallPathAndReleaseChannel` makes the cache key the pair: a fresh answer from the other channel is discarded rather than aged out in both directions, a default install writes no `release_channel` field so its on-disk shape is unchanged, and a cache written before the axis existed still reads as stable without a refetch. `TestUpdateStatusJSONRevealsTheReleaseChannel` pins the additive `release_channel` field and that a default install's `latest_version` can never carry a prerelease suffix, and `TestUpdateStatusTextNamesOnlyANonDefaultChannel` keeps the default channel's text report free of a channel row. The whole of the pre-existing `update_test.go` passes unmodified.
199
- - `make test`: Prerelease rc channel Phase 2 About > Updates opt-in toggle. The release channel axis Phase 1 built now has a user-facing switch: a `Release channel` toggle under `global.about.updates` writes `[update] release_channel` into the global config, and `newUpdateCommand` binds `releaseChannelSource` to that stored value so the next judgment runs on the chosen channel with no restart. `TestAboutUpdatesTogglesTheReleaseChannelOptIn` drives the real picker loop for the whole round trip -- the row opens on stable, one Enter puts the judgment on rc, a second returns it to stable, and the config carries the explicit opt-out -- because a toggle that only repaints itself would close neither half of C-3. `TestReleaseChannelOptInIsVisibleToTheNextJudgment` reads the stored value back through `updateCommand.releaseChannel()` rather than through the Settings process that wrote it. `TestReleaseChannelDefaultsToOffOnAnInstallThatNeverConfiguredIt` is the empty-fixture default: an untouched home resolves to stable, and rendering the row is proven not to be a write, since a key created merely by looking at the screen would silently retire a `PROJMUX_RELEASE_CHANNEL` opt-in. That is also why the resolution order is `stored setting > environment > stable` and not `stored setting exists at all`: `TestReleaseChannelSettingOutranksTheEnvironmentOnlyOnceStored` pins that the environment keeps answering until a setting exists and loses the axis the moment one does, including when the stored choice is stable -- which is what makes "turn it off and you are back on the stable line" true. `TestStoredReleaseChannelIsFailClosedForUnknownValues` keeps the stored side as strict as the axis, and `TestProjectUpdateConfigReleaseChannelRoundTrip` / `TestProjectUpdateConfigOmitsAnUnsetReleaseChannel` hold the on-disk shape, including that an unset channel renders no `[update]` section so "never configured" stays distinguishable from "configured to the default". `TestReleaseChannelRowStaysInAboutAndNotInLabs` is the positive companion to the Labs negative guard in `settings_test.go`, which passes unmodified; the whole of `update_test.go` also passes unmodified. `TestReleaseChannelToggleDoesNotReportItselfAsAnUpdate` keeps the row's feedback out of the apply vocabulary: the toggle shares the `update:` action prefix with check and apply, so the generic prefix label announced "Update complete" one row below "Update now" -- wording a user is entitled to read as "the binary was just replaced". The toggle now names itself in the mutation inventory while its two siblings keep theirs.
200
- - `make test`: Prerelease rc channel Phase 3 channel-aware `update apply`. Apply now picks its install target from the same authority the judgment read and verifies the result by semver precedence, closing the gap where an opted-in install was told an rc was available and then handed the stable line anyway. `TestRCChannelAppliesTheDistTagItsJudgmentRead` pins the npm target across the three registry states -- the rc pointer ahead of stable resolves `projmux@rc`, the line's own stable release supersedes its rc and resolves `projmux@latest`, and a registry with no rc pointer stays on the stable line -- so an rc install follows the judgment back off the prerelease instead of being stranded on it. `TestRCChannelGitHubReleaseApplyDownloadsThePrerelease` pins the github-release half against the release list with `releases/latest` made a trap, because that endpoint by definition never carries a prerelease, and requires the rc asset to be the binary actually published. `TestUpdateApplyVerifiesPrereleaseUpgradesByPrecedence` covers the reported symptom directly: `0.15.0-rc.1` to `0.15.0` succeeded and was still reported as "installed version did not change" because the check compared only the numeric core; the fixture also holds rc-to-rc, numeric identifier ordering, and the unchanged stall and backwards verdicts. The change-freeze boundary is `TestDefaultChannelNpmApplyIsUnchangedAndAsksNoRegistry`, which requires the default channel's published sequence to be byte-identical and its target to resolve without a single HTTP request, and `TestDefaultChannelGitHubReleaseApplyNeverAsksTheReleaseList`, which fails if a default install so much as requests the prerelease-carrying list. `TestUpdateApplyDryRunNamesTheAuthorityItsChannelWillRead` keeps `--dry-run` from promising `releases/latest` or `projmux@latest` to an install that will read neither. The whole of the pre-existing `update_test.go` passes unmodified.
201
- - 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.
202
- - `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.
203
- - `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.
204
- - `make test` also covers welcome revisit policy: legacy welcome state remains readable without suppressing shell entry, Enter continues without storing release skip, `s` stores an update skip for the current latest tag when an update is available, source installs show disabled Upgrade guidance, stale update cache refresh is best-effort, Settings > About > Welcome opens a Settings-native viewer without pending state, and shell prompt display does not schedule a redundant attach popup.
205
- - `make test` also covers transport-dependent app tmux defaults for pane/window navigation (`M-Left`/`M-Right`/`M-Up`/`M-Down` and `M-S-Left`/`M-S-Right`) while keeping visible default chords, allowing additive safe plain aliases that do not store or remove the transport defaults, and no UserKey/CSI-u generated fallback.
206
- - `make test` also covers direct `projmux create agent --provider <claude|codex|antigravity>` and `projmux create pane` launches, enabled-agent gating for disabled saved defaults and disabled picker selections, selective picker filtering with all-disabled shell fallback guidance, resume picker routing/New delegation/zero-session fallback, config-default preservation, extra args appended to resolved agent executables, managed pane metadata, and selective picker delegation. The legacy `ai split` route and its `--agent`/`--force-agent`/`--print-pane-id` flags are retired; the split-UI half of that coverage now lives in the Phase 11 entry below.
207
- - `make test` also covers managed Antigravity hooks integration in an isolated home: named-entry install/dry-run/remove/idempotency, byte-preserving unmanaged JSON edits, explicit four-event absolute commands with no `PreToolUse` permission policy, valid non-blocking stdout fallbacks, conflict/malformed/symlink/permission diagnostics, provider registry/doctor/Settings install-remove-dry-run guidance, PreInvocation thinking/busy state, quiet PostInvocation/PostToolUse bookkeeping with tool-error diagnostics, and Stop decision/notify behavior.
208
- - `make test` also covers Settings rendered-entry reachability across root/section/detail builders with a closed owner-loop table, handlerless-value rejection, passive info/disabled Enter no-op behavior, `send-noti` visibility in Hooks, no retired Project recipe or Effective merge route/catalog/i18n residue, AI/Labs view-first detail rows, Appearance Path/Git/Notify icon direct off/symbol/emoji preview selection with no Change page, Notifications root/Desktop notifications/Delivery sources relocation, removal of standalone queue/hook-override rows, `PROJMUX_NOTIFY_HOOK` state merged into Delivery sources, localized Korean Desktop notifications root/detail chrome without visible English residue, Delivery sources command-row clipboard copy, and Labs limited to Live system resources plus overview-first Project Hooks while legacy picker env/file values remain native-compatible.
209
- - `make test` also covers the macOS/Linux/WSL live resource status Lab: default-off and atomic saved-value persistence, direct Labs on/off mutation plus live tmux option update, generated app/standalone conditional placement between git and clock for both Lab modes, independent CPU 70/90 and memory 75/90 warning/critical boundary classification, mixed-severity per-metric tmux style runs without visible severity words, secondary-style unavailable values without severity leakage, fixed four-column percent slots and invariant metric/segment visible widths across nil/0/9/15/69/70/74/75/89/90/99/100 under default/custom themes, light/dark semantic-role rendering, Linux `/proc/stat` first/delta/reset/malformed/overflow and stale-reference handling, Linux `/proc/meminfo` `MemAvailable` semantics, macOS native Mach CPU tick and free/inactive memory sampling, 0–100 clamping, atomic private sample-cache writes, and compact unavailable rendering.
210
- - `make test` also covers AI desktop notification dedupe precedence (env override > Settings saved value > default), Desktop notifications mode persistence through `desktop-notify-mode`, saved-config precedence over live tmux options, `projmux config apply` regeneration of `@projmux_desktop_notify_mode`, configured dedupe-window collapse/send behavior, Settings > Notifications AI dedupe preset/custom rows, explicit notify focus consume rules for selected critical rows and older same-pane non-critical AI cleanup, preservation of critical/permission/stop-failure/external/git/k8s rows during bulk cleanup, `projmux internal focus --uri` compatibility-input queue consume, and attention clear paths that do not ack the queue.
211
- - `make test` also covers the two-state desktop notification contract: `NormalizeDesktopNotifyMode` accepting only `off`/`notify` while reading retired `raise`/`auto-raise`/`autoraise` literals as `notify`, per-source legacy-alias resolution that keeps env > saved file > tmux option precedence instead of falling through, a platform-neutral `notify` default including WSL + Windows Terminal, saved/tmux-applied values restricted to `off`/`notify`, Settings > Notifications > Desktop notifications offering exactly `Off` and `Notify` with no `Raise` row/description/search keyword in English and Korean, WSL Toasts carrying no `launch`/`activationType="protocol"` payload and writing no `projmux://` protocol-handler registration, and `projmux focus` never dispatching a host-window focus adapter in any mode.
212
- - `make test` also covers AI hook runtime action precedence over catalog defaults, runtime quiet for known Codex notify events, runtime notify for known Claude quiet events, generic in-app-only notify rows for known Codex hook events without specialized handlers, suppression of desktop notification and `send-noti` dispatch on that generic path, separation of runtime hook action from catalog install events, Settings > Notifications hook quiet policy display/write behavior without external install/remove execution, hook desktop notification payload parity with the in-app queue text across the shared OS notification payload, normal/transient OS urgency and expiration for critical AI queue rows, and dormant title/capture fallback gating once a pane is hook-active.
213
- - `make test` also covers Settings > Session State as global settings-only UI with default-off auto-save and no window/pane tree, Settings > Project > Session State project-derived identity plus project auto-save override/effective source rows and distinct save labels, closed-project save disabled reasons, named-snapshot portable path conversion, project/global auto-save precedence, Projects > Sessions > State read-only snapshot overview, and the optional closed-Project startup screen with exactly `Continue project` / `Open fresh`, Esc navigation, existing-session bypass, startup-before-trust ordering, detached client-scoped continuation, trust approve, and trust deny/cancel zero-session behavior.
214
- - `make test` also covers `projmux shell` usage without startup selector flags, project-context default target lookup from `PROJMUX_CWD` or nearest project marker, explicit `--session` separation from project-derived defaults, and direct empty attach behavior without shell startup picker/replay.
215
- - `make test` also covers pane identity separation and projection: canonical `rename-pane-label` prompts with `pane label:` initialized only from the current label, Enter confirms, empty Enter clears, and native Esc cancels; unchanged Ctrl-Shift-M default and Settings expose only `rename-pane-label`, while stale `[bindings.rename-pane-topic]` keymaps fail with an exact `[bindings.rename-pane-label]` replacement. AI topic set/clear keep existing topic/manual ownership, no raw-title action is exposed, and pane border/window tab/popup/sidebar/recent-window resolve user label → agent AI topic → known shell → raw title.
216
- - `make test` also covers session snapshot pane identity capture, old/new decode, and replay field-matrix contracts: `Pane.Label`, raw title, recipe topic/manual ownership, startup fields, and agent/resume metadata stay separate; old snapshots leave label and ownership absent without equality inference; tmux creation-returned pane ids are the replay targets; and agent direct-start never copies a saved topic into the raw title.
217
- - `make test` also covers Antigravity v1.1.12 current-storage resume discovery from sanitized synthetic `last_conversations.json` and workspace-bearing `conversation_metadata.json` fixtures: exact/depth workspace filtering, URI/path list variants, required metadata summaries, safe-title/short-UUID and blank-turn degradation, normalized UUID plus exact regular `<uuid>.db` existence/mtime validation, missing DB/sidecar/traversal/file-symlink rejection without DB reads, malformed/missing/unknown/workspace-less clean degradation, the separate live hook/session-state lane plus disk-picker last-conversation > metadata > legacy source dedupe without cross-provider behavior changes, high live/medium cache/low history preview and doctor confidence, and picker selection wiring to `agy --conversation <uuid>`.
218
- - `make test-e2e` also runs a disposable exact tmux server save → destroy → production restore flow for shell/startup/agent panes, changes `pane-base-index` before restore to reject index-derived targets, verifies raw tmux metadata plus visible label resolution, and replays an old snapshot with cleared label/manual ownership.
219
- - `make test` also covers manual Session State Save snapshot and restore dry-run preview actions, title-first non-agent pane preview labels with legacy snapshot fallback, agent pane health-first bounded rows that keep status/confidence/source ahead of the full resume id while retaining id/title in the unbounded model, missing-snapshot messaging, and the absence of the removed statusbar State shortcut.
220
- - `make test` also covers legacy named-snapshot storage compatibility for `.projmux/layouts/*.toml`, supported interpolation validation, malformed-file list warnings, portable conversion helpers, dry-run conversion to the read-only session-state preview with source labels, and missing-current-session failure. Startup does not expose these artifacts as action choices; explicit snapshot projection is the only restore authority.
221
- - `make test` also covers the project-local executable artifact trust boundary for named snapshots: one-read exact-byte parse/hash reuse, SHA-256 allow-once/allow-always decisions, changed-byte and rename/replacement reauthorization, final-file and parent-component symlink rejection, config-absent and `PROJMUX_PROJECT_HOOKS=off` independence, non-interactive fail-closed behavior, commandless-layout compatibility, terminal-control escaping for layout names/descriptions/window names/agent topics/command previews, and deny/error paths that stop before restore, session/pane creation, or startup `send-keys`.
222
- - `make test` also covers `projmux agent integrate codex` default hooks install/idempotence/dry-run/removal, compatibility legacy Codex notify mode install/idempotence, dry-run preview, unmanaged `notify = ...` conflict refusal, managed-block removal, catalog-driven Codex hook event installation with local override merging, nested inline Codex hook schema, `[features]` merge/preservation, preservation of unrelated Codex/Claude config and unmanaged hook entries, Settings Codex `/hooks` review notice and tested-version rows, and remove-all behavior for projmux-managed Codex blocks.
223
- - `make test` also covers canonical `projmux internal agent-hook ingest codex-hook` event parsing, PermissionRequest critical queue rows with tool/action metadata, Stop info queue rows, UserPromptSubmit busy/no-queue behavior, pane-level semantic badge kind writes that keep permission, response complete, and in-progress states distinct, quiet/no-notify diagnostics for non-firing Codex hook events including override-added catalog events, cataloged hook notification body formatting, hook-active pane marking, topic preservation without hook candidate writes, and mutable resume metadata writes for `@projmux_ai_resume_id`, source, and updated-at.
224
- - `make test` also covers `projmux agent integrate claude` user-level Claude Code hook settings install/idempotence, dry-run preview, unmanaged projmux ingest command conflict refusal across all settings events, managed-command removal by marker even for stale events outside the current catalog, preservation of unrelated settings/hooks, catalog-driven Claude hook event installation with local override merging, and managed wiring for the 29 Claude Code 2.1.140 events in the embedded default catalog.
225
- - `make test` also covers canonical `projmux internal agent-hook ingest claude-hook` core and extra Claude Code hook ingest for event parsing, transcript fallback, cataloged hook notification body formatting, permission summary formatting, UserPromptSubmit busy/no-queue behavior, Notification severity/text mapping, pane-level semantic badge kind writes for permission, input, response complete, and in-progress states, StopFailure/TeammateIdle text/severity/metadata mapping, SubagentStop quiet/no-notify diagnostics, unknown future-event quiet fallback, hook-active pane marking, topic preservation without hook candidate writes, mutable resume metadata writes for `@projmux_ai_resume_id`, source, and updated-at, and Claude transcript path preservation.
226
- - `make test` also covers the Globalization Phase 0 English baseline for AI desktop notification summaries/bodies and hook-ingest queue text while preserving agent names, tool names, commands, paths, and provider payload values as literals/data.
227
- - `make test` also covers the Globalization Phase 1 message catalog foundation: locale resolver priority and normalization, `ko-KR` to `en-US` fallback, missing fallback-key errors, `en-US` completeness for foundation keys, and plain-text versus ANSI/tmux-styled fragment API separation.
228
- - `make test` also covers the Globalization Phase 2 formatter foundation: compact/full relative age and duration formatting, locale-specific just-now text, notification count plural/count preservation, list joins that preserve payload items, status token and target label localization, terminal cell-width measurement/truncation, and ANSI/tmux style wrappers treated as zero-width.
229
- - `make test` also covers the Globalization Phase 3 notify runtime migration: AI notify category rendering for `en-US`/`ko-KR`, Codex/Claude hook literal preservation for agent names, tool names, commands, paths, URLs, provider messages, subagent and teammate payloads, desktop notification and in-app queue display parity, notify sidebar/statusbar locale formatter output, and raw queue storage schema preservation.
230
- - `make test` also covers statusbar notify width fallbacks that preserve normal INFO/WARN/CRIT badges at readable widths, use dotless clipped text/count at very narrow widths, and keep hard truncation closed with `#[default]`.
231
- - `make test` also covers Usage popup MODEL/WINDOW budget projection for percent-only, absolute-count, and unsupported rows; untruncated Antigravity labels; bounded rune-safe future-provider ellipsis; ANSI visible-width alignment; popup-only inactive named-quota filtering; and retained inactive quota metadata in text/JSON/cache surfaces.
232
- - `make test` also covers the Globalization Phase 4 Settings and popup guidance migration: `en-US`/`ko-KR` Settings root title/chips/rows/footer rendering, native picker search/empty/footer rendering without overflow, Settings row padding by terminal cell width, shell welcome guide catalog rendering, and long Korean ANSI/tmux-styled guide truncation safety while preserving key/command/path literals.
233
- - `make test` also covers safe shell Exit guidance in `TestShellWelcomeExitGuidanceFallbackAndLocales`: fallback, `en-US`, and `ko-KR` recommend the literal `projmux quit`, predict its default interactive action picker without promoting `--yes`, and remain lossless and terminal-cell-width-safe when wrapped and rendered.
234
- - `make test` also covers the Globalization Phase 5 locale settings surface: `PROJMUX_LOCALE` > global `[ui].locale` > `LC_ALL` > `LC_MESSAGES` > `LANG` > `en-US` priority, Settings > Appearance > Language / Locale row/detail visibility, `auto` current-locale/source display, unsupported locale fallback warnings, global config writes, and preserved command/config/env/path literals.
235
- - `make test` also covers the Globalization Phase 6 governance guard: Go string-literal audit classification for hardcoded Korean candidates, English user-facing candidates, and ignored literal/data/debug examples; no unapproved runtime Korean literals outside catalog/formatter/test fixtures; `en-US` coverage for every embedded default catalog key; and required `ko-KR` coverage for migrated notify, Settings, picker, welcome, update, and help surfaces.
236
- - `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.
237
- - `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.
238
- - `make test` also covers `projmux diagnostics agent-hook` tail/path rendering and bounded JSONL log trimming for ingest diagnostics.
239
- - `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.
240
- - `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.
241
- - `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.
242
- - `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.
243
- - `make test` also covers Operational diagnostics Phase 5 AI watcher/ingest adoption: the closed provider/event-kind/result/failure schema, provider-specific kind catalogs, result/failure tuple compatibility, and cross-family rejection; watcher start plus pane-gone/hook-active terminal ownership, actual launch-failure classification, per-run tuple coalescing, and no per-poll records; Codex/Claude/Antigravity malformed, read, oversized, unmatched, unsupported-event, and route-error projection plus tmux-bell valid, blank, and pane-not-found coverage; zero common AI events for normal state/notify/known-quiet/deduped ingress while Phase 4 notify retains its transition; legacy `ai-ingest.log` producer, `diagnostics agent-hook`, bounded retention, and support count-consumer parity; append-failure best-effort ownership with no generic duplicate; and seeded prompt/tool/path/UUID/conversation/session/title negative fixtures for operations and support output.
244
- - `make test` also covers Operational diagnostics Phase 6 resource adoption and legacy review: exact closed resource source/result/failure tuples and cross-family rejection; production app graph wiring through Resource Inspector lifecycle → tmux inventory/project discovery/procfs attribution; unavailable, partial, stale, collection error, and actual context-deadline scan-budget fixtures; normal sample/refresh zero volume, persistent-transition coalescing, silent recovery plus anomaly re-entry, and append-failure best-effort UI/CLI preservation; separate host-only status sampler zero ownership; operations text/JSON and error-only support projection with partial/stale omission; seeded CPU/MEM/PID/process/path/title/command/UUID/session/error text negative fixtures whose oracle inspects non-time semantic fields rather than whole serialized JSON, including a deterministic `.100` timestamp false-positive guard and real path/PID field fail-closed fixtures; common journal retention/rotation regression; and the file/surface legacy diagnostic inventory without breaking removal.
245
- - `make test` also covers the Phase 1 explicit redacted support report: complete preview-before-write ordering, deterministic private archive entries and manifest schema/redaction metadata, Doctor schema-version-1 structural reuse with closed static diagnostic allowlists, strict no-repair bounded operations reads, config-presence-only and AI-ingest count-only projection, exhaustive seeded free-text/path/routing/content negative redaction, missing/corrupt/unreadable source reasons, collision/partial-temp safety, and report success/error exclusion from legacy-hook migration and operational outcomes.
246
- - `make test` also covers welcome revisit policy: legacy `skip_version` readability without shell suppression, `s` Skip until next vs Enter continue, Settings > About > Welcome native viewer, best-effort shell update cache refresh, and attach-popup no-duplicate/no-op behavior.
247
- - `make test` also covers `projmux quit` action-picker rows, cancel/close no-op behavior, explicit quit of only app-owned tmux runtimes marked by `@projmux_app=1`, missing/default runtime no-ops, dispatcher wiring, and `Settings > About > Quit projmux` routing through the same picker before any shutdown side effect.
248
- - `make test` also covers the built-in semantic palette foundation: non-empty fallback truecolor/tmux tokens, distinct action/attention/AI/progress/danger roles, native picker chip/current/titlebar render strings including titlebar frame `surface` / `chrome_foreground` inheritance without separate overlay ANSI, statusbar git/notify/usage/settings palette regressions, attention/pane-border/popup/switch progress color guards, renderer-only lead-mode topic prefix styling, settings/trust/destructive row color guards, global-only theme resolver behavior for `global`/`fallback` source labels (project `[theme]` is not a source), preset fill, explicit token override, unknown-preset/invalid-color warnings that ignore only the global layer, and truecolor-to-tmux mapping with generated `xterm*:RGB` terminal feature support, semantic role map (`RenderRoles`/`ANSIRoles`) fallback byte-identity for non-background historical palette literals plus explicit-theme repaint of Tier A/B chrome/state roles while Tier C renderer-only literals stay fixed, split foreground behavior where legacy `foreground` fills `text_primary` and `chrome_foreground` but `text_primary` does not repaint frame/title/search/border/status chrome, Phase 6 public token (`progress`/`success`/`action_required`/`pane_active_bg`/`focus`) repaint of the tmux and ANSI roles with `action_required` kept independent of `critical`, default `projmux` background tokens pinned to terminal default without separate terminal preset variants, Phase 6b pane-body-vs-popup/status background separation where an explicit `background` repaints the inactive pane body, an explicit `surface` repaints popups/native frames, and an explicit `status_background` repaints only the bottom status bar, native popup-toggle `display-popup -s` body style propagation from the effective theme without touching global popup/shell/status styles, and Settings global theme editing with the merged Global/Effective view, directly selectable prefixed theme token rows, no legacy `foreground` row, no project theme editor, and no font keys.
249
- - `make test` also covers Recent Windows Phase 0 state/model behavior: server-scoped file paths, missing/corrupt state policy with corrupt-file backup, MRU newest-first recording, same-window dedupe and promotion, default queue limiting, current-window exclusion, cross-session candidate retention, gone-window pruning against live window inventory, and label fallback rules that keep win/pane ids as debug fallback instead of the primary row label.
250
- - `make test` also covers Recent Windows Phase 1 native picker behavior: `projmux window recent` empty-state messaging, cross-session candidate picker rows with descriptive project/session/window/pane command age metadata, pane summaries mirroring visible pane labels with AI topic and known-shell command precedence, four visible pane cells before compact `+N` overflow, window-only switching without pane restoration, switch-failure refresh/prune handling, and gone-window pruning before picker selection.
251
- - `make test` also covers notify reconcile eviction policy: expired rows are removed only when their real tmux pane/session target is gone, expired live and unexpired gone rows remain pending, inventory failures skip target-based eviction, hard-cap overflow keeps the newest 256 rows, and runtime prune triggers cleanup through the reconcile hook after session kills.
252
- - `make test` also covers stale state cleanup policy: successful prune, runtime stop, runtime sessions picker, and switch/sidebar session kills trigger best-effort preview-row cleanup; notify refresh publishing sweeps dead-PID sockets while preserving live-PID sockets; snapshot prune listing distinguishes dead sessions from old live persistent sessions without deleting either, explicit named deletion removes only selected snapshots, malformed snapshots remain preserved with a warning, and doctor documents the manual-only retention policy.
253
- - `make test` also covers Recent Windows Phase 2/3/4 keybinding, popup, and runtime recorder integration: `RecentWindows:Open` owns the guaranteed `M-3` launch default, generated tmux config uses client-scoped `tmux popup-toggle recent-windows`, the popup body runs `projmux window recent`, generated tmux config installs background `window record` hooks for window and client-session changes plus first-use population without `session-window-changed`, `window record` snapshots current tmux socket/session/window/active-pane metadata into the socket-scoped store with escaped field parsing, repeated same-window record fires dedupe instead of growing entries, the queue remains bounded by `recentwindows.DefaultLimit`, Settings > Keybindings shows Recent Windows with the `M-3` default, setup probe text names Recent Windows, `SessionPopupToggle` no longer owns the guaranteed `M-3` default, and escaped tmux field separators are parsed for current/list window metadata.
254
- - `make test` also covers the Linux resource-attribution Phase 0 contract: tmux-only typed socket/session/window/pane PID/TTY/current-path/project-anchor inventory without unsupported placeholder values; explicit project-anchor priority plus read-only current-path fallback through canonical pinned/root-child discovery, longest-root matching, outside/home exclusion, and linked explicit/fallback conflict handling; one-pass procfs stat/meminfo collection; PID+starttime reuse defense and SID ownership; first/invalid/logical-CPU-change unknown CPU; host-share/core-equivalent CPU and RSS/host-ratio memory; unique pane-to-window-to-project conservation; linked-anchor `Shared / ambiguous`, missing-anchor `Unassigned`, setsid escape, fast-exit/race/permission partial states; and explicit host remainder/overage without silent clamping. The opt-in `PROJMUX_RESOURCE_PROJECT_FALLBACK_SMOKE=1 go test -run TestResourceProjectFallbackTransientSmoke -v ./internal/integrations/tmux` test provides an isolated read-only fallback fixture, while `PROJMUX_RESOURCE_TMUX_SOCKET=projmux PROJMUX_RESOURCE_EXPECT_PROJECT_ROOT=/path/to/project go test -run TestResourceAttributionRealTmuxReadOnlySmoke -v ./internal/integrations/tmux` provides sanitized live-socket project-bucket/count evidence; benchmark commands and current measurements are maintained in [resource-attribution.md](resource-attribution.md).
255
- - `make test` also covers the Resource Inspector Phase 1 vertical slice: immediate non-actionable warming chrome; Project → Window → Pane → read-only detail hierarchy with project/window `PANES n` and pane `PROCESSES n` projections; concrete-project and `No project match` drill-down plus the non-actionable Other summary; defensive Shared attribution omitted from project rows while its CPU/RSS/pane aggregate remains in Attributed totals and a bounded non-drillable Sample diagnostic; shared label → active-agent topic → interactive shell → raw-title pane identity precedence with reused `Process <PaneCommand>`/PID-SID/Pane %N/TTY secondary and detail naming; resource-only Right/Enter forward, Left back with root no-op, all-depth Esc close, and complete Alt-Left action/hint removal; statusbar-parity CPU 70/90 and memory 75/90 exact semantic boundaries with severity-word-free values, unavailable `--`, lifecycle text, and resolved default/custom theme roles; Name-default Tab-computed CPU/Memory/Name ordering that remains stable across automatic refresh; stable-id search metadata plus refresh selection/query/breadcrumb/focus preservation and vanished-row nearest-neighbor behavior; fresh/stale/partial/unavailable/empty/gone/overage transitions with atomic refresh-progress/footer updates; an exact fixed five-row divider/Host/Attributed/Coverage/Sample bottom summary dock that remains independent of scroll/search and separate from the action footer, with bounded diagnostics on Sample only, minimum navigable list height, and no divider/border bleed; changed-row synchronized differential frames at 80x24 and wide en-US/ko-KR layouts; truthful RSS-sum rendering; immediate automatic pre-collection not-fresh progress with the last complete sample retained, gated non-overlapping two-second and Ctrl-R sampling, cancellation, and prior-sample disposal; direct CLI and client-scoped popup mode; no-default `Resources:Open` catalog/Settings/config/custom-alias close behavior; statusbar `resources` range/click byte-equivalence, Labs visibility independence, other-client isolation, and existing `projmux internal status resources`/window-click fallback regression.
256
- - `make test` also covers the Resource Inspector readable-layout Phase 0 contract: non-duplicating scope breadcrumbs; separate host, attributed, Other, diagnostic, list/detail, and contextual-action bands; primary identity separated from secondary context; vertically comparable CPU/Memory/count clusters; deterministic 80x24 and wide themed frame geometry without border bleed; en-US/ko-KR copy and concrete/Unassigned stable-root parity with no Shared root row; read-only pane detail without search, pointer, Enter, or sort affordances; explicit non-actionable Other/empty/vanished surfaces; populated/empty deferred action reconciliation without hidden sort; and resource-scoped directional hierarchy flow while collector, attribution, cadence, and client-scoped popup contracts remain unchanged.
257
- - Current focused unit coverage also includes strict notify SOT behavior
258
- (TTL does not remove rows, focus success and target-gone clicks ack,
259
- reconcile reports inactive `queue-stale` rows), `get notifications --live` queue/live explanations, notify sidebar
260
- three-line grouped card rendering with child-count/fold semantics plus focus/ack/non-critical-clear/clear-all
261
- actions, notify/statusbar/sidebar attention-vs-AI palette assertions including muted inactive/gone rows,
262
- native picker-session-local `a` ack that refreshes the sidebar without
263
- focusing or restarting the picker, non-critical `x` bulk clear that preserves critical rows, empty
264
- state rendering after all visible non-critical rows are cleared, and notify queue-write refresh events
265
- that update an open native sidebar best-effort without failing queue pushes, and `focus` dispatch diagnostics for session fallback, unresolved
266
- targets, window fallback, pane fallback, explicit id failures as unresolved exits, and
267
- notify-only fallback.
268
- - Security hardening coverage includes private `0700` directory and `0600`
269
- file creation for sensitive local config/state, idempotent repair of legacy
270
- permissions on load/save, best-effort behavior when chmod is unsupported,
271
- symlink-safe repair that does not chmod an external target, and race-safe
272
- notify/recent-windows lock retry jitter under concurrent store operations.
273
- - Picker focused unit coverage includes native picker item/action mapping, direct native routing with no backend selector, stale selector env/file non-observation without lookup/delete/rewrite/propagation, native title-focused filtering, numeric selection, shared close actions including raw native custom actions, fuzzy-score reference fixtures, AI picker title chrome and stable search-key ordering, Settings title chrome and root section order, Settings Labs without picker source or hidden keybindings rows, compact multi-line metadata gutters with one-column-indented metadata, proportional native scrollbar thumb rendering, native row width clamps that preserve ANSI resets and Korean/wide-cell frame geometry with matched scrollbar/no-scrollbar row budgets including Settings > Appearance AI badge preview rows and parent long emoji preview rows, native app-background and semantic selected/muted/accent/warning/critical role coverage for fallback/effective themes across content padding, empty no-footer rows, footer rows, scrollbars, split-preview gaps, reset-to-padding continuation, and Korean Settings > Appearance AI badge style chips/prompt/emoji rows, multiline partial next/previous item row rendering with rendered-row scrollbar units, fixed split-preview and sidebar list viewports with scrollbar tracks, native Up/Down and Ctrl-N/Ctrl-P navigation wrap with empty-list safety and PageUp/PageDown/Home/End clamp regression coverage, native mouse down/follow-drag/release behavior, deferred native row updates that preserve filter/selection state and can repeat from event triggers, preview tab/control normalization before width clipping, optional native frame titlebars without same-line rule fill and with title/chip gap inheritance guards, titled Alt-1 sidebar chrome, statusbar-preserving sidebar popup height, compact project sidebar popup sizing, cheap Alt-1 first-paint rows with deferred git/window/attention/preview enrichment, fixed three-line Alt-1 sidebar row geometry across cheap/enriched paints, bulk switch existing-session classification with per-candidate fallback, and notify sidebar title/popup sizing.
274
- - Picker/setup lifecycle coverage includes deferred native key-reader stop and repeated reopen cleanup, plus per-probe controlling-TTY cleanup and timeout-then-next-key reads without stale goroutines stealing input.
275
- - Doctor focused unit coverage includes tmux required/stale behavior on every host, Windows `stty` skip behavior, and tmux bell fallback diagnostics.
276
- - `make test`: Codex endpoint diagnostics C-1 is enforced by `TestDoctorCodexEndpointMismatchExactActivationSet` (mismatch/match/mixed/empty and installed-CLI skew), `TestDoctorCodexEndpointMismatchIncludesBoundNonRunningActivation` (current activation scope), `TestDoctorCodexEndpointMismatchSignalsEnumerationGaps` (missing/orphaned/conflicting activation, foreign/opaque endpoint and unavailable observations), and `TestDoctorCodexEndpointMismatchDoesNotGuessPrivateGenerationRouting` (version-shaped private IDs and unknown pool routing). `TestDoctorCodexEndpointMismatchTextJSONSupportParity` and `TestDoctorCodexEndpointMismatchPartialEnumerationParity` require exact Doctor UID sets, support-hashed identity parity, visible partial enumeration, 2026-09-09 n=1 observed risk with undetermined causality, and no mismatch block for a complete matching census. `TestDoctorCodexEndpointMismatchSupportAllowlistIsScoped` preserves support redaction. `TestDoctorCodexEndpointMismatchReadOnlyArgvAudit` exercises the production Doctor/support probe against an isolated failing proxy, requires visible incomplete enumeration and only `app-server proxy`/`app-server daemon version` argv, and preserves the Codex state tree; it never reaches live app-server, broker, or supervisor processes.
277
- - `make test`: Codex endpoint diagnostics C-2 is enforced by `TestOperatorRecoveryGuidanceOwnershipMatrix`: unmanaged current, skewed, and unknown-version endpoints name `codex app-server daemon bootstrap` and the observed `pid` backend's need to bootstrap again after reboot; managed and unknown ownership omit that prescription, with readiness/refusal/recovery codes unchanged. `TestDoctorCodexRecoveryGuidanceTextJSONSettingsSupportParity` requires Doctor's bounded sentence and the existing Settings/Doctor JSON/support recovery code to agree without a schema change. `TestOperatorRecoveryGuidanceReadOnlyArgvAndState` exercises repeated Doctor/Settings/support triggers against an isolated ready unmanaged proxy, allows only `app-server proxy` and `app-server daemon version` argv, and preserves Codex state byte-for-byte; recovery execution argv is zero. `TestProductSourceHasNoSharedDaemonStopRestartOrKillArgv` also forbids bootstrap argv in the product source. The reboot observation makes no claim about other backends' boot persistence.
278
- - Shell focused unit coverage includes canonical typed Project/ControlSession bootstrap followed by the non-creating foreground route `tmux -L projmux -f <generated config> attach-session -t =<session> [-c <cwd>]`; disappearance after preflight fails closed instead of letting `new-session -A` provision outside the plan. Generated tmux config callbacks, native key-broker platform policy, runtime overrides, and nested-socket guards remain covered.
279
- - `make test` also covers npm retire/staging path canonicalization for the resolved projmux executable so generated tmux config and hooks never embed a `node_modules/.projmux-<hash>` directory, plus error-suppressed attention and rebalance-panes hooks.
280
- - `make test` also covers the public config spelling: `projmux config edit [--get|--set <mode>]`, `config render standalone|app`, and `config apply` forward raw argv to their manual leaf handlers; flags, unknown tokens, streams, handler errors, and side effects stay on those handlers. `make install` and every update path emit `projmux config apply`, while generated plumbing uses only the hidden `internal` namespace.
281
- - `make test` also covers Legacy CLI retirement Phase 2/3: rejected compatibility argv below the surviving mixed roots returns exact replacement guidance with stdout/handler/pre-dispatch mutation zero; `attach project`, `focus project|window|pane`, `pin project`, and `prune project|snapshot` remain exact raw-argv forwards. The fully removed human roots (`current`, `kill`, `notify`, `sessions`, `session-state`, `tag`, `upgrade`, `usage`), removed pre-namespace internal tokens, and the final `ai` root have no catalog or top-level handler and use root unknown-command exit 1 for every argv. The exact `tmux apply` updater handoff still normalizes to `config apply` before routing, while every other `tmux` argv remains unknown. Canonical `internal agent-hook ingest` forwards all provider event shapes to the unchanged handler, while stale unmanaged legacy-hook fixtures remain byte-preserved and require the documented manual canonical-prefix remediation. The tracked-repository literal audit permits the retired producer spelling only in test fixtures and explicit retirement/upgrade history, ignores untracked local/build artifacts without opening them, and a normal `go build ./cmd/projmux` regression requires the production binary itself to contain zero contiguous retired producer bytes. Maintained-doc scans, catalog/canonical/handler two-way audits, root/command-tree/reference goldens, the exact removed route set, preserved Shortcut/kind aliases, and generated producer scans close the topology and drift contracts. The release-boundary ledger additionally pins the post-retirement `-p`/`-w`/`-A` resource scopes, Registry topology materialization, Registry-only Offline Window deletion, and their generated CLI reference evidence so compatibility cleanup cannot silently roll back later canonical behavior.
282
- - `make test` also covers current-directory keybinding shell safety: the generated `run-shell` command passes `#{q:pane_current_path}` to `switch open`, and adversarial cwd fixtures containing spaces, single and double quotes, command substitutions, backticks, and shell metacharacters remain one literal argv without executing injected commands.
283
- - `make test` / `make test-integration` also cover post-retirement generated UI routing: Agent/Pane default-target bindings use hidden `internal agent-pane launch-default`, launch/resume popups re-enter through `internal agent-pane picker`, and direct Codex/Claude/shell bindings use the hidden `launch-provider` / `launch-shell` canonical-intent bridges; the notification sidebar uses `get notifications`, and the existing-session popup uses `runtime sessions`. Regression assertions reject retired `ai`, `notify`, and `sessions` roots in these generated commands, raw-argv forwarding tests preserve the canonical intent and picker handlers behind the hidden bridge, and the built-binary smoke drives a saved shell default through that bridge with `%7` as its exact origin: the missing Pane mirror returns the origin-loss usage refusal with exit 2 and zero stdout, logs only the read-only Pane probe, and performs zero raw tmux mutations.
284
- - `make test` also covers the canonical manifest honesty audit: every canonical spelling resolves to a full executable path in the command tree, no canonical summary carries forward-looking vocabulary or any adjudicated-and-retired string, the remaining target-state summary (`delete pane`) is pinned verbatim with a named owning track and must stay absent from every command-tree node, and the separate shortcut gaps are enumerated as a two-way diff so a future change cannot drop a mapping unadjudicated; config and workflow routes are also asserted to have no cardinality-matrix row because they resolve no resource set.
285
- - `make test` also covers singular and plural kind spellings on the four resource verbs: `Route.Aliases` in `internal/cli/catalog.go` is the single declaration, `cli.CanonicalChildToken` normalizes an alias to its canonical token before `get`/`describe`/`delete`/`rename` dispatch, and the unknown-kind refusals are rendered from `cli.ChildSpellings` so an accepted spelling can never be missing from the list they print. `TestKindSpellingAliasesAreByteIdenticalToTheCanonicalSpelling` runs all 17 canonical/alias pairs -- including the two forwarded parity kinds per verb, whose forwarded argv is compared as well -- and requires identical stdout, identical stderr, and an identical error, with the canonical run required to succeed so an all-failing pair cannot satisfy byte-equality vacuously; `TestEveryManifestKindHasAParityCase` derives the matrix from the manifest so a kind added later cannot escape coverage, and `TestKindSpellingAliasesNeverReachAnotherRoute` proves each singular `get` alias answers its own inventory and is distinguishable from every sibling kind. On the manifest side `TestCanonicalKindSpellingsSurviveTheAliasContract` pins the canonical kind token of every resource-verb child so an alias can never replace a shipped spelling, `TestNoChildAliasShadowsACanonicalSpelling` walks the whole tree for alias/name collisions and duplicate aliases, and `TestResourceVerbsAcceptBothFormsOfEveryKind` asserts zero missing singular/plural combinations with `get pane`/`get panes` as the one enumerated divergent pair. That exception is the product decision the track recorded and is held by `TestGetPaneKeepsItsOwnRouteUnderBothSpellings` and `TestGetPaneSpellingIsUnchanged`: `get pane` stays the exact-one Pane read that owns `--current -o cwd`, `get panes` stays the 0..N inventory, and neither aliases onto the other. `rename` no longer special-cases the `agent` token by hand -- the manifest has no Agent child, so `agent` and `agents` both reach the ordinary refusal.
286
- - `make test-integration` / `make test-e2e` / `make test-install-smoke`: suite containers still run with `--network none`, so `scripts/test-docker-run.sh` prefetches the checked-in module graph once per `go.sum` into a host-side Go module cache outside the repository and mounts it into the isolated run; `test/lib/smoke.sh` honors that inherited `GOMODCACHE` instead of forcing an empty run-local one.
287
- - `make test` / `make test-e2e`: resource-first create and materialization removes the `--project` dispatch discriminator, so `create window|pane|agent|<provider>` share one parser and one product model. A two-way argv table parses every flag group -- `--name`, `--label`, `-o`, the `--` payload, `--window`/`-w`, `--pane`, `--selector`, `--create-window`, `--placement`, `--cwd`, `--add-dir` -- with and without an explicit `--project` on all six spellings and requires the two parses to be identical except for the scope occurrence, which is what the user reproduction `create codex -w hi --create-window` failed on; a structural test additionally requires `create` to carry no raw-argv split seam at all, so the removed model cannot return by one edit. Scope resolution has two branches: an explicit `--project` wins inside and outside tmux and never observes the active target, while an omitted one derives the Project from the mirrored `@projmux_window_uid` and its registry owner chain, and derives the Window **and** anchor Pane only when no `--project`, `--window`, `--pane`, or `--selector` was given at all -- which is what keeps the generated `create codex|claude|pane --placement right|down` bindings a split of the Window the operator is in rather than a fan-out over the Project. Refusals are measured, not asserted: outside tmux, a Home/control pane with no mirrored Window uid, an unattributed pane, a foreign uid the Registry does not hold, and a Window whose owning Project is gone each exit 2 naming `--project` with zero stdout, zero registry transactions, a byte-identical registry snapshot, and zero tmux calls, on all four create spellings; an argv-only refusal table additionally requires every invalid-input row to be decided with zero active-target observations, so a typo is never reported as a missing scope. Behavior tests run against the in-memory tmux server: the user reproduction creates Window/Agent/managed Pane in one transaction, materializes them in the derived Project's own session, mirrors the allocated uids onto the exact tmux objects, and prints the managed Pane's live handle for `-o pane-id`; an explicit `--project beta` from inside an alpha pane lands in beta with the active-target seam untouched; an explicit `--window` suppresses the implicit anchor; and a failing `split-window` under an implicit scope rolls back to a byte-identical registry with no leftover tmux window. The generated keybinding producer now renders every `run-shell` projmux binding as `TMUX_PANE=#{pane_id} <bin> ...`, because tmux exports `$TMUX` to a `run-shell` child but never `$TMUX_PANE`; a catalog-wide test pins that rendering for every run-projmux action and runs each `create ...` binding body through the route to prove the argv the generated config ships is the argv the implicit-scope route accepts. No create path issues `switch-client`, `select-window`, `select-pane`, or `attach-session`.
288
- - `make test-integration`: Docker-backed Linux integration smoke with real `tmux`, `git`, and `stty`; covers Doctor schema 2 missing/corrupt/permission degradation, runtime socket/config current/stale findings against a guarded run-unique `-L` socket, source byte/mode/mtime/inventory no-repair evidence, explicit support-report no-request/preview/private archive and Doctor-schema inspection, missing/corrupt/permission-denied source manifest reasons with no source-mode repair, preview-writer failure before output creation, collision byte/mode preservation and temp cleanup, tmux config render/install/apply including stale live `C-t` pane-label retirement, current `M-p` preservation, current `C-t` reassignment, repeated apply/keymap byte preservation, and unrelated live/current binding preservation, default-off/on live resource config plus two-sample procfs status rendering, notify queue CRUD against isolated HOME/XDG paths, private operational-log persistence/mode repair/readback/hot-path exclusion/best-effort write failure, and the built-CLI saved-default split bridge reaching canonical intent scope: with `%7` carrying no Pane mirror it exits 2 with the exact origin-loss reason, writes nothing to stdout, issues only the read-only exact-Pane probe, and performs zero raw tmux mutations, where the retired bridge would have logged one `split-window`. The generated config is also audited for the CLI internal-namespace relocation: it must contain the `internal status`/`internal statusbar`/`internal tmux` spellings and no `'<bin>' <route>` invocation of any of the 7 removed pre-namespace plumbing tokens; invoking any removed token directly is separately pinned to the root unknown-command contract. Root containers run the permission case through `runuser`+`nobody`; environments without that adapter take an explicit asserted skip instead of treating root chmod access as denial.
289
- - `make test-install-smoke`: Docker-backed source install smoke; covers `make install`, atomic binary replacement, `config apply` against a live `projmux` socket, post-install notify queue initialization, the installed `~/.config/projmux/tmux.conf` carrying the relocated `internal status`/`internal statusbar` spellings and no pre-namespace `status`/`statusbar`/`tmux` invocation of the installed binary, and installed-binary temporary HOME/XDG support-report preview → explicit private archive → manifest/Doctor schema inspection with exact smoke-root cleanup.
290
- - `make test-e2e`: Docker-backed real-tmux workflow smoke for session/pane setup, a canonical `create pane -o pane-id` whose returned `%N` handle is verified against the isolated live server, an attached-client `rename-pane-label` command-prompt flow covering current-label initial value, Enter confirm, native Esc cancel, empty clear, and AI topic/manual/raw-title non-mutation, app config sourcing, reply-state notify reconciliation, focus notify fallback, canonical `internal status notify` rendering with contextual project/state/agent badges, and an isolated attached-client `display-popup` Settings recorder flow against a temporary HOME/keymap: one Add binding recorder saves a single C-r after one confirm, Esc cancels another draft with zero mutation, the same still-open reader records visible `C-o,o`, pops/re-adds its last stroke with Backspace, writes canonical `C-o o`, applies the live root-table trie, renders comma-form detail, and removes the sequence without stale tables; stale retired picker env/file values remain inert, unchanged, and silent. The reused FIFO/attached-client Notify Clear All flow has command markers and queue JSON proving cancel no-write, INFO/WARN/CRIT confirm clear, and pane focus restoration. Host-only terminal, WSL, macOS, and GUI notification behavior remains outside Docker; see [docs/testing.md](testing.md).
291
- - `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 7 splits Project discovery from pin authority. Unit coverage pins the typed pin envelope (`projmux-pins v2`, stored kind, corrupt/newer-version refusal, order-preserving dedup), the legacy resolution table (exactly one Registry root becomes that Project uid, zero matches stay a candidate, two or more refuse the whole migration and name the repair), the `candidates.MatchKeyFor` Linux/Windows compatibility table asserted from either host, a managed pin surviving rebind/rename/MissingRoot/no-root as the same sidebar tier, resolve-or-candidate typing of every `<dir>` argument with `uid:<uid>` as the explicit escape, write-free repeats and reads, the `create project --root` exact-path and idempotence contract, the sidebar bootstrap running after the trust gate and only for the path being opened, and a two-platform Settings golden showing Additional discovery roots, Pinned Projects, and Candidate Pins as three collections. Integration adds the real-file legs: a generic mutation route reconciling against a live server registers zero Projects for the adjacent scan-root children, an unregistered `--project <name>` refuses with the exact `--root` to pass, `pin project migrate --dry-run` writes nothing, an ambiguous legacy pin leaves both the pin file and the Registry byte-identical until a rebind repairs it, and four concurrent migrations converge on one valid typed envelope. E2E drives the user-visible flow on an exact socket with a real attached client: `switch open` on one candidate registers that one path, its siblings stay unregistered, reopening is Registry-write-free, and a rebind keeps the managed pin on the same uid with the candidate pin untouched.
292
- - `make test`: the Claude provider workspace/payload argv boundary is verified against the provider's own option grammar rather than substring containment, because a payload absorbed by a variadic option is still *contained* in the argv -- which is how the shipped `claude --add-dir <root> <payload>` regression passed Phase 6 review and then started sessions with no task. `internal/app/agent_launch_argv_test.go` replays each provider's documented arity over the produced argv (one-value option consumes exactly one operand; variadic option consumes operands until an option-looking token or the terminator; the terminator makes the rest operands) across Claude and Codex x zero/one/multiple additional roots x empty/one-token/many-token payloads, and requires the recovered roots, working root and prompt to equal what the launch was given, with zero undeclared options. A negative control replays the pre-fix concatenation and requires the model to *lose* the prompt, so the grammar replay cannot pass vacuously. Exact-argv tables pin `--add-dir <a> <b> -- <payload>` for Claude at every root count, `-C <cwd> --add-dir <a> --add-dir <b> <payload>` for Codex, a bare argv for an empty payload on both, and the refusal for a stored additional root on a provider projmux gives none to. `create agent` and `agent resume` are then run through the real seams and compared on the exact exec tail, which also pins that resume adds no terminator because the provider's `--resume` option is what stops the variadic root option; the former substring assertion in `agent_workspace_test.go` is now an exact-argv assertion. The one Codex payload shape that stays unsupported -- a prompt beginning with `-`, unchanged from the shipped build -- is pinned as a deliberate limit rather than left to be rediscovered. Measured against installed Claude Code 2.1.234 and Codex: `claude --add-dir DIR hello` reports "Input must be provided either through stdin or as a prompt argument" (the prompt became a directory) while `claude --add-dir A B -- hello` reaches execution with the prompt intact, and `claude --add-dir A B --resume <id>` parses both roots and the conversation id. The real-provider installed activation smoke -- installed `create agent --provider claude` with `--add-dir` plus a short payload on an isolated exact socket producing stdout `%N`, provider-hook activation acknowledgement and a transcript, with sibling and default sockets unread and exact cleanup -- runs post-merge and is recorded in the track's Archive evidence rather than in CI, because CI has no authenticated provider CLI.
293
-
294
- - `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 8 termination evidence transport binds a managed process's real exit to the Pane it ran in. `internal/core/metadata` adds the per-materialization `status.activation` generation and the optional `status.lastTermination` receipt on Pane and Agent, and its tests pin the closed classification vocabulary (exit 0 is `normal` and never `intentional`; non-zero and signal are `abnormal`), the generation guard across replaced generations, deleted Panes, a shell Pane claimed by an Agent and an Agent that no longer binds the Pane, the byte-identical duplicate no-op, intent recordable only by `source: control-action` and sticky against the signal a deliberate delete causes, the operation-id-scoped withdrawal that refuses to clear another operation's receipt, the generation-guarded runtime handle, and a registry stripped of both additive blocks that decodes, validates, re-encodes byte-identically, and rejects a receipt for a Pane that never carried a generation. `internal/app` adds the hidden `internal supervise` route: the argv builder terminates its own flags with `--` so an operator payload spelling `--pane-uid` cannot be re-read as one, a shell Pane with no command of its own is supervised over tmux's documented `default-command`/login-`default-shell` rule read from the same exact server, and every failure to construct a supervisor -- an unresolvable binary, an unreadable tmux default, a Pane with no generation -- returns the caller's original command so the pane still starts with no receipt rather than not starting. The route's tests pin the recorded wait status for clean exit, non-zero exit, and signal with the matching `128+signum` exit status, a registry that cannot be written costing the evidence and never the pane's own status, a stale receipt reported and dropped, a usage refusal that starts no child, and a launch failure reported as a launch failure rather than a termination. `runSupervisedChild` is exercised against real `sh` children for all three wait statuses and for argv/cwd/environment parity. Create, create-window, create-agent, `agent resume`, and the topology materializer each issue their own generation before the launch argv is built, and a route-level table proves every launched pane quotes the generation the Registry stored for it, that a fan-out never repeats one, and that a receipt from the process a resume replaced changes nothing. `delete window|pane|agent` gains `--socket`/`--socket-path` and now resolves exactly one server from those flags or the inherited absolute `$TMUX`, refusing outside tmux instead of falling back to `-L projmux` -- which supersedes the `-L projmux` inventory the Window and Pane/Agent deletion entries above describe -- and it commits its intentional receipt in its own transaction before the first live mutation, withdrawing it again on every refusal that leaves the processes running. The integration smoke records real shell exit 0, exit 7, and a signal death on an isolated exact socket and reads the receipts back out of an isolated Registry; the e2e smoke drives the same lifecycle against a real tmux server and proves an isolated `delete agent`/`delete pane` dry-run and apply touch only the socket they were given, leaving a sibling socket and the default socket unchanged.
295
- - `make test` / `make test-integration` / `make test-e2e`: Agent clean-exit and Continue replay policy Phase 0 records Project-stop provenance without changing replay. `TestProjectStopInterruptionPrewriteSelectsOnlyExactRunningAgentsAtomically` requires every and only then-Running Agent-owned Pane in the exact Project to receive matching generation/operation `interrupted/control-action` evidence in one Registry commit. `TestProjectStopFailureCompensatesOnlyWhenExactSessionIsProvedLive` pins same-operation compensation after a proved-live failure and retention after an apply error whose exact Session is proved absent; the existing external-HUP supervision rows remain `killed/supervisor`, and `TestInterruptedControlActionIsCurrentGenerationStickyAgainstLateSupervisorHUP` prevents a delayed HUP receipt from overwriting Project-stop authority. `TestCanonicalV2MigratesToV3WithoutInformationLoss` and `TestCanonicalV2ToV3PublishesOneLosslessBackupReportAndRepeatsNoop` pin the schema 2→3 lossless transform, exact versioned backup/report, one-time write, and newer-schema downgrade refusal. Describe renders classification/source/generation as separate fields and `get` includes all three in its termination cell. Continue planning, explicit resume, Open fresh, snapshot projection, provider argv, and public hooks remain unchanged in this producer-only Phase.
296
- - `make test` / `make test-integration` / `make test-e2e`: Continue recovery diagnostics Phase 1 is enforced by `TestTopologyRecoveryTypedReasonDecisionSites`, `TestTopologyRecoveryMixedReasonsReachJournal`, `TestTopologyRecoveryStartupCommittedSummaryAndJournal`, `TestTopologyRecoveryPublicPreviewRepeatAndLockReplan`, `TestTopologyRecoveryFailureNeverRecordsPlannedSuccess`, `TestTopologyRecoveryWriterDisplayFailureAndNoClient`, `TestTopologyRecoverySnapshotRetainsSeparateAuthority`, and `TestTopologyRecoveryReportArchiveIncludesSuccess`. The diagnostics package tests `TestTopologyRecorderCommittedCountsAndOnce`, `TestTopologyRecorderFailureAndInvalidCounts`, and `TestTopologyEventClosedShapes` hold the exact ten reason codes, same-run reason totals, count/event isolation, privacy, and writer failure ownership. Startup tests cover 0/1/9 committed resumed/skipped counts, nine long Korean names, en-US/ko-KR summaries within 220 valid UTF-8 bytes including ellipsis, retained stderr details, public log/report, and no foreign-server display without a client. `TestTopologyRecoveryCanonicalContinueReportsAfterCommit` exercises the canonical zero-Window/no-topology fallback through the real session transaction, requiring no precommit success and exactly one 0/0 success or commit-failure outcome; `TestTopologyRecoverySwitchDoesNotDuplicateMaterializerOrSnapshot` protects existing reporting ownership. L10/L11 retain verified same-run safe events and their exact bounded summary as unique recovery JSON artifacts. L10 reads public materialize results through diagnostics log after each evidence-matrix execution and repeat; L11 reads the actual startup invocation after external HUP, mixed normal/interrupted recovery, and nine missing refs, preserving explicit-resume/snapshot authority and live-open bypass with no duplicate event.
297
- - `make test` / `make test-integration` / `make test-e2e`: Retained-Project Continue resumes exact stored conversations after unplanned stops. `TestTopologyAgentContinueEligibilityMatrix` covers interrupted/killed/abnormal/unknown/no-receipt × Running/Offline/Failed/Pending with actual exact-resume or zero-launch assertions; intentional and normal remain excluded. `TestTopologyAgentContinueRejectsInvalidActivationEvidence` and `TestTopologyAgentContinueTerminationPairingAndShape` require legal receipt provenance and wait status, matching Agent/Pane evidence, current managed generation/owner, Running's exact paneRef, and Offline/Failed's unique retained activation when evidence is absent. `TestTopologyAgentContinueResumePreparationMatrix` fixes missing/blank/malformed/mismatched refs, normalized IDs, unsupported/disabled providers, missing cwd, and prepare errors at fresh 0 with a reason. `TestRegistryTopologyMaterializationReplaysStoredAgents`, `TestRegistryTopologyContinueLaunchesInterruptedBOnceAndRetainsCleanA`, and `TestClosedProjectStartupContinuesUnplannedStops` require independent shell/Window recovery, abnormal Failed recovery, retained excluded UID/ref/evidence, and repeat duplicate/Registry-write 0. `TestRegistryTopologyContinueRechecksEvidenceUnderLock`, existing owner-guard/binder rollback tests, `TestRegistryTopologyContinueKeepsUnresumableAgentAnchorRefusal`, and `TestRegistryTopologyMaterializationAgentAnchorLazyDefaultShellAndRepeatNoop` retain current ownership and rollback boundaries. `TestTopologyAgentSnapshotKeepsFreshFallback`, `TestExplicitSnapshotRestoreRetainsAgentReplayAuthority`, and `TestExplicitSnapshotRestoredOfflineAgentAnchorConvergesLazyShellAgentAndRepeatNoop` preserve explicit snapshot fresh fallback and exact resume. L10 requires raw Window/server HUP exact resume 1 and deterministic abnormal/Failed, unknown, absent-receipt, intentional/normal, and managed-stop ref-less cases; L11 requires startup HUP resume 1, clean launch 0, managed-stop exact resume 1, repeat 0, and independent explicit resume/snapshot/Fresh parity. Both smokes use isolated HOME/XDG/TMUX_TMPDIR, remove inherited tmux routing, and clean only queried contained sockets. Receipt producers/schema/sticky intent, quit/delete/stop, provider stores/transcripts, prompt/turn/approval replay, and public hooks are unchanged.
298
-
299
- - `make test` / `make test-integration`: the delete result names the socket the invocation resolved. Phase 8 made `delete window|pane|agent` resolve one exact server from `--socket`/`--socket-path` or the inherited `$TMUX`, but left the result line printing a constant `socket=-L/projmux`, so every delete against any other server reported the wrong target -- and that line is exactly what an operator audits after a destructive command. `explicitTmuxTarget.label()` renders the flag and value the route actually used, a table pins all three resolutions (explicit name, explicit absolute path, inherited client) and fails if the default app socket appears in a result that did not name it, and the isolated integration smoke asserts the reported socket is the run-unique one -- which is the whole assertion, because a fallback would print `-L/projmux` instead, and a separate "must not contain" check would collide with the isolated socket's own `projmux` prefix.
300
-
301
- - `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 9 exit reconciliation consumes a generation-safe termination receipt and one exact runtime absence in a single lifecycle transition. `internal/core/metadata` adds `ProjectTermination`, the one transition body, plus the `TerminationSourceReconcile` provenance, the `AgentExitUnknown` exit, and a distinct `status.reason` clause per classification. Its tests pin the whole matrix from the evidence side -- an explicit control action is `intentional`/`Offline`, a supervised exit 0 is `normal`/`Offline`, exit 42 and a signal are `abnormal`/`Failed`, and an absence with no receipt is `unknown`/`Offline` -- with the released Agent asserted on every row for a cleared `paneRef`, a preserved `status.sessionRef`, a mirrored receipt that survives the deleted Pane resource, and a registry that still validates. Idempotence is asserted as two independent properties: the absence-with-no-receipt case *records* an `unknown` receipt so a second pass finds it already stored, and running the projection again leaves the registry byte-identical for an agent Pane with a receipt, an agent Pane without one, and a shell Pane. The guards get their own cases: an event quoting a replaced generation leaves the current binding and the whole registry untouched; an Agent-owned Pane the Agent no longer binds after a resume receives the evidence and nothing else, with the resumed binding still `Running`; a shell Pane's runtime loss keeps the resource, adds `MissingRuntime`, and adds the evidence; a Pane with no activation generation at all -- every Project session's adopted first pane -- converges on `unknown` and stops being re-projected; recorded intent still wins over the signal a deliberate delete causes; and a nine-row pairing table plus a `Validate` leg prove `unknown` is writable only by `source: reconcile` and that the reconciler may write nothing else, so a hand-edited registry cannot bypass it. `internal/app` owns the dirty-event seam: a narrowed event reconciles exactly the Pane it names and leaves another dead Pane's evidence unwritten, a stale-generation event opens **zero** transactions, a repeat pass opens zero and reports why, and both trigger paths -- the reconciler's observation step and the one-shot -- are asserted to classify the same exit 42 into the same phase, reason, and preserved exit code, because they share one transition body. Exact-host containment is proven over the routed two-server fake for both `-L` and `-S`: the sibling mirrors the dead Pane's uid on a live pane of its own and reuses the same `%N` handle space, and the assertion is that the sibling receives zero calls while the event host's absence still decides the outcome. A forbidden-verb audit over the recorded argv proves the pass issues no `split-window`, `respawn-*`, `kill-*`, `send-keys`, `set-option`, or focus-moving call, so an observation can never become an activation; the core-layer half asserts no replacement Pane is materialized for an Offline Agent. Fail-closed is pinned for both the preflight and the in-lock observation, each leaving the registry byte-identical and returning no error. The read projection is asserted with zero writes throughout: `describe agent|pane` renders classification, source, observed instant, exit code or signal, Pane ref and generation as their own rows and renders none at all for a live resource, `get pane|agent` renders a `TERMINATION` column of `<classification>/<source> [exit=|signal=] <age>` that stays blank for a live row (the four whole-table goldens and the display-cell width suite move with the added column), the Registry-first navigation row carries a *copy* of the receipt and renders it in its own column, a navigation refresh is re-pinned as single-socket and mutation-free, and a pending intent receipt on a still-live Pane survives `get` in three output modes plus `describe` with the Agent left `Running`. The integration smoke reconciles real shell and provider exits on isolated exact sockets and reads the phases and receipts back out of an isolated Registry: exit 0, exit 42 and a signal for the three provider stubs, an external `kill-pane`, a `SIGKILL`ed supervisor (the one case that is deterministically `unknown`, from `source: reconcile`), a preserved shell Pane carrying `MissingRuntime` plus its evidence, a write-free repeat after every case, and a restart leg on both an app-owned and a standalone host whose assertions are deliberately narrow -- an unreadable server may not attract *reconciler* evidence even though the same pass honestly reprojects session liveness, `kill-server` SIGHUPs the panes so a surviving supervisor's real `abnormal signal=HUP` receipt is accepted alongside `unknown`, and the restarted server must end with zero mirrored pane uids because an observation is not an activation authority. A sibling server carrying the same mirrored Pane uid on a live pane of its own is byte-identical afterwards and changed no outcome. The e2e smoke proves the user-visible property the integration legs cannot: with the generated config installed on an exact socket, a provider exiting 42 reaches `Failed` and one exiting 0 reaches `Offline` **with no projmux command run to make it happen** -- convergence is polled through `describe`, which reconciles nothing -- and it repeats the read-projection, restart/no-autostart, and sibling-containment legs against a real server.
302
- - `make test` / `make test-e2e`: Registry-first control plane Phase 10 scopes an explicit singular reference to the active Project. A `metadata.name` is unique inside its owner scope and never across the Registry, so `projmux describe window zsh` typed inside a managed Project compared one word against every Project at once; the measured baseline on the installed binary was `--window zsh matched 9 windows, want exactly one`. `describe window|pane|agent <ref>` and `rename window|pane <ref>` now fill `selector.Query.DefaultRoot` with the active Window's Project owner, which is the same `windowScope` choice point the plural `get windows|panes|agents` default uses -- the two differ in which invocations opt in, and Phase 13 later widens only the plural default to both managed root kinds. `internal/app` pins the applied matrix in both directions: each of the five adopting `<verb, kind>` pairs resolves a name that also exists in another Project to the active Project's resource with exactly **one** observation, while `describe|rename project`, `rename agent`, `delete`, `rebind`, and `agent resume` stay whole-registry with **zero** observations, and an explicit `-p beta` resolves beta with **zero** observations. The narrowing is proven not to be a target rule: schema v4 scopes same-kind name uniqueness to the owning root, so a positional name inside one Project resolves the unique root-wide resource while a `--selector role=shell` label match over two Panes of that Project stays the bounded `matched 2 panes, want exactly one` ambiguity with no beta candidate in the listing rather than being broken by the active Window, and a `uid:` reference belonging to another Project is `matched no windows` rather than a cross-Project hit. Outside tmux every one of those argv shapes keeps its byte-identical whole-registry result and ambiguity. Inside tmux an underivable namespace is its own refusal -- `resolve project scope: a resource reference was given inside tmux and ...; the active Project namespace is undecidable, ...` -- pinned verbatim for a missing `@projmux_window_uid`, a mirrored Window uid the Registry does not hold, and a Window with no owning Project, each asserted to be a usage error with 0 bytes on stdout, 0 writes, and a byte-identical registry snapshot on the mutation half; it is deliberately not the active-target refusal, whose "no selector was given" opening would be false here. E2E L07 `binding-convergence` runs the same three shapes on both host modes against real tmux: the standalone inherited-socket client builds Window `ns-one` and Pane `shared` in alpha *and* beta, a second `role=shell` alpha Pane `sibling` under Window `ns-two`, and an alpha Window also named `shared` -- root-scoped uniqueness refuses a second alpha `pane/shared`, so the bounded ambiguity is reached through the shared label and the cross-kind coexistence witness replaces the duplicate name. It then proves the reproduction resolves one Window, `describe window shared` and `describe pane shared` each resolve their own exact uid inside the one root, the intra-Project ambiguity survives as `resolve pane: --selector role=shell matched 2 panes, want exactly one` listing `owner=project/alpha window/ns-one` and `owner=project/alpha window/ns-two` and never `project/beta` while the identical label outside tmux is `matched 3 panes` including `owner=project/beta window/ns-one`, `-w` narrows that same label match to one Pane per Window and `-p beta` reaches beta's `shared`, a foreign uid is a no-match, `rename window` moves only the active Project's Window, and unsetting the live `@projmux_window_uid` refuses instead of widening; the app-owned `-L projmux` host repeats the reference/explicit-`--project`/foreign-Window trio read-only against its own fixture. Singular `uid:` namespace semantics, `delete`, and the empty-selector active target remain unchanged.
303
- - `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 11 lifecycle trigger convergence and legacy seam retirement. **One entrypoint.** `internal tmux reconcile-bindings` and `internal tmux release-dead-agent-panes` are retired and replaced by one hidden route, `internal tmux converge --socket-path <path> --session <id> --reason <config-apply|runtime-created|runtime-exited>`, which `config apply` and all four generated lifecycle hooks reach; the reason set is one closed enum the route parser and the hook renderer both read, so a spelling a config can emit is one the binary accepts, and a malformed target, a missing reason, an unknown reason, and a positional argument each refuse with the controller reached zero times. A trigger carries no pane id and no generation on purpose -- `after-kill-pane` fires with an empty `#{hook_pane}` -- and the trigger-inventory test reads both generated configs plus the live `show-hooks -g` output to prove every hook reaches the entrypoint, that the creation hooks stay synchronous while the exit hooks stay backgrounded, and that neither retired route survives anywhere. **One pass.** A pass is one locked reconciliation followed by an exit-half reobservation, in that order: the projection has to run after the same pass's binding writes or it files an unknown termination against every Pane the pass was about to bind, which a stage-order test states directly. `config apply` still converges only after a successful `source-file`, on the exact `-L` target it reloaded, with no hook session because an apply is not caused by a create; `--no-reload` and every preflight failure remain strict no-touch paths. **At most one worker.** The dirty-event log is one file per event under `<state>/projmux/controller/<key>.events/`, so marking needs no lock and no counter can be lost, and the worker lease is an advisory whole-file `flock` the kernel releases on process death -- an abandoned lock file is acquirable immediately, which is what makes crash recovery need no staleness rule. Coalescing is measured rather than described: eight producers arriving during one pass cost two convergence bodies and one worker, every loser exits zero with its event still unacknowledged, the loop repeats until a pass writes nothing and nothing is pending, and the repeat bound stops a producer that marks faster than a pass completes without claiming convergence. The e2e burst additionally proves no `acquire lock: exhausted` appears anywhere, which is the failure mode the pre-Phase fleet of per-event workers produced, and that a `get` read starts no worker and leaves no event or lease behind. Two spellings of one server get two keys and therefore two leases, and a sibling socket is never blocked by another's. **Split UI convergence.** `internal agent-pane launch-default` and the `Alt-7`/resume pickers produce a canonical create intent -- provider, placement, and for a resume the provider-normalized conversation id -- rendered as the exact argv an operator would type, so the projection is asserted as a pure function rather than through whatever the create did next; the resume branch has no public spelling and reaches the shared Agent body with one extra field, which keeps `create` free of a public `--resume`. The saved mode is the only hidden state the UI reads, a mode naming no launch opens its popup, and a saved or selected provider Settings has disabled refuses before the intent exists with zero mutations. The legacy `ai split` route, its `--agent`/`--force-agent`/`--print-pane-id` flags, the layout/context/title-watcher helpers only it used, and `mux.SplitWindow` itself are all deleted, and a source audit over `internal/app` proves the materializer is the only place a `split-window` argv exists -- tmux's own pane-context-menu entries excepted, which are menu items rather than a projmux-issued split.
304
- - `make test` / `make test-integration` / `make test-e2e`: Popup-origin split anchor. A `display-popup -E` job inherits `$TMUX` and no `$TMUX_PANE`, so after the split UI moved onto the canonical create intent every terminal action of the two split pickers -- provider row, `shell` row, resume row, and the resume picker's `new` row -- reached `create` with nothing to derive a scope from and refused with the `--project` usage error (exit 2). **The origin pane travels on the intent.** `agentPaneIntent.anchorPaneID` carries the pane the keypress came from, `createCommand.withAnchorPane` makes it that invocation's active target through `anchoredActiveTargetLookup`, and Project/Window/anchor resolve through the same identity mirror a pane-hosted invocation reads. Named regressions fix both halves in one table: an anchored intent with no `$TMUX_PANE` creates exactly one Pane in the origin pane's Window, while an anchor-free intent, a whitespace anchor, an anchor that leaked outside any client, and an anchor carrying no managed identity all keep the exact refusal and exit 2 with zero Registry writes and zero tmux calls. A typed `create pane|window|agent` inside the same popup runtime -- origin pane present in the environment -- keeps its refusal byte-for-byte, the pane-hosted `launch-default` producer states no anchor and probes nothing, and a source audit pins `TMUX_SPLIT_TARGET_PANE` to one reader in the split UI so it cannot become an implicit scope for the read, rename, or delete verbs.
305
-
306
- - `make test` / `make test-integration` / `make test-e2e`: Registry-first control plane Phase 0 control-session resource slice. The app-owned Home session becomes a Registry **owner** of Windows and Panes without becoming a Project. `internal/core/metadata` unit and table tests pin the fifth kind `ControlSession` in the closed `Kinds()` set and its declaration order, the `ctl-` uid prefix through `NewUID`/`UIDKind` with no prefix collision against the other four kinds, and the widened `requireOwner` **allowed set** -- a Window may be owned by a Project or a ControlSession, and the refusal now names both (`ownerRef kind "Pane" is not "Project" or "ControlSession"`) while every single-kind refusal (`Pane` under an `Agent`, an `Agent` under a `Project`, a `Project` carrying an `ownerRef`, a missing `ownerRef`, an `ownerRef` that does not exist) keeps its wording byte-for-byte. A reflect test pins that `ControlSessionSpec` carries **no path field at all**, which is the compile-time reason `$HOME` can never reach managed roots, trust, rebind, or cwd defaults. `BindControlSession` is proved convergent in the same table: a first pass mints the ControlSession plus one Window and Pane and creates zero Projects and zero Agents; a second pass over live objects carrying the allocated uids rebinds with a byte-identical registry; an unmirrored live window **adopts** the existing unbound Window instead of duplicating it (the mirror-write-failed repair); a live uid this Registry does not know mints a fresh resource rather than reusing the foreign one; a uid that belongs to a Project's scope is refused outright and left untouched; a mid-transaction failure rolls back every resource and reservation. A pre-Phase-0 registry document decodes, classifies as `SchemaCurrent`, validates, and re-encodes without the `controlSessions` key -- **`schemaVersion` stays 1 and `productionMigrations` stays empty**. `internal/app` covers the writer: the `projmux shell` entry provisions the app session with `has-session` followed by a bare `new-session -d`, and only then converges. It is deliberately **not** `new-session -A -d`: measured on tmux, `-A` against an existing session turns the create into an attach that `-d` does not suppress (`open terminal failed: not a terminal` outside a terminal, a seized client inside one), so the already-live backfill this preflight exists for would never run -- a table test therefore rejects any `-A` anywhere in the preflight argv, and the probe/create argv pair is asserted in order. A five-row table pins every answer the probe reads as absent, including the socket-level `error connecting to <path> (No such file or directory)` a real run measured on a socket whose file does not exist yet, while an unclassified probe failure such as `permission denied` stays fail-closed: nothing is created and the pass does not run. A live session is probed and left exactly as it was found, a `duplicate session` race is read as the postcondition already holding, the foreground attach argv stays byte-identical to the pre-marker contract, a Project-default target gets **no** preflight and **no** marker, and a failed provision or convergence prints one warning and still attaches. The converger writes `@projmux_session_role=control` once, on one exact `-t <session>` target with no `-g` and no pattern, routes every tmux call through the invocation's explicit `-L <socket>`, and mirrors `@projmux_window_uid` / `@projmux_window_name` / `@projmux_pane_uid` / `@projmux_pane_label` onto the exact live objects it bound; a repeat pass writes zero registry bytes and re-mirrors no already-bound uid. Fail-closed rows pin that a server without `@projmux_app=1`, a hand-set app-marker value, and a session carrying `@projmux_ephemeral=1` each skip with zero transactions and zero option writes. The parity audit compares `get projects` against the same registry with and without a control session (identical output, no `home` and no `ctl-` uid anywhere), lists the control-owned Window and Pane through `get windows -A` / `get panes -A`, resolves `describe pane` with no selector inside the control session, and pins the **next-phase-encroachment guard**: `create`/split inside Home still refuses with the `--project` usage error and exit 2, with both the pre-Phase-0 detail clause (`carries no @projmux_window_uid`) and the post-Phase-0 one (`resolves to window "window", which has no owning Project in the registry`) asserted verbatim so the documented wording change cannot drift.
307
- - `make test`: Control-owner reconcile commit projection. `projmux reconcile resources` (execute) aborted at the Registry commit with `validate registry: Window name reservation "window" refers to unknown uid "win-..."` on any machine whose Home session had become a `ControlSession` owner, so `--dry-run` planned cleanly while every execute rolled back and repaired nothing -- including the `@projmux_project_uid` / `@projmux_project_name` session mirrors another recovery path reads. **The defect was the projection, not the file.** `mergeScopedResourceRegistry` rebuilt Windows/Panes/Agents from `retained` (the graph of every *other* Project) plus `changed` (the graph of the reconciled Projects), and a Window a ControlSession owns is in neither, so it was dropped while `out.NameReservations = scopedAfter.NameReservations` put its reservation back. `retained` now comes from `resourceRegistryWithoutProjectGraphs`, a removal rather than an inclusion, so a resource the removal set does not reach is carried through untouched; the two are exact complements over a well-formed registry, which is why a Project-only registry projects byte-for-byte as before. Two further invariants are pinned rather than described: `resourceRegistryProjectGraph` claims the reservations of the ControlSession roots `Clone()` brings along, so the scope projection can no longer hold a root `Validate` rejects from the other direction (`ControlSession %q has no name reservation in scope`); and the commit projection is filtered through `retainReservedResourceNames`, which drops any reservation naming no resource it carried -- a table row over a scope whose Project stopped resolving between the two halves shows that failure was reachable for a Project-only registry too, before a ControlSession existed. The table runs {with control-owned resources, without} x {`resourceRegistryProjectGraph`, `scopeResourceRegistry`, `mergeScopedResourceRegistry`, a vanished scope}, asserting the committed projection passes `Validate` outright while the two allocator scopes -- which deliberately carry out-of-scope reservations as allocator input -- may never carry a resource the reservation table does not name. A round-trip test proves the ControlSession, its Window, and its Pane come through a no-op pass with the same uid, name, owner, and reservation, and that a second pass is a fixed point. The public route is covered end to end: with a live Home control session carrying the control role and mirrored Window/Pane uids, execute exits 0, commits a `Validate`-clean registry, writes both session option mirrors, leaves the control graph byte-identical, agrees with `--dry-run` on refused drift, and writes zero registry transactions on a repeat pass. The refuse vocabulary is a three-row table: a control-owned Window sitting in the session its own ControlSession is bound to is converged and is **not listed** at all, the same Window observed anywhere else is refused as `live Window uid is outside the exact ControlSession owner scope`, and a Project-owned Window outside its Project keeps `live Window uid is outside the exact Project owner scope` byte-for-byte.
308
- - `make test`: Root kind projection sweep. C-5 says a Registry has two root kinds, `Project` and `ControlSession`, and that every projection knows both; `#705` paid that cost for exactly one traversal by inspection, and nothing said how many others there were. `internal/app` now carries the sweep as an executable table: every non-test function that ranges over a root slice is classified `both-kinds`, `paired` (a kind-scoped lookup whose ControlSession half is a named sibling), `project-only` (Project semantics -- a root path, a Project session claim, a Project pin -- where a ControlSession is structurally absent rather than forgotten), or `gap`, and the site set is **re-derived from the source with `go/ast` on every run**, so a traversal added, removed, moved, or renamed fails the test until it is classified. Each verdict is machine-checked against the same scan: a `both-kinds` site must range over `.ControlSessions` in the same function, a `paired` site must name a sibling that does, and a `project-only` or `gap` site must not. Every function that visits `.ControlSessions` must appear in the table as a site or as a named pair, so no root traversal can hide from the sweep in either direction. A second test asserts zero `gap` rows inside the Phase 4 boundary and logs the ones outside it with their exact path and owner; the whole table renders as a fixed-width printable report. The one gap inside the boundary is closed: `registryResourceRecords`, the flat record projection behind `registryUIDSet` and `registryReconcileItems`, read Project as the only root, so a ControlSession uid answered "not a resource projmux owns" to the foreign-drift check and an appearing or changed control root produced no plan item at all. It now carries both kinds, pinned behaviourally: the uid set contains the control root, an appearing ControlSession produces exactly one `missing`/`create` item targeting `controlsession/<name>`, and an unchanged Registry still produces none. The four routes C-5 Guarantee has to survive are green on one control-owned fixture seeded through the real `BindControlSession` -- `reconcile resources` execute, `prune project --missing --older-than --yes`, the pin authority (selection, rows, discovery paths, legacy resolution), and `--materialize-project` dry-run plus execute -- each asserting the Registry still validates and the ControlSession, its Window, its Pane, and all three name reservations come through untouched. The pin route additionally pins C-5's Non-Guarantee as behaviour: the control root never reaches the resolver as a `ProjectRef`, never reads as a pinned Project, and a legacy path pin naming the control session's own Pane cwd stays a candidate instead of being typed onto anything.
309
- - `make test`: Declarative contract stabilization Phase 7 divergence taxonomy. `TestDivergenceTaxonomyTableIsClosedAndPrintable` pins the closed printable D1--D6 label set; `TestDivergenceClassifierCoversEveryRegistryInventoryCombination` and the named property test `FuzzDivergenceClassifierLabelsEveryRegistryInventoryItem` prove every item produced for arbitrary Registry/runtime Inventory pairs has exactly one valid label and a non-empty separate reason; `TestIncident20260820ClassifiesExactlyTwoOrphanMirrors` fixes the incident fixture at exactly two D3 orphan mirrors; and `TestResourceReconcilePlanItemsHaveExactlyOneDivergenceAndReason` covers every plan/refusal emitter. `TestControlSessionRootPreservesRuntimeAndDescendantAttribution`, `TestControlOwnedHomeGraphSurvivesSidebarRuntimeAndSessionAttribution`, `TestRootKindProjectionSitesAreExhaustivelyClassified`, and `TestRootKindProjectionSweepLeavesNoGapInsideThePhase4Boundary` close the resolver/index, node, Registry view, runtime diagnostics, offline-row, and session-attribution root-kind gaps while preserving the control-owned Home graph. Doctor and dry-run goldens pin all six counts, and the support-report redaction test permits only aggregate counts -- no reasons, paths, or identifiers.
310
-
311
-
312
- - `make test` / `make test-integration`: First-open Project identity convergence. Opening an unregistered directory mints an exact Registry Project UID and sends session creation, initial Window/Pane adoption, Project identity, lease, and startup finalization through the canonical materializer transaction on one explicit app route before moving the client. There is no reduced `EnsureSession`/later-mirror fallback: a missing UID, tmux runner, or canonical transaction seam refuses with zero runtime writes, and a failed stage rolls back only exact handles owned by that operation. An already-registered Project still converges through the Registry topology engine; a Home row carries no Project UID and therefore cannot authorize managed Project creation. The two-action startup screen uses the same transaction for `Continue project` and requires canonical identity before `Open fresh`. End-to-end coverage pins unregistered directory → one Project → one fully attributed session → subsequent managed Window/Agent creation, while `preflightSessionOwnership` continues to refuse blank, foreign, and duplicate-root claims with zero Registry/tmux mutation. The real-tmux smoke uses an isolated unique logical socket and `TMUX_TMPDIR`, removes inherited `TMUX`/`TMUX_PANE`, verifies app/logical markers and exact `#{socket_path}`, and cleans up only that observed socket.
313
-
314
- - `make test`: Declarative contract stabilization Phase 2 adds the Registry materialization invariant audit to `projmux doctor` as the `registry` section, present in the default report and in `--section registry`, in both text and `--json`. The audit is the consumer predicate rather than a second opinion about it: every Registry Project is planned through the shipped `planRegistryTopology` with no observed sessions, and `refusalScope` supplies the fatal/skipped split, so `TestDoctorRegistryInvariantAuditReusesTheMaterializePredicate` fails if the reported counts ever diverge from the planner's own refusals over the same Registry. After schema v2, `TestDoctorRegistryInvariantAuditReportsTheFieldFailureStructure` proves deleting a Window's last shell installs a canonical replacement and therefore emits no skipped-Window finding; the remaining gone-root Project is still reported as `registry.materialize.fatal.project`. `TestDoctorRegistryInvariantAuditStatesTheZeroCase` requires a clean Registry to report `registry.materialize.clean` and the number of Projects planned without `--verbose`, because a silent clean audit is indistinguishable from a section that never ran, and `TestDoctorRegistryInvariantAuditGoldens` pins the zero and N section output in text and JSON. `TestDoctorRegistryInvariantAuditWritesNothing` runs the production zero-write snapshot read under an isolated `HOME`/`XDG_STATE_HOME`/`XDG_CONFIG_HOME` on a machine that never created a Project and requires an unchanged filesystem tree and no state directory; `TestDoctorRegistryInvariantAuditNeverReachesATmuxServer` pins that the runner handed to the planner refuses every call, and `TestDoctorRegistryInvariantAuditDegradesWhenTheRegistryCannotBeRead` keeps an unreadable Registry a `registry.materialize.unavailable` finding rather than a failed diagnostic. `TestSupportReportCarriesRegistryInvariantCountsWithoutReasonsOrPaths` requires the archive's `doctor.json` to carry the same codes, kinds, and counts and neither the planner's reason wording nor the fixture's absolute paths, and `TestDoctorRegistryInvariantCodesStayInsideThePublishedInventory` keeps every emitted code and remediation inside `doctorFindingCodeInventory`/`doctorFindingRemediationInventory` so nothing reaches a support archive as an opaque hash. The audit performs no repair, schema migration, or display-layer diagnosis, and adds no second diagnostic command.
315
- - `make test` / `make test-integration` / `make test-e2e`: Declarative contract stabilization Phase 3 raises the Registry envelope to schema v2 and ships the atomic v1 → v2 invariant repair. `TestRegistryV2GenerationMigrationGoldens` labels the 0.10/0.11/0.12 source generations and pins raw SHA-256 bytes for every source/output pair while comparing generated output directly to the raw golden bytes and asserting repair and information-loss counts, identity/name/owner/name-reservation preservation, Agent conversation-pointer preservation, graph validity, and a no-op second pass. `TestV1MigrationCreatesCanonicalShellChainForAZeroWindowProject` covers additive opaque Window/Pane creation, and `TestValidV2CanonicalAnchorRoundTripsWithZeroDiff` fixes a valid anchor at zero diff. `TestEveryFirstMigratorLeavesDurableV012RepairAndLossEvidenceOnce` drives normal `Load`, `Update`, and explicit `Migrate` as the first writer and requires one private report adjacent to the exact versioned backup, with that backup's absolute path/SHA-256 and exact repair/loss detail; returned paths match the durable pair where the API returns evidence, and the next migration pass writes zero Registry, backup, or report bytes. Failure-injection rows require a report published before Registry staging to be removed on every failed ordering. `TestMigrateRejectsAnInvalidCurrentRegistryWithZeroWrite` prevents a current-version no-op from bypassing validation, and future/unversioned envelopes remain byte-identical refusals with no evidence report. `TestV012MigrationLeavesNoMaterializeRefusals` runs the shipped materialize predicate over the repaired 0.12 sample and requires zero fatal and zero skipped items plus the exact Project → Window → direct shell Pane chain. Current-schema validation globally enforces that chain, and last-shell deletion installs a replacement bare shell. Phase 15 consumes this canonical anchor as the only identity retained by `Open fresh`; schema-v2 validation remains the pre-commit refusal boundary for an invalid anchor.
316
- - `make test` / `make test-e2e`: Declarative contract stabilization Phase 5 moves the generated `MouseDown3Pane` Horizontal/Vertical Split and Kill entries onto managed resource intents. The generated-config byte golden fixes the exact `internal tmux pane-menu --client #{client_tty} <action> #{pane_id}` actions, and a negative audit requires zero `split-window`, `respawn-pane`, and `Respawn` literals in that menu (since Orphaned Registry Window Phase 3 its one `kill-pane` is tmux's stock item as the mirror-absent branch of Kill). Unit tables prove both split directions emit one `agentPaneIntent` with the exact clicked `%N` in `anchorPaneID`, so `createFromIntent` and the popup-origin `anchoredActiveTargetLookup` remain the only scope and mutation definitions; Kill resolves through the same anchor and calls canonical `delete pane`, preserves its full output, and displays its summary on the originating client. Injected create and delete refusals are displayed on that client and trigger no fallback mutation. Respawn is removed from the generated menu and its internal action is rejected; no managed respawn/replace intent is introduced. The isolated real-tmux leg strips inherited `TMUX`/`TMUX_PANE`, uses a run-unique `TMUX_TMPDIR` and socket, audits the installed `MouseDown3Pane` binding and selects Split/Kill actions through a real `display-menu` on an attached client, verifies split uid/Registry visibility and canonical Kill removal/result, and validates then records the exact socket cleanup target under its smoke root.
317
- - `make test` / `make test-integration` / `make test-e2e`: Declarative contract stabilization Phase 6 makes termination evidence survive the exit boundary without taking the Registry lock. The supervisor writes one bounded JSON row, framed by leading and trailing delimiters in a single `O_APPEND` call to `termination-receipts.jsonl`; the leading delimiter terminates any prior process's partial tail. Controller convergence reads the append snapshot outside the Registry transaction, absorbs generation-guarded receipts before absence projection, tolerates malformed/partial rows without disabling later convergence, and relies on duplicate/stale receipts as no-ops rather than truncating or garbage-collecting the journal. `TestSupervisorReceiptPrewriteDoesNotWaitForRegistryLock` proves the prewrite never enters a deliberately blocked Registry update, `TestMalformedTerminationJournalRowCannotDisableConvergence` pins recovery from a truly unterminated tail, and `TestInvalidTerminationJournalRowCannotDisableLaterValidReceipt` proves a well-formed but contract-invalid row cannot poison later valid evidence. The generated hooks use three distinct reasons: `pane-exited` carries exact `#{hook_pane}` and reobserves only the matching stored activation handle; `pane-killed` carries no invented pane and retains whole-host reobservation; `window-unlinked` carries exact `#{hook_window}` as explicit event evidence while retaining whole-host fresh reobservation. Phase 6 had no persisted runtime/liveness mapping; Phase 3 now persists only the exact last-positive Window owner handles required for causal teardown. `TestLifecycleHookRoutesCarryExactPaneAndWindowEvidence` and `TestWindowUnlinkedRecordsKillEvidenceWithoutPersistedRuntimeState` pin the Phase 6 boundaries and kill-window evidence recording. `TestPaneKilledCoalescedBehindExactPaneExitWidensEveryLaterPass` pins that a narrow pane-exited lease holder widens after any coalesced event and stays whole-host through its final no-op verification, so a kill or different-pane event cannot be acknowledged under the first Pane's selector. `TestPaneExitedAbsorbsLateSupervisorReceiptAfterUnknownProjection` covers the fast-exit ordering where runtime-created first records unknown: exact pane-exited still treats later journal absorption as work, refines the released same-generation Agent's evidence to normal or killed, and repeats with zero transactions; `TestLateSupervisorRefinementNeverFollowsAResumedBinding` keeps a new non-empty Pane binding strictly outside that exception. The closed classification vocabulary adds `killed`: only a supervisor's SIGHUP wait status may claim it, it means external kill rather than intent, and a same-generation `control-action/intentional` receipt remains sticky when the resulting HUP arrives. `TestHookWaitStatusReceiptDecisionTableIsClosed` covers pane exit, pane kill, standalone window kill, last-pane clean exit, paired control receipt, and evidence-free absence; `TestTerminationPairingRejectsUnentitledClaims` proves supervisor cannot claim intent, control action cannot claim observed outcomes, and reconcile can claim only unknown. `FuzzTerminationEventSequencePreservesCanonicalDeleteAndHookRows` fixes the authority boundary over arbitrary event order: canonical delete removes the Pane row and every repeat is byte-identical, while hook-driven observation changes evidence/status, clears the Agent binding, preserves Pane/Agent rows, and a repeated reconciliation opens zero transactions.
318
-
319
- - `make test`: Phase 6 kill/reap race enforcement keeps external-kill evidence from settling as unknown merely because tmux's 50ms hook beat the supervisor append. `TestPaneKilledWaitsPastHookDelayForSupervisorReceipt` delays the SIGHUP receipt past 50ms and requires killed evidence; `TestPaneKilledWaitRefinesRacingUnknownWithDelayedSupervisorReceipt` starts from a racing reconcile/unknown and requires the same killed refinement. `TestPaneKilledReceiptWaitIsBoundedBeforeUnknownProjection` proves a genuinely receipt-free kill waits only for the fixed bound and then remains honest unknown. Phase 2 extends the same lock-free bounded wait to the exact `pane-exited` activation only: `TestExactPaneExitedReceiptWaitIsBoundedBeforeUnknownProjection` proves a receipt-free timeout still retains the resource with honest unknown evidence, while integration/e2e prove a racing same-generation normal supervisor row enables deletion. The wait reads only the exact-host inventory, Registry snapshot, and lock-free journal before convergence; it takes no Registry write lock, invents no intent, keeps exact pane exit narrowed to its `%N`, and leaves kill/window paths whole-host. At the process boundary, `TestRunSupervisedChildRetainsRelayedHUPEvidenceWhenChildExits129` proves a provider that catches a successfully relayed HUP and exits 129 still records external-kill evidence, while `TestRunSupervisedChildDoesNotInferHUPFromExit129` proves the same numeric exit without an observed relay remains abnormal.
320
- - `make test` / `make test-integration` / `make test-e2e`: Runtime topology exit cascade Phase 2 promotes only one exact positive event to Registry delete authority. Generated `pane-exited` carries tmux's `%N` Pane and `@N` Window; preflight and locked revalidation both resolve that Window UID and the current Pane activation/owner chain on the same exact socket. A same-generation `normal/supervisor` row must already exist in the lock-free termination journal before `PlanPaneAgentCascadeDelete` removes the managed Pane while retaining its Agent Offline with `sessionRef` intact and `paneRef` empty. `TestPaneAgentCascadeDeletePlanReleasesPaneAndRetainsAgentWindow`, `TestExactCleanShellAndProviderExitCascadeRowsAfterJournalEvidence`, and `TestExactPaneExitReceiptPermutationsConvergeToOneIdempotentPlan` pin clean shell/provider parity, journal survival, duplicate/permutation idempotence, and Window/sibling preservation. `TestExactPaneExitNegativeAuthorityRowsProduceDeletePlanZero`, `TestWindowUnlinkAloneAndAdversarialObservationsDeleteNothing`, and `TestLatePaneExitNeverFollowsAResumedGeneration` pin delete-plan zero for abnormal, killed, unknown, empty/unavailable/foreign observation and stale/resumed generations. Integration drives clean shell, Claude, and Codex outcomes against an isolated exact socket and reads the pre-delete journal after the rows disappear; e2e closes one of several Panes and proves the sibling runtime, Agent, Window, Registry graph, and sibling socket remain. This path never consumes `window-unlinked`, deletes a Window/Project, changes root/pin/reopen state, or parses `/exit`, pane content, prompt, history, or transcript.
321
-
322
- - `make test` / `make test-integration` / `make test-e2e`: Declarative contract stabilization Phase 8 recovery authority. `TestRecoveryLadderL0ThroughL10IsClosedLossOrderedAndPrintable` and `TestRecoveryAuthorityClassificationTriggerTableIsClosedAndPrintable` pin the printable closed L0--L10 ladder and every D1--D6 x hook-converge/explicit-reconcile/Project-open cell; `TestPropertyEveryClassificationTriggerAutomaticAllowanceIsExact`, `TestAutomaticRecoveryProducerInventoryIsClosedAndEveryPathIsAuthorized`, and `TestAutomaticRecoveryCallsitesCannotBypassTheProductionAuthorityGate` keep automatic producers inside the exact non-ordinal table. `TestIncident20260820ExactD3AutomaticallyRunsL8WhileL7RequiresApproval`, `TestPublicReconcileD3L8PreservesAuthoritativeRegistryAndAllowsFollowUpWrite`, `TestIncident20260820DryRunDisclosesExactApprovedL7CommandAndLoss`, `TestApprovedL7ImportsExactD3OnlyAndLeavesBlankD2Unattributed`, and `TestApprovedL7StillRefusesD4Contamination` pin exact D3 classification, automatic L8 mirror/routing-index discard, authoritative Project graph and Agent session-ref preservation through the public planner/merge boundary, a usable follow-up Registry write, and the separate `--import-orphan-mirrors --yes` L7 approval path with `identity-provenance` kind and exact object count. `TestLockedAutomaticRecoveryReclassifiesConcurrentRegistryClaimBeforeL8`, `TestAutomaticRecoveryNeverDeletesConversationSnapshotOrRegistryRow`, and the incident's managed Agent session-ref sentinel prove all production triggers reclassify under the Registry lock and automatic recovery preserves Registry rows, conversation pointers, snapshots (which are unreachable from the L8 execution seam), provider, and topic. Default hook, explicit reconcile, and Project open never import D2, including anchored legacy state; materialization is authorized as Project-open automatic L4/L5 or explicit approved L4/L5 without changing snapshot restore or Open-fresh semantics.
323
- - `make test` / `make test-e2e`: Declarative contract stabilization Phase 9 degraded Registry mode. `TestDegradedModeEntersOnlyForAnIllegalRegistry` closes the state table: only `valid` and legitimate `first-use` permit ordinary mutation, while initialized missing/empty, malformed, unsupported/newer schema, graph-invalid, and unreadable Registries enter degraded mode. `TestOrdinaryUpdateCannotReplaceAnInvalidRegistry` pins the lock-free ordinary-write gate, preserves the original typed validation cause, and requires the refusal to end in exact `projmux reconcile registry --dry-run` guidance instead of exposing a raw graph error. `TestRepairDoesNotReuseOrdinaryWriteLockOrValidation` holds `registry.json.lock`, injects failure into the ordinary staged-validation hook, and proves explicitly sourced restore proceeds through its distinct `registry.json.repair.lock` plus source/staged/current verification. `TestIllegalRegistryFixtureKeepsReadDiagnosisAndRepairAvailable` drives the checked-in dangling-reservation fixture through production paths: `get projects` remains readable, recovery dry-run diagnoses and prints a checksum-guarded exact restore command, `create project` is byte-preserving and refused before the ordinary lock, explicit repair succeeds, and the same create succeeds afterwards. The existing isolated create e2e now pins a persisted stale `anchorPaneRef` as degraded exit 1 with the exact repair-plan command, zero Registry/runtime mutations, and zero stdout; its inherited `TMUX`/`TMUX_PANE` removal, run-unique socket, smoke-root socket-path validation, and exact cleanup target remain unchanged. The repair command never automatically selects or reconstructs a Registry source and does not widen the Phase 8 authority ladder or loss disclosure.
324
-
325
- - `make test` / `make test-integration` / `make test-e2e`: Declarative contract stabilization Phase 11 control-root identity convergence. `PlanControlTargetConvergence` is the pure closed plan for one exact app socket/session declaration or an already-recorded ControlSession identity; its input enumerates Registry-root, session-role, Window uid, and Pane uid presence without cwd, display-name, command, or `ProjectByRoot` evidence. `FuzzControlTargetConvergenceIsClosedAndIdempotent` seeds all 16 partial-state combinations and requires every input to converge or record one exact refusal, with every allowed first plan producing an empty second plan. App tests execute the installed-Home repair shapes (root only missing, mirrors only missing, and root/role/mirrors all missing), prove the second pass issues zero Registry and tmux writes, and pin zero-write reasons for foreign claimants, duplicate claimants, and Project uid conflicts. Canonical `projmux shell` and successful `config apply` declare the exact target; generated lifecycle triggers continue only exact ControlSession identities already in the Registry, so an already-live Home repairs after install/apply without a one-shot migration or a raw tmux fallback. The smoke contract unsets inherited `TMUX`/`TMUX_PANE`, uses one run-unique `-L` socket under a dedicated `TMUX_TMPDIR`, records the exact socket path, and verifies that cleanup targets remain inside that smoke root.
326
-
327
- - `make test` / `make test-integration` / `make test-e2e`: Declarative contract stabilization Phase 12 closes canonical UI create over pane-menu, saved-default, provider-picker, resume-picker, direct-provider, and direct-shell producers for both Project and ControlSession roots. `TestCanonicalCreateProducerRootOutcomeTable` executes every producer x root x created cell and rejects inventory gaps, while `FuzzCanonicalUIIntentStaysManaged` requires every generated success to add exactly one mirrored managed Pane below the exact origin Window (and an Agent owner where applicable). Scope is proved only by the Phase 11 Pane uid -> Window uid -> Project-or-ControlSession uid chain plus the exact ControlSession declaration/app/root-role mirrors; cwd remains launch workspace and never root identity. `TestCanonicalCreateConflictAndLostAnchorHaveZeroWritesAndExactReason`, `TestCanonicalProducerNegativeAuditHasZeroHiddenStderr`, `TestPopupCanonicalFailureIsProjectedToExactOriginatingClient`, and the pane-menu refusal fixtures pin zero Registry/tmux writes, no exit-code-hidden reason, and exact client-scoped projection including route stderr. `TestHomePopupOriginSplitResumeDefaultAndShellUseCanonicalCreate` and `TestHomePaneMenuSplitUsesCanonicalCreate` cover the Home popup provider, resume, saved default, shell, and pane-menu paths, while `TestOnlyTheMaterializerRunsSplitWindow` keeps raw producer mutation/fallback at zero.
328
-
329
- - `make test` / `make test-e2e`: Declarative contract stabilization Phase 13 makes selector-omitted `get windows|panes|agents` derive one exact managed-root default from the active Window owner chain. `TestWindowScopeChoosesExplicitProjectThenDefaultRootThenGlobal` pins the resolver's Project-or-ControlSession root branch and Pane/Agent propagation; `TestPluralReadContextSelectorMatrix` asserts exact sets for all three kinds over Project/ControlSession/foreign/outside contexts crossed with omitted, explicit Project, both whole-set spellings, and applicable `uid:` selectors. Explicit `--project`, `--all-projects`/`-A`, and any opaque `uid:` occurrence bypass active observation with zero calls and keep Registry-global authority, including cross-root and foreign contexts; names remain root-scoped and a foreign context still refuses rather than widening. The real-tmux e2e extends the isolated Home ControlSession fixture with one sibling Project carrying its own Agent and one foreign Pane, runs the same exact-set matrix through the built binary, strips inherited `TMUX`/`TMUX_PANE` before supplying each exact context, validates the socket path is below the smoke root, and records the exact server/root cleanup target.
330
-
331
- - `make test` / `make test-integration` / `make test-e2e`: Declarative contract stabilization Phase 14 closes generic operation parity over Project and ControlSession roots. `TestControlRootOperationTableIsClosedAndPrintable` is the maintained verb × root-kind table for rename/delete/focus/attention/reconcile/termination projection and permits only the intentional Project-only `rename project` and `focus project` cells. Generic `rename window|pane|agent` now derives the active Project-or-ControlSession namespace without adding a public ControlSession selector or an outside-tmux fallback. Window/Pane/Agent delete live preflight carries `(root kind, root uid, exact session)` through the signed target, applies Project's optional uid mirror separately from ControlSession's no-Project-mirror invariant, and reports final-session impact with the actual root kind. Focus Window/Pane and attention remain exact-runtime/root-neutral; reconcile retains its exact ControlSession diagnostic vocabulary; termination projection retains the ControlSession → Window → Agent/Pane owner chain while releasing only lifecycle bindings.
332
-
333
- - `make test` / `make test-e2e`: Declarative contract stabilization Phase 10 fresh ControlSession bootstrap regression. `TestControlBootstrapNoServerBindsFirstCreatedPhysicalRoute` now starts from a genuinely absent logical route marker, accepts that absence only in the `write-route-marker` action's pre-effect phase, requires the exact marker after execution, and parses both raw and escaped tmux row separators before comparing the exact `$N/@N/%N` containment. `TestPreMarkerRouteGuardAllowsOnlyTheMissingLogicalEffect` keeps the exception closed: physical socket, server PID, app ownership, printable route authority, and any existing marker stay exact, a foreign marker is refused, and the ordinary post-write guard still rejects absence. Selector-free shell discovery treats exact HOME as an upward-walk boundary, so HOME remains a ControlSession even when `$HOME/.git` exists; nested `.git` and `.projmux` markers below HOME still resolve as Projects, and general hook marker semantics remain unchanged. The isolated real-tmux e2e runs that exact command from such a HOME against a run-unique absent socket, allows only the final non-terminal attach failure, then requires the surviving `home` ControlSession, route marker, Window/Pane mirrors, zero Projects, smoke-root socket identity, and exact cleanup.
334
- - `make test` / `make test-integration` / `make test-e2e`: post-deployment Corrective A contains the Home dual-root incident without reopening a phase. `TestHomeDualRootClaimantPrecedenceAndPhysicalAllocation` and the order-independence property pin exact `ControlSession.spec.session` precedence over Project session-name fallback, D4 refusal when a Project uid/root contaminates that exact control-owned session, and a stable tmux-valid `home--<full Project uid>` physical projection for the same-name Project. `TestHomeDualRootIncidentDoesNotBlockUnrelatedCreateAgent` runs the public create-agent pre-reconcile over the exact preserved ControlSession/closed-Project fixture and requires the unrelated Agent to bind while both root uids, owner chains, and control-owned live mirrors survive with no cross-kind write. Explicit Project materialization creates only the deterministic suffixed session and repeats with zero Registry writes; malformed unrelated session observation becomes one reason-bearing D6 item and does not stop the remaining reconcile pass. Constructor and injected-planner tests keep every refusal/diagnostic map writable before observation.
335
-
336
- - `make test` / `make test-integration` / `make test-e2e`: Corrective A install-acceptance follow-up contains a generic runtime Agent marker on the exact Registry canonical default shell without advancing anchor/primary-shell semantics. `TestLinkAgentPaneRefusesCanonicalShellMarkerWithZeroWrites` pins the pure direct Window-owned `role=shell` plus `defaultShellPaneRef` guard before Agent allocation, while `TestLinkAgentPanePreservesAnchorOnlyPromotionBehavior` keeps anchor-only promotion unchanged. `TestAutomaticReconcilePreservesCanonicalShellWithGenericAgentMarker` and `TestResourceReconcileRefusesCanonicalShellAgentMarkerAndContinuesUnrelatedDrift` require zero Agent mint/reservation move/Pane reparent, unchanged owner/role/default-shell ref and Registry bytes, a deterministic reason-bearing D2 public refusal, unrelated mirror convergence, and stable repeat behavior. The isolated real-tmux create fixture adds the same marker to its exact canonical shell, requires `config apply` success plus Registry SHA and `$N`/`@N`/`%N`/uid/marker preservation, and verifies byte-identical public dry-runs and zero-write refused execute.
337
-
338
- - `make test` / `make test-integration` / `make test-e2e`: post-deployment Corrective B closes selector-free ProjectDefault cold start without reopening a phase. `TestShellTmuxExecRunnerReturnsClosedTypedFailures` and `TestClientSessionExistsClassifiesTypedFailures` pin typed genuine absent-socket, generic exit, permission, not-found, and runner failures while preserving removal of inherited `TMUX` and `TMUX_PANE`; only genuine no-server or missing-session evidence is classified as absence. `TestShellTypedPrepareFailuresReachCLIAndClosedJournal` requires a cause-bearing CLI diagnostic plus a closed content-free journal code instead of raw stderr authority or exit-code suppression. The isolated real-tmux shell fixture starts with clean Home, Registry, `TMUX_TMPDIR`, and a run-unique absent socket, succeeds on the first ProjectDefault PTY attach/detach without Home bootstrap, succeeds again on repeat attach, validates the queried physical socket and cleanup target below the smoke root, and removes that exact socket and root.
339
-
340
- - `make test`: Codex native control plane Phase 8 capability and review coverage pins distinct localized default/advanced provider rows; default Codex launch with zero capability-session/readiness calls and zero `--model`/`model_reasoning_effort` overrides; advanced-only hidden/duplicate model removal, supported-effort and closed-modality normalization, boolean-only personality projection, catalog-localized semantic picker labels with provider model/effort/modality data unchanged, paginated `model/list`, connection/version epoch cache invalidation, stale picker refusal, live-session cleanup on pre-plan create refusal, the live picker-to-pre-create session refresh rejecting a removed option, and dynamic `model/list` -> exact launch argv. Advanced discovery failure returns its exact unavailable reason with zero create intents while a separate default launch remains usable. Review tests require one exact Running Agent -> owned Pane -> activation generation -> stored/live thread binding before `review/start`, use one bounded action context, reject incomplete or unknown initial turns, map only a valid initial response to provider-control-plane interaction state, and commit zero lifecycle writes for unavailable, timed-out, mismatched, or replaced bindings. The installed smoke is opt-in through `PROJMUX_CODEX_CAPABILITY_INSTALLED_SMOKE=1`; it closes the discovery context before refreshing the retained live connection. CI does not require a local authenticated Codex daemon or create a disposable provider thread.
341
-
342
- - `make test`: Codex native control plane Phase 5 makes `account/rateLimits/read` the authoritative Codex Usage source and consumes `account/rateLimits/updated` through one single-lease, demand-bounded read-only watcher. Bucket/malformed/sparse-event goldens pin percent, reset, label, nullable identity, unknown cadence, and row-level isolation. Manager tests require an accepted native event batch to atomically replace only Codex rows, preserve other-provider/backoff state, advance the Codex throttle timestamp, and trigger zero second collector calls in the same HUD invocation. Watcher tests pin single ownership, demand-expiry heartbeat cleanup, failure backoff, sparse-update recovery after malformed events, and a private atomic native-only sidecar that never replaces last-known-good bytes with rollout/fallback provenance. The watcher launch boundary rejects `.test` and `.test.exe` executables before starting a child; the status regression runs repeated calls from the actual Go test binary with zero child starts and all usage state confined to its isolated root. Adapter/CLI/HUD/diagnostics tests keep unavailable and unsupported accounts on the existing rollout lane, all-malformed native batches on last-known-good with a closed stale reason, source/value parity across table/JSON/HUD, and an exact request inventory containing only `account/rateLimits/read` with zero login/logout/config/token mutation.
343
- - `TestWatchNativeRateLimitsPeriodicReadKeepsQuietConnection`,
344
- `TestWatchNativeRateLimitsNoEOFFailureIsBounded`, and
345
- `TestWatchNativeRateLimitsPeriodicReadRefreshesSparseBase` use Go's virtual
346
- clock to pin the initial read, fixed 30-second same-client reads, two-second
347
- timeout, busy-notification independence, single pending read, native-only
348
- publication, validated sparse base and malformed-result last-good retention.
349
- `TestWatchNativeRateLimitsParentCancelRejectsLateProbeAndEvents` prevents a
350
- canceled connection's late results or queued events from publishing.
351
- `TestOwnedProxyCloseIsBoundedWhenPeerDoesNotRead` and
352
- `TestOwnedProxyCloseUnblocksConcurrentWriterAndReapsChild` fill real owned
353
- child pipes and require local close within one second, child reap, joined
354
- reader/writers and concurrent/idempotent Close. Close never waits for a
355
- WebSocket close frame or its write lock. Only the exact owned proxy is killed;
356
- its upstream daemon lifecycle is unchanged.
357
- `TestNativeWatcherProbeFailureUsesExistingBackoff` and
358
- `TestNativeWatcherDemandExpiryCancelsPendingProbe` distinguish a failed
359
- two-second probe from parent cancellation. They pin failure timestamps,
360
- heartbeat removal, lease release, the existing inclusive 30-second backoff,
361
- next-demand relaunch, and the 15-second demand TTL plus one-second monitor.
362
- Existing cache freshness, newer-than-store, independent collector/fallback,
363
- request cancellation/late-response and notification channel lifetime tests
364
- remain the parity gates; heartbeat alone supplies no connection health proof.
365
-
366
- - `make test`: Codex native control plane Phase 9 makes the compact Usage identity native-first: label goldens map healthy authoritative app-server rows to exact `Codex`, fresh rollout rows and unknown non-stale provenance to `Codex [fallback]`, and retained last-known-good rows to `Codex [stale]`; en-US/ko-KR narrow statusbar fixtures pin spacing and width. Same-snapshot HUD/table/JSON tests and operations-journal tests preserve exact percent/reset values plus typed source/fallback/stale reasons, while Claude/Antigravity labels, projection order, and Usage semantics remain unchanged.
367
-
368
- - `make test` / `make test-e2e`: Alt-1 Runtime diagnostics visibility Phase 0 makes the Projects sidebar's Runtime link contextual without touching the diagnostics contract behind it. `internal/config` owns the saved `runtime-diagnostics-visibility` choice (`when-needed` default, `always`), and its unit coverage pins the read-time default with no file written, the round trip, an unrecognized saved value that resolves to the default without rewriting the operator's bytes, the closed normalization table, and a rejected save. `internal/app` owns the pure reducer and the settings adapter: the visibility table covers preference x class x observability with no gap -- empty, control-only, ephemeral-only and control+ephemeral hosts withhold the row; each of `Unattributed`, `Foreign`, `Recoverable`, `Conflict` and a mixed tally restore it; no transport and any unavailable scope restore it; and `Always` keeps it in every one of those states. A second table proves a visible row keeps its exact shipped label, tally and sentinel. The rendered-surface tests drive the production observation adapter over a fake host that mirrors precisely what the Registry desires -- the fixture asserts a zero tally and a complete observation before asserting the withheld row -- then over a uid-conflict host and a no-transport host that restore it, with `Always` placing it last before Settings on the healthy host. A taxonomy guard records that an un-mirrored control or scratch session restores the row through its `Unattributed` descendants rather than through its own class, so a class-taxonomy change surfaces there instead of silently changing Alt-1. The settings tests pin the three source annotations including the invalid fail-safe, the save/reload round trip applying on the next invocation, the rejected action value, the ko-KR/en-US parity of the row, both choices, the state row and its source, and the control living inside the exact `Project Sidebar` view with no Advanced or Troubleshooting container. The negative parity audit compares `get runtime sessions|windows|panes` output, the exact server and the Registry across both modes, and compares the whole Registry navigation view -- its single complete Runtime row, its complete class tally, every row's actions and reason -- plus the Sessions surface's own Runtime link, across both modes. The e2e smoke adds a real 80x24 sidebar over a host whose every observed session, window and pane is managed: the default withholds the row (proven by the list's own filter answering `no matches`), `Always` restores it with its empty-state label and its nested Runtime entry and Esc return, and the direct `get runtime` output is byte-identical across both modes while the Registry, the sibling socket and the attached client are unchanged.
369
-
370
- - Real-tmux Phase 3 hook-format evidence is an exact causal pair. `pane-exited`
371
- exposes `%N` through `#{hook_pane}`, but its current-context session/window
372
- formats may already name a surviving client Window; projmux therefore pairs
373
- `%N` with the owner Window's `$N/@N` binding saved by the latest live
374
- observation. `window-unlinked` supplies that dead Window directly through
375
- `#{hook_session}` and `#{hook_window}`. Generated routes never substitute
376
- current `#{session_id}`/`#{window_id}` for teardown authority.
377
-
378
- - `make test` / `make test-integration` / `make test-e2e`: Project runtime stop and fresh identity separation Phase 1 pairs only an exact clean last-Pane `pane-exited` receipt with its matching `window-unlinked` using socket, `$N` session, `@N` Window, `%N` Pane, Registry owner chain, activation generation, and the current-or-exact-released Agent binding. `TestLastPaneExitAndMatchingWindowUnlinkDeleteWindowAndRetainProject`, `TestLastPaneCausalPairDeletesWindowAndPreservesZeroWindowProject`, `TestControlSessionLastWindowCausalPairDeletesWindowAndRetainsRootIdentity`, and `TestWindowCascadeRefusesReceiptAfterAgentResumedOnDifferentPane` pin target-only Window/Pane/owned-Agent deletion, sibling-primary reanchor, ControlSession retention, zero-Window Project validation, later-clock receipt byte-idempotence, and stale-resume zero-write. The canonical Window clean-exit corrective coverage adds `TestCanonicalWindowCreateCommitsExactOwnerPairAndInitialActivationForManagedRoots`, `TestCanonicalWindowOwnerPairBindingFailureRollsBackRegistryAndRuntime`, `TestCanonicalWindowCreateFeedsExactLastPaneCausalCleanupForManagedRoots`, and `TestCanonicalWindowCreateLifecycleAuthorityMatrixIsClosed`: Project and ControlSession intent producers atomically commit exact `$N/@N/%N`, an injected owner-pair binding failure leaves zero Registry/runtime claimant, the producer result feeds the shipped causal consumer, and missing/abnormal/killed/unknown/stale/foreign/mismatched evidence retains Window-delete zero. The controller tests cover unlink-before-pane-exit carry/retry; negative tables keep abnormal/killed/unknown/no-server/foreign/stale/unpaired evidence at Window-delete zero. `TestWindowUnlinkedWithoutExactPendingEvidenceWritesNothing` fixes historical absence-only ghosts at zero writes; the only recovery is exact canonical `delete window`, whose last-Window path retains the Project. The real-tmux integration closes a one-Window Project and the e2e closes a target in a two-Window Project followed by its final Window; the canonical `internal tmux window-create` e2e leg also proves its committed owner pair and initial Pane activation before a normal target-only clean exit while a separately contained sibling-socket sentinel remains byte-identical. These require exact descendant rows zero, Project uid/root preservation, same-server Project and sibling-socket containment, server/session absence, and a byte-identical repeat. Project stop, Continue materialization, Fresh/startup identity behavior, public hook routing, UI output, and provider-content parsing remain outside this phase.
379
-
380
- - `make test` / `make test-integration` / `make test-e2e`: Project runtime stop and fresh identity separation Phase 2 closes the retained-window/zero-window/deleted × Stop/Continue/Fresh/explicit-delete lifecycle table. `TestProjectLifecycleStateTableHasTwelveClosedExclusiveCells` pins one exclusive operation and write set per cell, while `TestProjectLifecycleAppClassifiesAllThreeDesiredStatesWithoutRuntimeEvidence` pins the app classifier without runtime-absence authority. `TestManagedRuntimeStopUsesOnePrintedPhysicalObservationAndRegistryAuthority` keeps the retained desired graph unchanged while stopping only the exact physical runtime, and `TestManagedRuntimeStopAuthorityRejectsZeroWindowNoWriteCell` refuses the zero-Window no-runtime cell. `TestZeroWindowContinuePreservesProjectAndAllocatesCanonicalWindowShellUIDs` preserves the Project uid while allocating one new canonical Window/shell pair. `TestPlanProjectFreshReplacementCreatesOneNewClaimantAndPreservesPreimage`, `TestPlanProjectFreshReplacementSupportsZeroWindowAndFailureRetainsOldGraph`, and `FuzzPlanProjectFreshReplacementIsScopedValidAndAlwaysMintsIdentity` require every Fresh from retained or zero-Window state to atomically replace the Project/Window/Pane uid chain, leave exactly one same-root claimant, preserve unrelated graphs, and retain the old preimage on failure. `TestDeleteProjectIsTheOnlyRegistryUnregisterAndPreservesExternalAssets`, `TestDeleteZeroWindowProjectConsumesExplicitUnregisterCell`, and the post-commit write/flush failure tests keep explicit unregister separate from runtime, root, Git/worktree, and snapshot ownership while exposing action, stage, and old/new uids. The isolated real-tmux startup matrix drives managed Ctrl-X stop→Continue with exact Project/Window/shell identities and resumed Agent conversation, zero-Window Continue with new child identities, and Fresh from both retained and zero-Window states with a new canonical chain each time; it removes inherited tmux and private routing authority, uses unique contained sockets, preserves snapshot bytes and a sibling socket, and cleans only queried exact socket paths.
381
-
382
- - `make test`: Codex native control plane Phase 6 pins exact Agent turn control
383
- and approvals. Adapter fake-server tests freeze `turn/start` with only
384
- `threadId` plus one byte-faithful text input, `turn/steer` with the current
385
- `expectedTurnId`, `turn/interrupt` with the exact turn id, raw numeric and
386
- string JSON-RPC request-id preservation, optional approval ids, and the
387
- command/file/network one-shot and permission turn-only response shapes.
388
- App property and adversarial tables cross action, request kind, available
389
- decision, binding generation, connection epoch, resolution, and concurrent
390
- response attempts; stale, lost, ambiguous, resolved, reconnected, or repeated
391
- identities require zero writes. Private bounded local-socket tests cover
392
- ownership, modes, collision refusal, deterministic overlong-path refusal,
393
- epoch teardown, and frame limits. Picker tests require bounded terminal-safe
394
- request targets and effects on every actionable decision row, with detail and
395
- focus-only rows writing nothing. Notify tests require responder-vs-focus-only
396
- availability and resolved queue cleanup, while privacy audits keep prompt,
397
- command, path, permission, and request content out of Registry, tmux, notify,
398
- diagnostics, support, and Archive sinks.
399
-
400
- - `make test`: Exact Codex turn-start admission reads one fresh, content-free
401
- lifecycle snapshot through the bound control epoch immediately before the
402
- provider mutation. `TestExactAgentControlStartFreshLifecycleAdmissionTable`
403
- pins the cached/fresh matrix, exact `turn-in-progress` and
404
- `turn-state-unavailable` tokens, read-before-start ordering, cross-thread and
405
- structurally inconsistent snapshot refusal, private-detail exclusion, and
406
- zero mutation on every refusal.
407
- `TestExactAgentControlStartRechecksBindingAfterFreshLifecycleRead` keeps the
408
- Agent/Pane/runtime/generation/thread binding current across the fresh read,
409
- while `TestExactAgentControlFreshTerminalReconcileClearsCachedApproval`
410
- proves that an idle/terminal snapshot repairs stale turn and approval state
411
- before the same request starts exactly one new turn. Start-specific behavior,
412
- interrupt, approval, old-epoch, and canonical generation-consumer fences are
413
- unchanged by the steer admission slice below.
414
-
415
- - `make test`: Exact Codex steer admission reuses the content-free lifecycle
416
- read and accepts only the cached turn that a fresh snapshot still proves is
417
- the same exact active turn. `TestExactAgentControlSteerFreshLifecycleAcceptanceTable`
418
- pins the active/idle/terminal/different-turn/unavailable matrix plus exact
419
- lifecycle-read-before-steer order and one-or-zero steer wire count;
420
- `TestExactAgentControlSteerDifferentTurnReconcilesWithoutRetargetingCurrentRequest`
421
- proves reconciliation cannot retarget the current request and only an
422
- explicit retry may address the newly observed turn.
423
- `TestExactAgentControlSteerPostReadFenceMismatchIsTurnStateUnavailable` and
424
- `TestExactAgentControlSteerOldOrCrossEpochReadsAndWritesZero` keep the
425
- Agent/Pane/runtime/generation/thread/control-epoch fences closed before the
426
- provider write. `TestBodyFreeSteerSuccessIsProviderAcceptanceWithDeliveryUnconfirmed`
427
- kills a body-free-success-to-delivery-confirmed mutant by requiring the
428
- structured `acceptance=provider`, `delivery=unconfirmed` receipt, while
429
- `TestAgentControlCLISteerExactAcceptanceRefusalOutputAndExit` fixes the exact
430
- CLI receipt, refusal tokens, and exit 0/1 mapping.
431
- `TestAgentControlCLISteerRejectsMissingOrConfirmedDeliveryReceipt` prevents a
432
- missing or fabricated delivery acknowledgement from reaching successful CLI
433
- output. `make test-e2e E2E_SCENARIO=C01` runs the maintained
434
- `test/e2e/codex-lifecycle.sh` replacement-sibling steer: it boundedly retries
435
- only `turn-state-unavailable` with stdout and the exact sibling steer ledger
436
- held at zero, then requires one qualified provider write and the exact
437
- acceptance/unconfirmed-delivery receipt. Retry time is pacing only, never
438
- lifecycle authority. Start, interrupt, approval, payload privacy, TUI
439
- input/resume, and upstream delivery acknowledgement remain unchanged.
440
-
441
- - `make test` / `make test-integration`: Codex public turn control live-binding
442
- compatibility uses one strict six-field tmux frame. Unit tests accept only
443
- literal `\037` and raw unit-separator spellings without generic escape
444
- decoding or value trimming; missing, extra, mixed, multiline, Registry
445
- activation, runtime, Pane, thread, generation, and epoch drift all fail
446
- closed with zero app-server writes. The isolated real-tmux regression removes
447
- inherited `TMUX`/`TMUX_PANE`, uses run-unique `TMUX_TMPDIR` and `-L` names,
448
- pins tmux 3.4's literal frame, and cleans only its immediately queried exact
449
- socket path.
450
-
451
- - `make test`: Invocation-target authority Phase 2 keeps create, topology replay,
452
- resume, and native Codex on one typed exact-route Pane binder. The maintained
453
- matrix crosses `-L`/`-S` with success and first/middle/last option-write
454
- failure, requires complete managed/native option projection or reverse-order
455
- compensation, and rejects route-less observer transport. Exact same-Window
456
- create ignores inherited `TMUX_PANE` and private child anchors while retaining
457
- explicit route-anchor plus socket/marker/PID guards. Observer tests pin
458
- inherited-tmux environment removal, bounded connect/control fallback, exact
459
- Registry/Pane/generation/thread guards, and explicit-route-only sink writes;
460
- control tests require the resolved logical route plus current Pane/thread and
461
- non-empty epoch before start, steer, interrupt, or approval can reach a wire.
462
-
463
- - `make test` / `make test-integration` / `make test-e2e`: Codex observer
464
- recovery Phase 0 gives native create and resume one post-commit startup
465
- contract: the exact Agent/Pane/generation/runtime/route watcher must complete
466
- a bounded private handshake as `provider-control-plane` with a non-empty
467
- epoch and live control endpoint, or converge to `provider-hook` with an empty
468
- epoch and a typed start-failed, early-exit, handshake-timeout, endpoint, or
469
- control-endpoint reason. Fake-child and exact-route tests pin argv,
470
- environment, ready-settle lifetime, process-group cleanup, and the full
471
- startup matrix; observer/control integration pins exact active-turn steer and
472
- socket removal. Binding replacement and cancellation keep old authority,
473
- fallback, cleanup, progress, and sibling writes at zero.
474
-
475
- - `make test` / `make test-integration` / `make test-e2e`: Codex observer
476
- recovery Phase 2 revokes E1 control before publishing disconnect
477
- invalidation, then retries only the same exact Agent/Pane/generation/runtime/
478
- thread binding with 100ms exponential backoff capped at 1s and six failed
479
- replacement attempts. A recovered observer publishes E2 only after a fresh
480
- snapshot and control endpoint are ready; exhaustion publishes the single
481
- typed `reconnect-exhausted` provider-hook fallback and exits. Unit tests pin
482
- E1-to-E2 ordering, stale epoch/event zero-write, exact retry delays and count,
483
- terminal cleanup, and independent multi-Agent convergence on one shared fake
484
- endpoint. Public start, steer, interrupt, and approval tests require zero
485
- app-server writes throughout invalidating, reconnect-gap, exhausted, and
486
- identity-drift states. The isolated lifecycle E2E records fake provider
487
- mutations across old -> down -> replacement and requires the same public
488
- zero-write gap before a distinct ready epoch appears; the run-owned tmux,
489
- proxy, control socket, and observer process cleanup contract is unchanged.
490
-
491
- - `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.
492
-
493
- - `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.
494
-
495
- - `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.
496
- - `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.
497
-
498
- - `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.
499
-
500
- - `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.
501
- - `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. `TestSessionReplacementSnapshotForwardsPendingSuspensionFirst` and `TestBrokerObserverReplacementSnapshotConsumesPriorSuspensionEdge` pin both sides of the reconnect transport: the runtime forwards the causally prior suspension before a replacement snapshot, and an observer that selects an already-buffered snapshot first consumes that suspension before rotating, so a late edge cannot retire the new sibling epoch. `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.
502
- - `make test` / installed opt-in smoke: Codex payload-free safe fallback chooses the existing managed plain-interactive lane before `Current`, `Resolve`, `thread/start`, `turn/start`, or the durable-resume barrier. `TestCodexCreatePayloadCardinalityInteractiveOnlyAndReadinessOutcomeTable` closes payload cardinality × `--interactive-only` × readiness, while `TestPayloadFreeCodexCreateUsesSafePlainFallbackAndInteractiveOnlyEquivalentLane` pins canonical/shortcut stdout and launch-argv equivalence, exactly one Running Agent/Pane, no native binding, and the `payload-free-fallback` declaration. `TestEmptyPromptCodexSplitProducersKeepOnePlainCLILane` covers saved-default, provider-picker, and direct-provider AI intents; `TestPayloadFreeCodexPlainLaunchFailureRollsBackWithoutProviderMutation` covers Registry/Pane/tmux rollback with no second provider lane; and `TestPayloadFreeFallbackDescribeAndDoctorSignalsAreContentFree` pins the activation/source plus CLI/Doctor reduced-control signal. `TestInstalledPayloadFreePlainFallbackOutcomeSmoke` strips inherited `TMUX`/`TMUX_PANE`, uses isolated XDG/Codex/tmux roots and the actual socket path, and requires one live plain Pane, one Running Agent, no session ref, zero provider-thread delta, no ambient lifecycle mutation, and exact cleanup. `TestPhase7PayloadFreeReadinessFailureIsNegativeSafetyEvidenceOnly` and `TestInstalledPayloadFreeCreateOutputClassificationRequiresFunctionalPlainSuccess` retain the old typed safe failure only as negative evidence. Prompted create remains native: `TestPromptedNativeCodexCreateIssuesOneTurnAndNeverRepeatsThePromptInPaneArgv` pins one turn and no duplicate prompt; native picker resume, stored Agent resume, generation routing, Claude, and Antigravity retain their existing tests. `--interactive-only` keeps its spelling and meaning under `TestInteractiveOnlyIsTheOnlyPlainCodexLaneAndBothSpellingsAreEquivalent`, and `TestCodexNativeLaunchOutcomeTableIsClosed` keeps all functional and negative rows classified explicitly.
503
- - `make test`: Codex install capability guidance Phase 0 makes prompted native create refusal the primary actionable surface while Doctor and Settings consume the same typed `internal/app` authority. `TestCodexInstallCapabilityGuidanceMatrixStatesOnlyObservedFacts` closes `managed-ready`, `external-cli-only`, `cli-missing`, and `unknown`: external-only states exactly that the ordinary CLI exists and the managed standalone payload was not observed, while managed-ready and unknown never imply payload absence or reinstall, and no row infers npm/curl/brew provenance. `TestCodexInstallCapabilityGuidanceHasThreeConsumerParity` requires native create, Doctor, and Settings to render the same capability observation and official Codex CLI reference; the create refusal also names `--interactive-only`. `TestCodexInstallCapabilityConsumersCarryNoSurfaceLocalCopyOrURL` keeps all URL and capability copy in the one authority. Doctor and Settings remain read-only, and support JSON, install/update/daemon mutation, shell welcome, dismiss state, i18n keys, interactive-only semantics, and other providers remain unchanged.
504
- - `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 three by-design plain-CLI lanes - payload-free fallback, explicit `--interactive-only`, and rollout-catalog resume - typed declarations 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.
505
- - `make test` / `make test-e2e`: Codex native reconnect stability keeps the durable exact-thread binding as reconnect authority after a transient endpoint loss. `TestCodexNativeTwoAgentDisconnectRecoversSameAgentControlAndStableProjection` fences stale start, steer, interrupt, and approval writes out of both the retired and replacement epochs; holds one target at a single invalidating/unavailable projection with zero intermediate provider-hook or hook-badge writes; restores same-Agent control only after the replacement snapshot and control endpoint agree; and proves a healthy sibling's authority, semantic projection, and control remain live. `TestInstalledIsolatedRealTmuxTwoAgentReconnectSmoke` reruns the maintained lifecycle fixture with the installed binary, stripped inherited `TMUX`/`TMUX_PANE`, disposable Registry/XDG roots, a unique real-tmux socket, immutable binary hash proof, and exact contained cleanup. `test/e2e/codex-lifecycle.sh` repeatedly polls the reconnect gap and requires byte-identical authority/status/badge tuples before accepting the replacement semantic snapshot once.
506
- - `make test-e2e-shards`: selectorless local `make test-e2e` runs the four Linux fixtures serially in canonical `fixture-1` through `fixture-4` order as the default acceptance profile; `PROJMUX_E2E_LINUX_MODE=parallel` is the explicit stress profile and crosses a four-consumer start/release barrier. Both profiles build exactly one immutable product binary, consume the exact `L01`-`L19`/`C01`/`N01` inventory, and reduce it through the same terminal result hash. `scripts/test-e2e-docker.sh` also 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. `test/e2e/shard-contract.sh` uses fake-runner causal barriers rather than elapsed timing to pin default maximum overlap one and explicit parallel overlap four, then 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.
507
- - `make test` / installed opt-in smoke: Codex Resume Picker state-DB-only catalog wire compatibility makes every `thread/list` the picker sends an explicit `useStateDbOnly:true` bounded metadata read instead of the omitted request that asks the endpoint for a rollout JSONL scan-and-repair. `TestCatalogListSendsStateDbOnlyWithoutOtherWireDrift` is the byte golden for both the first page and the exact-cwd continuation: the field is present and true, and the cursor, limit, sortKey, sortDirection, sourceKinds, archived, and cwd bytes around it are unchanged. `TestCatalogListRejectionIsTerminalWithoutOmittedOrFalseRetry` closes the compatibility matrix -- method-unsupported and unknown-field refusals of a first page are attributed to `ErrStateDbOnlyRejected` wrapping `ErrUnsupported`, an unrelated refusal and a refused continuation cursor are not, and every arm sends exactly one list request with no false or omitted retry, because support is never inferred from a version allowlist and the retry would be the scan-and-repair read the field exists to avoid. `TestStateDbOnlyRejectionRunsOneRolloutFallbackWithoutRetryOrNativeMerge` carries that typed refusal through provider discovery to exactly one bounded rollout fallback whose rows keep `codex-rollout` source, medium confidence, and the `app-server-unsupported` reason with native authority and native/rollout merge at zero. `TestStateDbOnlyIsAListOnlyRequestField` is the negative wire audit that keeps `thread/read`, `thread/resume`, `thread/start`, `turn/start`, and `thread/loaded/list` params free of the list-only field. `TestInstalledStateDbOnlyCatalogListABSmoke` is opt-in through `PROJMUX_CODEX_STATE_DB_ONLY_SMOKE_ROOT=/tmp/<isolated-root>` with an optional absolute `PROJMUX_CODEX_STATE_DB_ONLY_BINARY` pinning the exact generation; it opens a fixture-owned private listener and records the route, row count, nextCursor presence, and elapsed time of the true, false, and omitted arms, failing only when the real endpoint refuses the explicit field. That smoke owns compatibility, not latency: its isolated CODEX_HOME holds no sessions, so the scan-and-repair timing separation of a large real store stays with the exact installed matrix.
508
- - `make test`: Codex Resume Picker route budget isolation gives route resolution, the native page, the rollout scan, and the result handoff four independently measured clocks instead of one 450ms provider envelope every stage inherits the remainder of. `TestAIResumeStageBudgetsAreDeclaredNotDerived` is the exact bound audit -- route 2.5s, native 300ms, fallback 1.25s, handoff 35ms, a 2.835s declared Codex provider terminal, the 450ms discovery envelope, and the app's native bound matching `aisessions.DefaultResumeSummaryNativeBudget` -- plus per-span elapsed measured from each span's own start and the typed `aiResumeBudgetTimeoutError` wrapping `context.DeadlineExceeded` without a new failure vocabulary. `TestResumeRouteBudgetIsNotCutByTheProviderEnvelope` drives a virtual clock through the 450ms cutoff and a 1.97s route and keeps both resolving, published, and counted, while `TestResumeRouteBudgetOverrunIsTypedWithoutLateRouteWrite` makes a route that spends its whole 2.5s bound a typed route timeout whose blocked call publishes zero inventory entries, zero row mutations, and zero list updates after the frame settled. `TestResumeFallbackClockRunsWhileRouteResolvesWithoutTouchingSiblings` holds the rollout clock on its own 1.25s bound while the route is still inside 2.5s and leaves both sibling provider results exactly as they settled, and `TestResumeRouteTerminalReasonTableKeepsOneResultWithoutLateMutation` closes empty, invalid, errored, and cancelled inventories at one terminal result each. `TestCatalogRoutesProjectsCurrentAndDrainingWithZeroLifecycleWrites`, `TestUnobservableCatalogRoutesRefuseWithoutMutatingThePool`, and `TestDefaultCatalogRoutesIsOneAttachOnlyReadWithoutLifecycleCalls` keep `CatalogRoutes` a read-only Current+Draining projection: exact route identity, journal bytes and mtime unchanged, and zero start/stop/admit/drain calls.
509
- - `make test`: Codex Resume Picker native/rollout budget ownership makes the bounded native page read and the bounded rollout scan siblings rather than a chain, so neither is spent out of the other and neither is reduced to whatever a caller context has left. `TestResumeSummaryStageBudgetsAreDeclaredAndOwnedPerStage` pins the declared 300ms native and 1.25s rollout bounds, zero-value resolution, and a per-stage deadline measured from that stage's own start even when the caller deadline is already spent. `TestResumeSummaryRunsOneRolloutScanThatOutlivesTheNativeAttempt` runs exactly one rollout scan per invocation and keeps it alive until the native attempt reaches its own terminal, and `TestResumeSummarySourceSettlementPermutationsKeepOneSourceAndOneSnapshot` settles fresh, nearly-spent, and cancelled callers against native rows, native empty, native error, and native overrun at one source and one immutable snapshot with zero merges. `TestResumeSummarySlowNativeRetainsDepthAwareRolloutRowsAtScale` holds the completed depth-0 43-row and depth-5 158-row results of an 828-session-equivalent store after a native attempt spends its whole bound and fails, and `TestResumeSummaryRolloutCutoffKeepsFullyParsedRowsAcrossTheSendEdge` keeps only fully parsed rows at the rollout stage's own cutoff while a send inside the settlement window is retained and a later one never reaches the published snapshot. `TestResumeSummaryNativeAuthorityAndRolloutRowsKeepGenerationAxesEmpty` leaves native success and native empty with the authority they had and keeps every retained rollout row free of a state-domain, endpoint-generation, or generation-state axis. `TestBoundedNativeSummaryReadOwnsItsBudgetAndClosesOnceOnOverrun` separates a bound overrun from a cancellation and closes a blocked list exactly once, while `TestBoundedRolloutScanNegativeEdgesReturnFullyParsedRowsOnly` degrades unreadable rollouts, symlink directory aliases, and cancellation mid-walk to fewer complete rows with zero duplicates.
510
- - `make test` / `make test-e2e`: honest Project lifecycle verbs and operation receipts. `projmux start|open|stop project` and the canonical `projmux unregister project` become executable routes; `delete project` stays a behavior-preserving deprecated alias of the last of those, with byte-identical stdout, one stderr deprecation line, and one `compatibilityWarnings` receipt entry. `TestStartProjectMaterializesDetachedAndReportsAlreadyLive`, `TestOpenProjectMovesTheCurrentClientAndRefusesOutsideTmux`, and `TestStopProjectEndsOnlyTheRuntimeAndRefusesAnOfflineTarget` pin the offline/live runtime split, the inside/outside-tmux refusals that precede any materialization, and the offline-stop zero-write refusal; `TestUnregisterProjectAndItsDeprecatedDeleteAliasAreByteIdenticalOnStdout` and `TestUnregisterProjectReceiptCountsTheWholeRemovedSubtree` pin the alias equality and the preserved runtime. Every create, rename, delete, and lifecycle route now emits one `OperationReceipt/v1`: the default result prints it as a trailing line and the new `-o receipt` prints it as JSON, while `-o uid|name|ref|metadata|json|pane-id|none` keep their bytes. `TestReceiptValidateRejectsEffectsTheRouteDoesNotAllow`, `TestProjectLifecycleReceiptsMatchTheDecidedEffectMatrix`, and `TestEveryOperationNamesAnExecutableRouteWithAnEffectRow` require every emitted tuple to be one the route manifest already allows and every lifecycle verb to have a distinct tuple. A same-root `create project` repeat now reports `reused` rather than `created` (`TestTheFirstMutationCreatesTheRegistryFromACompletelyEmptyState`). `switch` declares `create project` and `open project` as its canonical composition instead of `focus project`, and the closed-Project startup row `Open fresh` is renamed `Recreate Project` and gated by a confirmation naming the exact old Project UID and counts (`TestSwitchProjectStartupRecreateConfirmsBeforeReplacingIdentity`, `TestSwitchProjectStartupRecreateDeclineWritesNothing`). The isolated tmux smoke drives start/open/focus/attach/stop/unregister against one Project on a contained socket.
511
- - `make test`: Codex Resume Picker concurrent rollout scan start enters the bounded rollout scan with the Codex provider instead of after the generation route has resolved, so the declared route + native + handoff provider terminal is the real worst case rather than route_end + a full rollout bound. `TestResumeRolloutScanIsEnteredOnceWithTheProviderWhileTheRouteResolves` is the start-order ledger: on a virtual clock the scan is entered at the provider start instant, exactly once per invocation, and is already running while a 1.97s route is still inside its own 2.5s bound, with the single native read entered only at the route terminal. `TestRouteSlowInvocationPublishesTheRolloutRowsItAlreadyFound` runs the 1.97s route against a 0.95s depth-aware scan and requires the rollout rows to be settled before the route terminal, the Codex footer to be a count rather than `search_failed`, and every retained row to keep source `codex-rollout` with empty state-domain, endpoint-generation, and generation-state axes. `TestCodexProviderTerminalTableStaysWithinTheDeclaredWorstCase` is the boundary table for a route that spends its whole bound and a route that resolves one millisecond inside it: `providerTerminal()` stays route + native + handoff, the scan's own terminal stays inside it, and the Codex result settles as a count in both arms. `TestCodexRouteReadAuthorityIsUnchangedByTheConcurrentScan` re-runs the Phase 2 source decision at the app layer, where native rows and native empty both keep authority over the concurrent scan and only a native read that settles on no catalog selects the already-running rollout result. A per-route native read declares `codexRouteReadRolloutBudget` so it never opens a second scan of its own; the invocation's single scan is the one owned by the fallback span.
512
- - `make test` / `make test-integration` / `make test-e2e`: explicit child-create target cardinality. `create pane|agent|<provider>` gains `--all-windows` and `--primary-window`, the two spellings that state the target Window set outright: `--all-windows` was the explicit form of the `--project P` whole-Project fan-out of that release and resolved the identical set, and `--primary-window` was the exact-one `spec.primaryWindowRef` selection the release after it made the default. Nothing flipped in that release -- `--project P` alone still fanned out -- it only gained one stderr line and one `compatibilityWarnings` receipt entry naming both escape hatches, with stdout byte-identical; the bullet below is the cutover that spent that notice and removed it. `TestChildCreateTargetCardinalityTable` is the zero/one/multi-Window table across all four spellings, reading the selected set out of `-o receipt`; `TestChildCreateZeroWindowCardinalityRefusesWithZeroWrites` closes the empty-Project row for every spelling including the Agent route. `selectedWindowUIDs` is now sourced from the shared target planner rather than from the created results, so `TestChildCreateSelectedWindowSetIsIdenticalAcrossProviders` can require `create pane`, `create agent`, and the `codex`/`claude`/`antigravity` shortcuts to report one identical set for identical argv. `TestChildCreateCardinalityFlagConflictsAreUsageErrors` pins every mutual exclusion -- each new flag against `--window`, `--pane`, `--selector`, `--create-window`, and against the other -- as an in-parser exit `2`, and `TestChildCreatePreflightRefusalsCostNothing` adds the dangling and cross-Project `spec.primaryWindowRef` refusals and the explicit-`--name` cardinality refusal; every refusal leaves zero Registry writes, zero tmux objects, and zero provider effects. `TestChildCreateCardinalityFlagsMaterializeAndRollBack` keeps the new spellings on the shared runtime path: an offline Project is still materialized detached, a failed `split-window` still rolls the whole fan-out back, and the Settings provider gate still refuses before allocation. The native Codex exact-one fan-out refusal is unchanged and is now decided on that same planner set. `TestRepositoryCallSitesSpellTheirChildCreateCardinality` is the migration gate: every `.sh` call site under `scripts/`, `test/`, and `npm/` that scopes a child create with `--project` must name its cardinality, and the smoke fixtures were migrated to `--all-windows` because that spelling is provably the set they already resolved.
513
- - `make test` / `make test-integration` / `make test-e2e`: primary-one child-create default. The `--project P` child create whose whole-Project fan-out the previous release deprecated now resolves exactly one Window, the Project's `spec.primaryWindowRef`, and the compatibility notice it earned is gone from stderr and from the receipt's `compatibilityWarnings` -- the default is the behavior that notice named. Both spellings of that selection share one preflight (`primaryWindowSpelling` picks which one the refusal names), so `--primary-window` and the bare `--project` scope cannot drift apart on the target or on the missing/dangling/cross-Project refusal. `TestProjectOnlyOmissionIsThePrimaryWindowDefault` is the cutover golden: the omission is byte-identical to `--primary-window` on stdout, stderr, and the resulting Registry; `--all-windows` still produces one Pane per Window of a two-Window Project while the omission produces exactly one, in the primary Window only, which is the `silent target 확대/축소 0` claim in observable form; and the two spellings that were never part of the compatibility surface keep their meanings -- a wholly omitted scope inside a managed Pane still splits the active Window even when the Project's primary is elsewhere, and `--primary-window` still overrides the active Window. `TestChildCreateTargetCardinalityTable` moves the `--project`-alone rows to the exact-one primary set and now requires every spelling to carry zero warnings on both channels, and `TestChildCreateZeroWindowCardinalityRefusesWithZeroWrites` moves the empty-Project row from a bare no-match to `--project: project/empty has no spec.primaryWindowRef`. `TestChildCreatePreflightRefusalsCostNothing` adds the dangling `spec.primaryWindowRef` refusal reached through the bare `--project` spelling, on the same zero Registry writes, zero tmux objects, zero provider effects assertion. The integration smoke adds the end-to-end half on the two-Window Project the discovery section already builds, invoked from a Pane in the *non-primary* Window so that "the default is the primary Window" is separable from "the create split wherever the caller was": `--project` alone and `--primary-window` each add one Pane to the primary Window and none to the other, `--all-windows` adds one to each, an explicit `--window` adds one to the Window it names, combining the two cardinality flags is exit `2` with no Pane, and the `--project`-only receipt carries the primary Window uid and `"compatibilityWarnings": []`. The Go fan-out fixtures that had been spelling the whole-Project set as `--project P` alone are respelled `--all-windows`, which is provably the set they already resolved, and `TestRepositoryCallSitesSpellTheirChildCreateCardinality` keeps every `.sh` call site explicit -- a fixture whose subject is the implicit spelling opts out by writing `projmux-cardinality-default-fixture` on the line, so an exemption is visible rather than silent. `--all-windows`, `--primary-window`, every mutual exclusion, the `natural-omitted` active-Window contract, the `delete project` alias, and the native Codex exact-one fan-out refusal are all unchanged.
514
- - `make test`: Codex Resume Picker provider-independent status and elapsed settlement gives every enabled provider its own settlement envelope, started at that provider's own instant with that provider's own declared bound, instead of one shared discovery deadline that stretched to the longest declared provider bound whenever Codex was configured. `TestCodexSettlementLeavesSiblingProviderStatusAndCountUnchanged` runs the whole Codex matrix -- native rows, a route read that declines authority, an unavailable route inventory, route reads that all fail, a genuine failure with nothing found, and a Codex that never returns inside its own envelope -- and requires Claude 143 and Antigravity 4 to keep the same projection, the same footer count, and the same rows in every arm, with a final pair of runs proving that enabling or disabling Codex does not move where Claude terminalizes. `TestCodexFallbackRowsProjectFoundInsteadOfSearchFailed` makes rows the authority for the status: route reads that all fail, an unavailable route inventory, and a partial that settles on its own cancellation all publish their rollout rows as `N found` with source `codex-rollout` and empty generation axes, while `search_failed` is left to a Codex terminal that genuinely found nothing. `TestProviderElapsedIsMeasuredOnEachProviderOwnClock` drives a virtual clock where Claude terminalizes at 120ms and Antigravity at 200ms while a 1.97s Codex route is still inside its own bound, and requires each witness to stay on its own clock, each provider to have been granted its own envelope, and the elapsed values never to reach a row or the footer. `TestGlobalCapAppliesOnceAndProviderFooterCountsStayPreCap` applies the visible cap once to the settled rows of all three providers and keeps the provider footer counts pre-cap, and `TestProviderSettlementKeepsFooterVocabularyLocalesAndGeometry` is the EN/KO golden for the unchanged `count`/`search_failed`/`disabled` vocabulary, provider order, and two-line footer geometry. `TestLateProviderSendAndCloseNeverMutateSettledItemsOrFooter` returns a provider after its own terminal and then closes the controller, with zero changes to the settled Items, the settled footer, and the deferred update.
515
-
516
- - `make test` / installed opt-in smoke: Codex Resume Picker exact private-generation installed closure replays the source, status, time, and cleanup contracts of this track once against a real binary and a real-scale store, because every earlier phase settled them on virtual clocks and synthetic trees. `TestInstalledStateDbOnlyCatalogListSettlesAtRealScale` is opt-in through `PROJMUX_CODEX_SCALE_CATALOG_SMOKE_ROOT=<isolated-root>`, which like every installed smoke root must be an isolated child of the process `TMPDIR` -- a multi-gigabyte store copy usually needs that pointed at real disk rather than a tmpfs -- with `PROJMUX_CODEX_SCALE_CATALOG_BINARY` naming the exact absolute generation and `PROJMUX_CODEX_SCALE_CATALOG_VERSION` naming the version it must self-report and answer with; the root holds a pre-made read-only `codex-home` copy the fixture adopts, never the live store. It puts a pass-through WebSocket ledger in front of the private listener so the assertion is what the product actually wrote on the wire: exactly one `thread/list` with `useStateDbOnly` explicitly true, settling inside the declared 300ms native window as either an exact result or a typed timeout rather than a field or params refusal, and rewriting zero rollout files. The explicit-false and omitted arms run afterwards as recorded baselines only, and the ledger's final tri-state order proves they were never a product retry. `TestInstalledResumePickerSettlesProviderTruthOnTheExactPrivateGeneration` is opt-in through `PROJMUX_CODEX_SCALE_RESUME_SMOKE_ROOT`, `PROJMUX_CODEX_SCALE_RESUME_BINARY`, `PROJMUX_CODEX_SCALE_RESUME_VERSION`, `PROJMUX_CODEX_SCALE_RESUME_STORE`, and `PROJMUX_CODEX_SCALE_RESUME_CWD`. It takes a same-run census of the store copy first -- rollout file count plus untimed depth-0 and depth-5 rows per provider -- and compares every observation against that census instead of a planning literal, because the store keeps growing. Four arms settle the picker population at `resume_scan_depth=5`: a native read on the exact private listener, a slow native on a socket that accepts and never answers, an absent native socket, and a run with Codex disabled. The native arm must publish `codex-appserver` rows carrying the resolved route's exact generation identity; both failing-native arms must publish exactly the census depth-5 rollout row count as `codex-rollout` with all three generation axes empty; Claude and Antigravity must equal their own census counts and settle on their own clocks in every arm including the Codex-disabled one; no provider witness or frame may exceed the declared provider terminal; and no published frame may change after the terminal has fully elapsed. Both smokes register a live-store guard that manifests the operator's real rollout store and requires it byte-identical at the end, and both require the fixture rollout store to be unchanged by the product path.
517
- - `make test`: Agent-owned Pane derived explicit name. `create agent` and the three provider shortcuts now supply `<agent-name>-pane` as an **explicit** `BootstrapPane.Name` for the Pane the Agent owns, so the launcher convention that lived only in documentation as a mandatory follow-up `rename pane` is code and a caller that never read that page no longer leaves a managed Pane addressed by a raw UID. `TestCreateAgentNamesItsManagedPaneAfterItsAgent` is the derivation table: an explicit `--name reviewer` produces `reviewer-pane`; no `--name` at all produces `<agent-uid>-pane`, because the Agent's own automatic name is its exact full UID and the point is that the *Pane* is not named after its own UID; a 123-byte Agent name produces the largest legal 128-byte derived name; and a 128-byte Agent name, whose derivation would be 133 bytes and fail `ValidateName`, falls back to ordinary automatic naming rather than refusing a `create agent --name <128 bytes>` that works today -- length is the only fallback, and every row additionally requires the name reservation to follow the Pane. `TestADerivedAgentPaneNameCollisionIsATypedZeroWriteRefusal` proves the other half rather than assuming it: the derived name enters the same explicit-name machinery an operator's `--name` does, so a name already held root-wide for kind Pane is a typed exit-2 refusal with zero Registry commits, zero minted uid published, zero tmux objects, zero provider bindings, and no invented suffix; the whole metadata phase runs before the first tmux or provider call, which is what makes that free. `TestAgentAndPaneRenamesStayIndependentAfterTheDerivation` pins the deliberately-absent invariant: renaming the Pane to the very name the derivation produced is a successful no-op (`reserveExplicitName` guards with `owner != uid`, so an existing launcher's follow-up rename keeps working), an explicit `rename pane` is neither forbidden nor restricted and does not follow the Agent, and `rename agent` still reports `panes=0 agents=1` so its `cardinality=exact-one` effect tuple and the seven-axis command effect manifest are untouched. `internal/core/metadata`'s `TestAnAgentlessPaneKeepsItsExactUIDAutomaticName` is the C-5 `Sequence-free automatic naming` regression guard: the automatic allocator is not touched by this change, so bootstrap topology Panes, `AddPane` with no declared name, and `AttachAgentPane` with no declared name all still carry their exact full minted UID, and an explicitly named sibling never makes the next automatic name react to it.
518
- - `make test`: Codex endpoint broker diagnostics dial the endpoint the runtime published. `observeCodexBrokerRuntime` used to name `DefaultEndpointKey` up front, while a managed runtime publishes under the generation-scoped key its route derives, so `projmux doctor` reported a broker that was listening as `absent / host-unavailable` -- and an operator acted on that answer by terminating a foreign app-server process. The reader now lists the discovery records the state domain actually holds, keeps only those whose announced endpoint derives back to the record that announced it, and dials that set in one stable endpoint order under a single deadline; `Dial` stays the call, because `Ensure` may create the directory, reclaim an artifact, and start a runtime, which would make a diagnostics read a side effect. `TestBrokerDiagnosticsDialTheEndpointTheRuntimePublished` is the machine's exact shape -- a live generation-scoped runtime beside a default-key lock with no socket and no record -- and requires `running`, the generation-scoped key as the observed endpoint, the answering host's runtime identity, a byte-identical state-domain file set across the read, a runtime still serving afterwards, and a second read reaching the same runtime; it fails against the default-key dial. Its mutation audit covers the connection axis and not only the file set, because a dial is itself a connection: two reads must leave zero upstream opens, an unmoved connection epoch, zero bindings and zero upstream connections reported, and host stats -- live sessions, bindings, refusals, draining, endpoint -- back at their pre-read values. `TestBrokerDiagnosticsStayAbsentWhenOnlyAnUnpublishedArtifactRemains` keeps the resting answer and its closed token: a startup lock is not a runtime and neither is a record whose announced endpoint does not derive back to its own filename, so the verdict stays `absent / host-unavailable` with zero published endpoints and no endpoint claimed. `TestBrokerDiagnosticsSelectEveryPublishedEndpointDeterministically` publishes two generations at once and pins the selection rule the reader is allowed to have: a stable ascending endpoint order, the first runtime that answers, both hosts alive after the read, and the same endpoint on a repeat read -- which generation ought to be current stays the generation pool's judgment. `doctor` now prints `Published endpoints: N` and, when one was judged, `observed endpoint: <key>` on every state rather than only on `running`, so `absent` beside a non-zero count is visibly a runtime this reader could not reach rather than a runtime that is gone.
519
- - `make test`: One oversized answer no longer cycles the Codex app-server control plane. The broker multiplexes one upstream connection across every bound thread, and `codexappserver.Client.readLoop` used to treat an inbound message past the 1 MiB frame bound as a fatal connection error. `thread/read` with `includeTurns=true` returns a thread's whole transcript, so a long-lived Agent's own lifecycle snapshot crosses that bound on its own: the connection died, `Broker.revoke` suspended **every** binding at once, each observer's next epoch re-issued the same read, and the cycle sustained itself -- measured on this machine at 0.86-1.35 connection epochs per second for hours, with `bind` never dipping. The framing layer now reads an over-long message to its exact byte count and drops it, so the stream stays framed; the client answers only the requests that were waiting with `ErrPayloadTooLarge` and keeps the connection; and the broker classifies that as `indeterminate` under its own `payload-too-large` reason rather than collapsing it onto `disconnect-boundary`, because the connection survived and an operator reading the two alike looks for a broker fault that is not there. `TestClientDropsAnOversizedAnswerAndKeepsTheConnection` requires the dropped answer to reach its caller as `ErrPayloadTooLarge`, a following notification to still arrive on the same connection, and the drop to be counted. `TestClientKeepsServingRequestsAfterAnOversizedAnswer` proves the drop is request-scoped by serving the next request on that connection. `TestWebsocketStreamDropsAnOversizedMessageAndStaysFramed` reads the whole message behind a dropped one, which is what a discard that consumed the wrong byte count would break. `TestOversizedAnswerEndsOneMutationAndNotTheConnectionEpoch` holds the blast radius: five oversized answers leave the connection epoch, the disconnect count, the binding count, and a neighbour binding's live fence and stream all untouched. `TestOversizedAnswerIsToldApartFromADisconnectBoundary` keeps the two indeterminate reasons distinct. Beyond the frame bound the message is still refused, because reading it out is itself the denial of service the bound exists to stop.
520
-
521
- - `make test` / `make test-integration`: Window teardown decision journal Phase 0 records each consumed lifecycle teardown decision at the app layer (never inside the pure core planner) as one closed `topology.teardown.decision` event. `TestTeardownReasonJournalMappingCoversEveryCoreReason` and `TestTeardownVocabularyMatchesCoreDecisionTable` read `internal/core/metadata` source for every `TeardownReason`, `TeardownAction`, and `TerminationClassification` constant and fail on any unmapped or unallowlisted value; the first also round-trips every code through the real store and every conflict reason through its unchanged error bytes. `TestTeardownDecisionEventClosedShapes` and `TestTeardownRecorderAppendsWithoutOutcomeOwnership` pin the closed shape, strict `win-`/`pane-` UIDs, rejection of the new fields on every other family, and best-effort appends that never own the top-level outcome. `TestTeardownJournalCleanLastPanePairRecordsOneDeleteWindow`, `TestTeardownJournalKilledLastPaneRecordsRetainAndKeepsRegistry`, `TestTeardownJournalCleanupRetryRecordsRetainOnce`, and `TestTeardownJournalAuthorityConflictRecordsOneRefusal` run each fixture beside an unjournaled twin and require identical results, errors, and Registry bytes with exactly one decision per evaluation; `TestTeardownJournalWindowUnlinkRecordsFirstWaitAndExhaustion` requires one `retain`/`awaiting-pane-exit` on the hook's first wait, zero during carried causal retries, and one at exhaustion; `TestTeardownJournalKillWindowShapeFirstWaitCarriesWindowPaneAndKilled` drives the real controller body for a `kill-window`-shaped Registry and requires that first record to carry the exact Window UID, Pane UID, and `killed` with unchanged Registry bytes, while `TestTeardownJournalAmbiguousUnlinkSubjectOmitsUIDs` and `TestLifecycleWindowUnlinkAwaitingSubjectOmitsAmbiguousPane` omit any ambiguous UID. The integration termination smoke asserts one `delete-window`/`window-teardown` record for the exact closed Window UID, the first-wait plus exactly one exhaustion record for a replayed unpaired `window-unlinked`, and one `awaiting-pane-exit` record with the exact Window UID for a real `tmux kill-window` whose Window row is retained. Its read-only status/viewer and automatic-path journal line-count windows, which follow hook-driven session stop/unregister/prune closes, exclude only the asynchronously appended `topology.teardown.decision` lines, so any record the measured command itself appends still fails.
522
- - `make test`: Orphaned Registry Window Phase 1 records the live `$N/@N` of every Window a Project start/Continue topology replay materializes, in the replay commit itself, through the same `ObserveWindowRuntimeBinding` writer a canonical Window create uses. `TestTopologyReplayCommitsLiveBindingForEveryMaterializedWindowOnly` requires the new-session-adopted first Window and the new-window ones to carry their live pair with MissingRuntime cleared at the single replay write, while a Window outside the replay keeps its runtime ids byte-for-byte; `TestTopologyReplayUnderLiveSessionLeavesPresetLiveWindowBindingsUntouched` re-creates a raw-killed first Window under the existing session and leaves every preset-live Window row, including a wrong sentinel binding, byte-identical. `TestReplayedWindowCleanLastPaneExitAndLiveUnlinkDeleteWindowAndJournalOnce` pairs the replayed sole Pane's clean `pane-exited` with the live `window-unlinked`, deletes the Window and Pane, keeps the Project, journals one `delete-window`/`window-teardown`, and shows the stale pre-fix row shape does not pair; `TestReplayedWindowKillWindowRetainsWindowAndJournalsAwaitingPaneExitKilled` keeps a raw-killed replayed Window and journals `retain`/`awaiting-pane-exit` with `killed` and the exact Window UID; `TestReplayedWindowNonLastAgentPaneCleanExitRemovesOnlyPaneAndKeepsAgentOffline` removes only the Agent Pane and keeps the Agent Offline with its conversation; `TestPastOfflineWindowWithUnpairedTeardownEvidenceSurvivesReplayAndOtherPairs` keeps a past offline Window with MissingRuntime and unpaired evidence byte-identical across the replay and another Window's causal pair. Pairing conditions, teardown decisions, and Windows the replay does not materialize are unchanged.
523
-
524
- ## When To Update This List
525
-
526
- - A feature moves between unit, integration, and e2e coverage levels.
527
- - A new subsystem introduces a new validation target or removes one.
528
- - Behavior changes require new parity assertions or a different e2e scenario.
529
- - A target stops being authoritative and must be replaced.
530
-
531
- - `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.
532
- - `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.
533
- - `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.
534
-
535
- - `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.
536
-
537
- - `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`, `TestAgentResumeProductionBinderWriteFailureIsVisibleAndRollsBack`, `TestRegistryTopologyBinderWriteFailureIsVisibleAndRollsBack`, and `TestProductionAIPaneBindersUseOnlySuppliedExactRuntimeRoute` require exact create, native create, resume, and retained topology materialization to use only explicit `-L`/`-S` calls—including exact `select-pane -T ... -t %N` title presentation through the real AI binder—while returning success only after the caller consumes `BindAgentPaneOnRoute`'s result; injected option-write failures remain non-zero and roll back the partial Pane and Registry state. The provider shell contains no tmux mutation. 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.
538
-
539
- - `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.
540
-
541
- - `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.
542
-
543
- - `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.
544
- - `make test`: Claude resume discovery opens candidates in descending mtime order until it has the displayed limit of unique matching IDs and has drained the terminal mtime tie group. `TestClaudeDiscoveryScanCostTracksLimitAndTerminalTie` reverses Phase 0's whole-history cost assertion for both interactive and summary lanes: 120 and 480 transcripts open the same limit-plus-terminal-tie count. `TestClaudeDiscoverySkipsOlderNonMatchingTranscripts` reverses its older-nonmatch assertion. The bound is data dependent: `TestClaudeLimitedScanPreservesFullSettledRows` compares the real global settlement against independently declared full-scan rows for a 512-file terminal tie with reversed filename/ResumeID order, 256 newest duplicate IDs, 256 newest cwd nonmatches, and cross-directory depth scans with equal-mtime duplicate IDs carrying different titles. These adversarial cases may require O(M) reads; correctness does not have a fixed file-cap exception. The summary lane retains confirmed unique matches before the global visible cap, including terminal ties, so its provider count describes this invocation rather than the accumulated history. `TestClaudeCanceledScanReturnsOnlyFullyParsedRows` deterministically cancels before scanning and during a real second-file read, retaining only complete earlier rows and marking incomplete discovery with `MoreNotLoaded`.
545
- - Installed observation: `TestClaudeResumeRealStoreObservation` skips unless `PROJMUX_CLAUDE_RESUME_OBSERVE_CWD` explicitly supplies the real absolute cwd. After merging and installing, run it from the merged source alongside the installed native picker smoke. The opt-in test restores the original XDG config home that ordinary package tests isolate, without changing normal test isolation. It first runs all configured providers with the production native catalog opener and real read-only generation-route inventory at the configured depth/limit, then records Claude-only comparisons at depths 0 and 5. It reports only the exact-cwd file census, provider states, confirmed/visible counts, route count, and each provider's elapsed witness. Criterion ⑥a requires visible Claude rows with `count` in the concurrent invocation, including a nonempty partial; ⑥b requires each Claude-only scan below the unchanged 450ms budget. The observation performs population only, without preview, selection, launch activation, or transcript/config/Registry writes. It does not print conversation contents or session identities.
546
- - `make test`: `TestClaudeEnvelopeExpiryPublishesParsedPartialRows` replaces Phase 0's artificial late-send harness and pins C-1/⑥c: the injected clock expires the provider envelope, a private nil-by-default timer seam witnesses entry into the unchanged 35ms handoff, and only then a channel releases four fully parsed fixture rows into an invocation limited to twenty. The controller publishes those exact rows and detail references with `count=4` and `MoreNotLoaded=true`. The boundary fixture uses a smaller scan limit to obtain its real prefix; cancellation during parsing is independently covered by `TestClaudeCanceledScanReturnsOnlyFullyParsedRows`. The combined assertion has no real timer or wall-clock dependency. `TestClaudeHandoffExpiryKeepsLateResultOutsideSettlement` preserves the separate negative case: a provider that misses both windows leaves its late result buffered outside the already immutable settlement. C-3 is absolute when the scan completes; envelope expiry yields a descending-mtime prefix, strictly better than discarding every row but not equivalent to the completed scan. Remaining provider budget-allocation asymmetry is outside this phase.
547
- - `make test`: `TestResumeProviderUnfinishedAndFailureHaveDistinctLocalizedTerminals` pins the Phase 2 empty-scan terminal split in both footer locales: an expired envelope without a provider error is `scan_unfinished`, a genuine error remains `search_failed` (including when the expiry marker is also set), and a completed empty scan remains `count=0`. The virtual-clock handoff test keeps late rows outside that new terminal. `TestSessionScanThreeProviderDiscoveryEquivalence` compares full ordered exported discovery rows for Claude, Codex, and Antigravity against fixtures captured and passed on `fc287c26` before the sparse parser change; Antigravity is a control lane because its current-storage/history adapters do not use the transcript scanner. `TestSessionScanRecordPreservesLegacyFields` separately checks tolerant aliases, unknown nested metadata, escaped/case-sensitive keys, malformed/overflowing records, and unchanged provider title helpers. Candidate readiness, line limits, and the Phase 1 mtime/dedupe/cwd stop rule stay unchanged.
548
-
549
- ## E2E Required-Gate Evidence
550
-
551
- - `make test-e2e-coverage` is the maintained AGS-OEDR inventory gate. It
552
- requires exact `L01`-`L19`/`C01`/`N01` executable marker parity, four Linux
553
- shard assignments, valid referenced lower-layer test symbols/selectors, and
554
- orphan count zero. A moved matrix is invalid unless positive, negative, and
555
- fixed-point evidence are all non-empty and executable while E2E retains a
556
- real-boundary sentinel. `make test-e2e` always runs the manifest/orphan half;
557
- the referenced lower test is also exercised by the required unit-test job.
558
- - L19's former 60-cell plural-read matrix now runs in
559
- `TestPluralReadContextSelectorMatrix`: each Project/ControlSession/foreign/
560
- outside x omitted/Project/global-short/global-long/UID cell runs twice with
561
- identical output/error/observation count and zero Registry transaction,
562
- write, or model change. The L19 real-tmux fixture retains five sentinels and
563
- owns exact popup origin, queried socket containment, and owned-root cleanup.
564
- - The same manifest absorbs merged-main evidence without expanding the stable
565
- inventory. L17 closes simultaneous/coalesced provider exits through exact
566
- generation dead/mirrored cleanup, resumable Offline Agent refs, sibling
567
- preservation, and byte-identical repeat. L18 closes the generated foreground
568
- `run-shell` matrix through exact-client bounded delivery, success silence,
569
- no view-mode overlay, and origin PID/focus/Registry identity preservation.
570
- Merged lifecycle commit `3322b5f7` adds no stable ID: L17 separately owns the
571
- causal clean last-Pane/Window-unlinked Project-stop path, zero-Window root,
572
- stale-resume refusal, sibling reanchor/containment, and fixed point; L19 owns
573
- the ControlSession last-Window descendant cleanup, root retention, zero
574
- replacement allocation, sibling containment, and fixed point. Their exact
575
- lower test sets share the existing real last-Pane integration marker but use
576
- distinct E2E markers before each scenario's pass. Every referenced guarantee,
577
- unit symbol/selector, and integration/E2E marker is fail-closed by the
578
- coverage audit.
579
- - Merged lifecycle commit `dcffa5da` stays on L11: its closed row links managed
580
- runtime stop, retained/zero-Window Continue, always-new Fresh, exact client
581
- handoff, and sibling containment to lower tests plus the existing lifecycle
582
- integration completion and L11 pass boundary. Merged authority commit
583
- `de52d15d` stays on L17: its row links creator-root activation admission,
584
- exact receipt/exec disambiguation, immediate exit-42, the fresh-root focused
585
- repeat harness, and the read-only owner/queue + product-terminal handoff.
586
- Stable inventory remains exact `L01`-`L19`/`C01`/`N01`; source commit,
587
- guarantee, lower selector, supporting marker, and marker-before-pass drift
588
- all fail closed.
589
- - `make test-e2e` uses one attempt-local binary across isolated four-way Linux,
590
- Codex, and npm consumers. Local Security is the exact three-way
591
- `make security` topology; CI's required branch-protection context is the
592
- fail-closed aggregate named `Test`.
593
- - `test/lib/smoke.sh` owns every scenario wait through `smoke_wait_until
594
- <seconds> <description> <command>`; `test/e2e/` holds no raw `for _ in
595
- {1..N}` polling loop. A wait that expires fails with the description it was
596
- given plus the budget applied, elapsed time, attempt count, and last predicate
597
- status, so no scenario can enter its next assertion with the condition still
598
- unmet. `E2E_WAIT_SCALE` multiplies every budget for a slow runner.
599
- `test/e2e/reliability-contract.sh` F08/F09 pin both halves: a deliberately slow
600
- fixture times out with its own description and state dump and then passes once
601
- the scale buys it time, and the timeout instant itself moves with the scale.
602
- CI sets that scale to `2` for both required E2E child families. The Phase 5
603
- sample covered 57 runs: the slowest job family (`fixture-3`) had p50 167s,
604
- p95 177s, and max 201s. Because the scale applies to scenario waits rather
605
- than whole jobs, the deciding tail is the widest scenario distribution: L12
606
- had p50 7.185s, p95 8.081s, and max 10.188s (`max/p50=1.42`). Rounding that up
607
- to 2 gives headroom while retaining the named fail-closed timeout; the former
608
- 3-5x suggestion had no runner measurement and is not the configured policy.
609
- - `make test-e2e-residual-policy` pins the Phase 5 residual decision. The exact
610
- required inventory remains `L01`-`L19`/`C01`/`N01` (21 scenarios), and
611
- `test/e2e/quarantine.tsv` is empty because the 57-run, 1,128-group evidence
612
- corpus found no scenario with three observed flakes. The companion
613
- `test/e2e/residual-observations.tsv` records each scenario's actual terminal
614
- attempt denominator (51-54 after whole-run self-regression exclusion), and
615
- the contract requires its complete inventory and threshold-derived eligible
616
- set to equal the quarantine ledger. A row is invalid without
617
- a stable scenario ID, owner, ISO-8601 deadline, and at least three observations;
618
- this closed zero-set contract rejects a non-empty row until execution/evidence
619
- and non-gating wiring exist. Therefore the quarantine execution Negative is
620
- N/A: its antecedent does not exist. `test/e2e/shard-contract.sh` is the single
621
- canonical owner proving that both E2E child families still feed the exact
622
- `E2E Tests` compatibility context and the project-wide `Test` gate. Go
623
- `Unit Tests` remain an exact required child and outside automatic
624
- classification/quarantine: they have no per-test attempt artifact or `class`
625
- surface, so assigning an observed-flake count would invent evidence. A future
626
- unit quarantine first needs attempt-preserving per-test evidence and a separate
627
- contract.
628
- - `test/e2e/admission-contract.sh` (`make test-e2e-contract`) pins the local
629
- full-suite capacity boundary used by default `make test-e2e`: one invocation
630
- owns the active state while another waits, and the wait diagnostic plus state
631
- file expose `reason=local-full-suite-capacity`, capacity one, owner pid,
632
- start time, cwd, and exact state path. Both callers retain their own terminal
633
- status after admission. `PROJMUX_E2E_ADMISSION_BYPASS=1` is the explicit
634
- stress path and is required to overlap a live admitted owner. Single-scenario
635
- replay and CI shard/suite selectors remain outside this local full-suite
636
- boundary; `test/e2e/shard-contract.sh` keeps the four Linux plus two suite
637
- jobs on independent runners with `fail-fast: false` and the stable required
638
- name `E2E Tests`, while also proving both selector families enter with a local
639
- slot already occupied. A dead owner is diagnosed as `stale-owner` and reclaimed
640
- only while the active hard link still identifies its exact owner record.
641
- - `test/e2e/evidence-contract.sh` (`make test-e2e-contract`) and
642
- `test/e2e/reliability-contract.sh` (`make test-e2e-reliability`) keep the persisted
643
- `projmux.e2e-attempt/v1` evidence and success result hash stable while adding
644
- a separate stderr-only `projmux.e2e-terminal/v1` first-failure record. Its
645
- closed fields identify scenario, phase, owner, shard, status, repository
646
- source/line, sanitized single-scenario command, binary/state hashes, and exact
647
- replay without raw argv. Synthetic L06/L08/L16 fixtures pin cardinality-neutral
648
- holder/racer event state, bounded Registry changed paths plus pending owned state, and child/file/
649
- exact tmux state plus sanitized hashed tails; denylist checks reject token,
650
- raw HOME/path, full Registry, and raw command leakage. Normal scenario meaning,
651
- one-build/four-shard topology, and `.bin/e2e-evidence` retention are unchanged.
652
- - `test/e2e/evidence-contract.sh` also pins failure-evidence preservation. A
653
- failing attempt folds the emitted terminal record and the L06/L08/L16
654
- diagnostic into the persisted `projmux.e2e-attempt/v1` artifact as the additive
655
- optional `terminal_line`, `terminal_status`, `terminal_source`, and
656
- `diagnostic` fields; a begin/pass record keeps its exact prior field set and
657
- bytes. Degraded input no longer discards the whole attribution: a zero
658
- `BASH_LINENO[0]`, an out-of-range wait status, and a non-allowlisted source
659
- path each drop only their own field and mark the terminal record
660
- `attribution=partial` while scenario, phase, owner, shard, and replay survive.
661
- The `PROJMUX_E2E_INTENTIONAL_ZERO_LINE` fixture drives that path through the
662
- real harness, and CI preserves the attempt evidence subtree — excluding
663
- `binary/` — as a downloadable artifact on both failing and passing e2e runs.
664
- - `test/e2e/evidence-contract.sh` also pins observed failure classification. The
665
- `class` field is derived, not declared: the recorder compares the attempts
666
- already recorded for the same `binary_sha256` under `--evidence-root` and only
667
- falls back to a caller's `--class` when they say nothing. A build seen both
668
- failing and passing is nondeterministic and splits by whether the source of
669
- that nondeterminism is ours — `flake` for a scenario we own, and
670
- `unowned-nondeterminism` for one like `C01` that observes the real Codex model
671
- — so a later quarantine policy never sets a deadline nobody here can meet. A
672
- failure reproduced on the same build is `deterministic-regression`; a failure
673
- nobody has retried is `unrepeated-failure` and no longer borrows the
674
- reproduction claim. Where a derivation happened the record carries the
675
- additive optional `class_basis` and `prior_outcomes`; where it did not, the
676
- record keeps its exact prior field set and bytes. `e2e-evidence.py classify`
677
- exposes the predicate as a truth table, and `e2e-evidence.py flake-rate <dir>`
678
- reports per-scenario flake rate over downloaded attempt evidence plus an
679
- `E2E_FLAKE_FLIP` line for every stored class the other attempts contradict.
680
- L06 diagnostics are derived the same way: `attribution` names the event-time
681
- racer outcome (`registry-lock-deadline`, `command-failure`, no observed racer,
682
- or no racer failure) with no `other`/`unknown` fallback. Holder held/released
683
- timestamps and racer PID/start/finish values are captured while those processes
684
- run; a leftover compatibility marker cannot invent holder identity or timing.
685
- The cardinality-neutral payload travels as `projmux.e2e-diagnostic/v3` while the
686
- unchanged L08/L16 payloads stay `v1`, and evidence retained from before the
687
- rename still validates and still aggregates. `PROJMUX_E2E_ATTEMPT` now defaults to `GITHUB_RUN_ATTEMPT`, so a rerun's
688
- evidence is no longer written as attempt 1.
689
- - `make test` / `go test -race` / `make test-e2e`: the Registry mutation lock
690
- succeeds on a deadline, not on an attempt budget. `acquireLockWithDeadline`
691
- blocks on `flock(LOCK_EX)` against a persistent `registry.json.flock`
692
- descriptor so the kernel owns the wait queue, keeps writing the legacy
693
- `registry.json.lock` O_EXCL marker for the compatibility window an older
694
- install still observes, and treats a marker as stale only when its recorded
695
- pid is gone rather than after 30s of wall clock. The separate recovery lock
696
- (`acquireRecoveryLock`) keeps its 200-attempt budget and wall-clock stale
697
- window. `TestRegistryLockFailsOnItsDeadlineNotOnAnAttemptBudget` pins
698
- `ErrLockTimeout` and the absence of any attempt count in the failure,
699
- `TestRegistryLockDeadlineIsMeasuredOnTheInjectedClock` spends a whole budget
700
- in simulated time, `TestRegistryLockReclaimsAMarkerOnlyWhenItsOwnerIsGone`
701
- reclaims a reaped owner at once while a live, empty, malformed, or unreadable
702
- owner is never reclaimed, `TestRegistryLockKeepsMutualExclusionWithAMarkerOnlyInstall`
703
- covers the compatibility window in both directions, and
704
- `TestRegistryLockGrantsEveryWriterUnderDeliberateContention` plus the fixed-clock
705
- `TestConcurrentUpdatesSerializeThroughTheRegistryLock` require every contending
706
- writer to be granted with no overlapping critical sections. The degraded-mode
707
- gate in front of an ordinary mutation is now a suspicion confirmed under the
708
- lock rather than an immediate refusal: a commit publishes the initialized
709
- marker before it renames the staged registry into place, so an unlocked
710
- observer could read a healthy neighbour's in-flight commit as state loss and
711
- refuse a writer that never reached the lock.
712
- `TestAConcurrentWriterIsNotRefusedInsideAnotherCommitsMarkerWindow` builds that
713
- window directly and requires the second writer to wait through it. The
714
- `TestHostStartupAndShutdownHaveDeterministicHappensBefore` pins `StartHost`
715
- return as the startup barrier: immediate and concurrent `Close` stop accept
716
- before waiting for sessions, stop the broker before publishing the shared
717
- terminal `Done`, and return every caller through that same channel.
718
- `TestStaleRuntimeArtifactsAreReclaimedOnlyByExactOwnerProof/late_old_host_close_preserves_replacement_artifacts`
719
- keeps shutdown cleanup behind `SameFile`, so an old Host cannot remove or
720
- change a replacement socket or record. The stable `Race Tests` CI aggregate
721
- now fails closed over separate core, Codex broker, and Codex app-server
722
- children; the core child preserves metadata, notify, and recent-window
723
- coverage, and `CIWorkflowContractTest` pins both Codex failure paths red.
724
- `CIWorkflowContractTest.test_required_unit_job_runs_pinned_deadcode_without_bypass`
725
- requires the existing `Unit Tests` job to run `make deadcode` with the module's
726
- pinned Go tool before `make test`, preserving every required job name.
727
- `test_deadcode_failure_fails_unit_and_test_aggregate` executes the workflow
728
- commands with a failing scanner gate and rejects failed, skipped, or cancelled
729
- Unit results in `Test`. `DeadcodeBaselineContractTest.test_stale_current_and_new_finding_are_rejected`
730
- preserves rejection of new findings; neither deadcode baseline is expanded.
731
- Required real-tmux L06 owns only the deterministic holder/released-before-
732
- deadline/waiter convergence guarantee. Eight-way throughput and fairness stay
733
- outside required acceptance in the explicit stress command documented above.
734
- - `make test` / `make test-integration`: attention omitted-target convergence
735
- - `make test` / `make test-e2e`: Closed-Project startup mode selection is one adjudication shared by both entry points. `sidebarStartupPickerState` is the single read-only preference authority: no file resolves to `on/default`, saved `on` and `off` keep their byte meanings, and runtime plus Settings consume the same result. `TestSidebarStartupDefaultPolicyIsSharedReadOnlyAndLocaleStable` pins the en-US/ko-KR row/detail golden, action IDs, runtime/Settings parity, missing-file zero-create, and saved bytes/mtime preservation. `TestProjectStartupModeSelectionIsOneDecisionAcrossEntryPoints` drives no-file/on/off × registered/unregistered × explicit choice through the in-process open, sidebar emitter, and re-exec continuation, requiring one matching lifecycle entry. Saved `off` still uses `defaultProjectStartupMode`; `TestSidebarOpenPromotesUnregisteredRootToFreshWhenPickerIsOff` and `TestSidebarOpenKeepsRegisteredRootOnContinue` preserve that automatic split. No-file and saved `on` preserve explicit choice, including `TestSidebarOpenContinueOnUnregisteredRootKeepsTheSnapshotRefusal`; live-session bypass, `switch sidebar-open --mode` tokens, picker row order, trust ordering, and Continue/Open fresh engines remain unchanged. L11 step 7 removes the preference, observes both native rows, cancels back to Projects with zero config creation, then selects Continue. Step 9 writes exact `off`, forwards the legacy neutral `continue` against an unregistered root, requires Fresh adjudication and a new Project, and proves saved bytes/mtime unchanged on an isolated socket/HOME/XDG/TMUX_TMPDIR.
736
- is enforced by `TestAttentionMutationOmittedTargetMatchesExplicitPaneLedger`,
737
- `TestAttentionMutationOmittedTargetRefusesWithoutExactInvocationPane`, and
738
- `TestAttentionMutationHelpUsesOptionalPaneCatalogUsage`; generated/reference/
739
- guide parity stays pinned by `TestAttentionMutationOptionalPaneDocsParity`,
740
- and generated hook argv by
741
- `TestGeneratedAttentionFocusHooksKeepExplicitPaneArgv`. The unit tables pin
742
- three-verb explicit/omitted handler-ledger parity, exact `%N` targeted
743
- reobservation, no targetless tmux calls, and no-write refusal for outside,
744
- missing, blank, malformed, stale, or contradictory inherited evidence. The
745
- isolated real-tmux integration runs omitted toggle→clear and arm against one
746
- inherited Pane with a sibling unchanged, then proves outside/stale failures
747
- have non-zero status, stdout zero, bounded actionable stderr, and unchanged
748
- attention state.
749
-
750
- ### Codex app-server generation Phase 0 tests
751
-
752
- - `TestCodexEndpointAndAuthorityRefsRoundTripExactlyAndIgnorePaneGeneration`,
753
- `TestLegacyCodexRefsRemainGenerationUnavailableWithoutInference`,
754
- `TestLegacyHookReobservationPreservesAnExactCodexEndpointRef`, and
755
- `TestCodexEndpointIdentityRejectsNonCanonicalWhitespace` own the additive
756
- Registry schema: refs reopen exactly, remain independent of Pane generation,
757
- reject noncanonical identities, preserve future exact refs across legacy
758
- hook observations, and never infer a current endpoint for legacy state.
759
- - `TestBoundedPoolRejectsEveryInvalidV1Topology`,
760
- `TestBoundedPoolStateSpaceProperty`, and
761
- `FuzzBoundedPoolNeverAcceptsMoreThanTwoLiveSlots` own the current-one plus
762
- draining-one state model.
763
- - `TestCompositeAuthorityRejectsSameNumberCrossGenerationAndLegacyWithZeroWrites`
764
- and `TestOldStopBarrierKeepsSuccessorResumeAtZeroUntilSafe` own the Phase 0
765
- write-zero fences for equal numeric epochs, missing generation identity, and
766
- same-thread successor resume before old-stop.
767
- - `TestReadOnlyUpgradePlanIsRepeatableContentFreeAndNamesExactBlockers` freezes
768
- exact JSON/text blockers and all five zero mutation counters;
769
- `TestReadOnlyUpgradePlanNeverRendersInvalidIdentitiesOrQualificationFields`
770
- keeps hostile invalid input out of both projections.
771
- - `TestQualificationDecisionTableClosesPhase2OnEitherSharedStateFailure` and
772
- `TestQualificationReceiptRetainsOnlyContentFreeAllowlistedFields` own the
773
- persisted go/no-go and redaction schema.
774
- - `TestContentAddressedBundleSurvivesSourceRemovalForServerTUIAndHelpers`,
775
- `TestBundleDriftAndProtocolMismatchRefuseBeforeFinalCommit`, and
776
- `TestCommittedBundleTamperingIsRefusedBeforeLaunch`,
777
- `TestCommittedBundleRefusesMatchingSymlinksBeforeLaunch`, and
778
- `TestBundleRefusesNonCanonicalRootsAndVersions` own immutable complete
779
- release-bundle retention and refusal, including manifest/artifact links and
780
- noncanonical mutation roots.
781
- - Opt-in `TestInstalledIsolatedGenerationPoolQualification` is the
782
- declared-pair shared-state conformance. The pair under test comes from
783
- `PROJMUX_CODEX_GENERATION_OLD_VERSION`/`_NEW_VERSION` and each declared
784
- version must match what its binary reports; the canonical receipt is written
785
- to `PROJMUX_CODEX_GENERATION_RECEIPT` before the verdict is asserted, so a
786
- refusal is on disk too. It strips inherited tmux identity, uses one unique
787
- private state root and two private sockets, records no provider content or
788
- secrets, requires distinct-thread overlap plus the old-stop same-thread
789
- barrier, tallies the receipt's coverage counters from the ledgers it already
790
- writes, and performs exact cleanup. See
791
- [codex-generation-pool.md](codex-generation-pool.md).
792
- - `TestQualificationGateRefusesEvidenceCountersNoObservationBacks`,
793
- `TestQualificationEvidenceIntegrityIsFixedByCounterAndClaim`, and
794
- `TestQualificationSchemaVersionRefusesTheCoverageFreeReceipt` own the
795
- evidence-counter integrity the gate checks and `Validate` cannot: a
796
- self-consistent YES whose coverage counters back nothing is refused as forged,
797
- and a coverage-free version-1 receipt does not decode.
798
- - `TestActivateManagedCurrentRefusesEveryUnqualifiedRequestBeforeDraining`,
799
- `TestActivateManagedCurrentPublishesTheQualificationItRanUnder`,
800
- `TestActivateManagedCurrentRefusesAnExistingPoolWithNoReceipt`,
801
- `TestResumeRefusesAnUnqualifiedPairBeforeAnyHandoverEffect`, and
802
- `TestResumeStillRetiresAVacantGenerationWithoutAReceipt` own the two entry
803
- gates: each refuses before its own irreversible step with a path-specific
804
- token, the accepted receipt is published into the journal, and the vacancy
805
- lane the planner leaves open stays open.
806
- - `TestQualificationStoreAnswersOnlyForThePairAReceiptNames`,
807
- `TestQualificationStoreRefusesAFileThatIsNotItsOwnReceipt`,
808
- `TestQualificationStorePathStaysInsideItsOwnDirectory`,
809
- `TestUpgradeQualifyCarriesAProducedReceiptToTheEntryPathStore`,
810
- `TestUpgradeQualifyStoresNothingItWouldNotHonor`, and
811
- `TestUpgradeQualifyRequiresExactlyOneAbsoluteReceipt` own the route from a
812
- produced receipt to the store the entry paths read.
813
- - `TestDeclaredGenerationPairAcceptsAnyDistinctReceiptVersionPair`,
814
- `TestDeclaredGenerationPairRefusesEmptyEqualAndNonVersionTokens`,
815
- `TestEmittedReceiptFileRoundTripsToTheIdenticalResult`,
816
- `TestEmittedReceiptReplacesAnEarlierVerdictAndRefusesRelativePaths`, and
817
- `TestGenerationQualificationSourcesCarryNoVersionLiteral` own the declared
818
- pair and the receipt file: any distinct receipt version pair is accepted, the
819
- emitted file is `0600` and byte-identical to the canonical result, a rerun
820
- never republishes a stale verdict, and neither harness source may pin a
821
- version literal again. Only the pair is declared — every evidence boolean and
822
- counter stays measured.
823
- - `TestEmittedQualificationReceiptEntersTheUpgradeRequestVerbatim` carries the
824
- emitted receipt file into the `qualification` field of an `agent app-server
825
- upgrade --request` document and reloads it through the real request loader.
826
- The declared pair, every counter, `Validate`, and `GateQualification` survive
827
- the hop, and a tampered evidence counter stops decoding instead of keeping
828
- its verdict.
829
- - `CIWorkflowContractTest.test_generation_pool_lane_is_dispatch_only_and_fail_closed`
830
- and `test_generation_pool_runner_types_every_terminal_outcome` own the
831
- dispatch-only lane and `scripts/test-generation-pool-qualification.sh`: the
832
- lane never schedules, never joins a required aggregate, always uploads its
833
- typed record, and is green only on a measured `pass`; the runner refuses a
834
- non-version pair before creating any root and still types an unavailable
835
- binary as `unsupported`.
836
-
837
- ### Codex app-server generation Phase 1 tests
838
-
839
- - `TestGenerationLifecycleProjectionClosedTable` and
840
- `FuzzGenerationLifecycleProjectionMatchesClosedTable` own the single 6×7
841
- effective-interaction × generation-state tuple. Planned drain, handover,
842
- recovery, and blocked projections require an exact operation endpoint;
843
- `TestPlannedGenerationProjectionRequiresExactDurableOperationRef`,
844
- `TestMarkerlessCrashAndVersionDriftRemainOrdinaryFailure`,
845
- `TestMarkerlessCrashAndVersionDriftRemainOrdinaryFailureThroughProductionReconcile`, and
846
- `TestLifecycleProjectionAuthorityInputsExcludeProcessHeuristics` keep
847
- process exit, version, executable, path, socket, and PID evidence from
848
- becoming maintenance authority.
849
- - `TestRuntimeMutationEquivalenceTableIsClosed`,
850
- `TestRuntimeMutationClassesMatchDecisionKernel`, and
851
- `TestRuntimeMutationCompositeFenceAndSiblingRecorder` own the eight
852
- owner/foreign × current/stale × target/sibling classes. Exactly one class
853
- has a semantic effect; equal numeric epochs from another endpoint generation
854
- or restarted broker invoke neither Registry nor tmux writer.
855
- - `TestGenerationLifecycleSinkCompositeAuthorityHasZeroCrossWrites` exercises
856
- the application sink with a Registry, fake tmux, and the exact-Pane authority
857
- fence. Endpoint generation, broker runtime, connection epoch, binding epoch,
858
- and stored operation mismatches return the managed-observation refusal with
859
- zero Registry and tmux writes. A syntactically valid provider-supplied
860
- operation ID cannot replace the durable operation authority.
861
- - `TestNativeSemanticApplyAndInvalidationShareExactPaneFence` deterministically
862
- pauses an older provider-control Apply after its Registry commit and before
863
- its Pane writes, starts disconnect invalidation behind the same exact-Pane
864
- fence, and requires invalidation to remain the final Registry/Pane tuple.
865
- It then pins the recovery order separately: a replacement snapshot may Apply
866
- while authority is `invalidating`, followed by the exact ready authority.
867
- - `TestGenerationLifecycleProjectionReconcileWritesOnceThenZero`,
868
- `TestGenerationLifecycleProductionReconcileRejectsForeignOrSiblingAuthorityWithZeroWrites`, and
869
- `TestGenerationLifecycleProjectionUsesIsolatedRealTmuxAndExactCleanup` own
870
- production fake and isolated-real-tmux convergence for draining,
871
- handover-pending, recovering, and blocked tuples. They prove the durable
872
- lifecycle/activation input survives a later full resource reconcile and
873
- makes its second pass zero-write. The real fixture strips inherited
874
- `TMUX`/`TMUX_PANE`, starts on one unique contained physical `-S` path, accepts
875
- that path only from the successful `new-session` Pane/socket receipt, waits
876
- on tmux command completion as its semantic barrier, and kills/removes only
877
- that queried and containment-proven socket; there is no logical-name cleanup
878
- fallback.
879
- - `TestPlanOnlyMutationProductSurfaceInventoryIsBidirectionalAndClosed` keeps
880
- generation presentation under the existing `agent.presentation` typed
881
- surface and requires the complete composite fence in its guard. Existing
882
- pure reducer PBT, Phase 0 endpoint/authority tests, broker reconnect tests,
883
- and C01 remain their canonical unique boundaries. The complete
884
- old/new-mutant migration receipt is in
885
- [codex-generation-pool.md](codex-generation-pool.md#mapping-and-authority-test-migration-ledger).
886
-
887
- ### Provider capability matrix Phase 0 tests
888
-
889
- - `TestAgentCapabilityCatalogIsClosedCartesianMatrix`,
890
- `TestAgentCapabilityCatalogPinsCurrentGroupsAndDeferredVocabulary`, and
891
- `TestAgentCapabilityCatalogPinsCodexNativeAndSharedFamilies` own the single
892
- action × provider catalog, including all eight app-server generation leaves,
893
- the four unsupported future coordination cells, the four integration
894
- targets, and the three usage providers.
895
- - `TestAgentCapabilitiesProviderProjectionIsStaticAndReadOnly` and
896
- `TestAgentCapabilitiesExactProjectionSeparatesRuntimeEpochAndAvailability`
897
- own the provider-only versus exact-Agent projection and prove that Registry
898
- reads, live transport, and provider writes are not smuggled into static
899
- support. `TestUnsupportedNativeAgentActionsRefuseBeforeRuntimeOrProviderEffects`
900
- owns the pre-runtime non-Codex refusal for turn, approval, and review.
901
- - `TestProviderResumeExecutionPreservesExactAgentAndLaunchesOneExactArgv`
902
- owns the three-provider resume execution matrix: one exact child argv, stable
903
- Agent uid/name, and no fresh Agent creation.
904
- - `TestAgentCapabilityCatalogRoutesMatchExecutableHelpGraph` and
905
- `TestAgentIntegrationAndGenerationLeafHelpMatchesCapabilityCatalog` own
906
- catalog ↔ CLI/help parity. The generated CLI reference test owns the docs
907
- projection; provider-neutral `agent message` and `agent wait` are the only
908
- coordination routes, and provider-specific Agent namespaces remain absent.
909
-
910
- ### Resume Picker exact projmux binding Phase 0 tests
911
-
912
- - `TestAIResumeExactBindingProjectsCoherentAgentAndPaneNames` owns exact
913
- provider/conversation sessionRef selection: prefer a round-trip Pane owner,
914
- then lexical Agent UID, and project that same Agent's context and stable
915
- name with its optional Pane stable name. Duplicate ordering cannot combine
916
- different Agents' fields. Stable names come only from `metadata.name`.
917
- - `TestAIResumeBindingDisplayFallsBackWithoutRoundTripPane` pins the three
918
- binding-first row forms: `[agent → pane] conversation`, `[agent] conversation`,
919
- and the unchanged conversation-only row. Missing/stale/foreign Pane bindings
920
- omit only the Pane; missing Agent bindings retain exact Value, count, and
921
- order. No cwd, topic, live tmux title, newest resource, or pane-order guess
922
- recovers a deleted binding.
923
- - `TestAIResumeBindingNamesPreserveFullSearchAndDetailWithinCellBudget` pins
924
- the existing 90-cell trailing budget, balanced binding brackets, ANSI/CJK
925
- clipping, 128-byte stable names, and full names in SearchKey and selected
926
- detail projection. `TestAIResumeBindingLongNamesAreRecoverableByNativeDetailScroll`
927
- reconstructs complete 128-byte and CJK names from the app-local, 64-cell
928
- continuation lines and verifies native Shift-Down exposes every chunk at
929
- 80 columns in EN/KO. The canonical full binding line remains intact; only
930
- overflowing bindings add continuation fields under Details.
931
- `TestAIResumeBindingNativeFramesLocaleAndSizeGolden`
932
- exercises the public native runner on an isolated PTY/Registry fixture at
933
- 80x24 and 120x40 in EN/KO; short Agent and Pane names are visible together in
934
- both the row and detail. The PTY harness uses Python 3's standard library.
935
- - `TestAIResumeBindingSnapshotIsInvocationLocalAndAddsNoReadsOrWrites`
936
- pins one Registry load per invocation, cached detail reads, unchanged
937
- row/order/Value/footer after focus, zero extra command/file reads and writes,
938
- and next-open conversation fallback after Registry deletion. Binding names
939
- are an invocation snapshot: rename/deletion is observed on the next open,
940
- historical names are not persisted or reconstructed after cascade. Existing
941
- title suppression/precedence, provider settlement/status, exact routing,
942
- and first-frame bound suites remain the authority for those unchanged paths.
943
- ### Resume Picker provider session title Phase 1 tests
944
-
945
- - `TestClaudeExplicitTitleProvenanceWithinBoundedScan` pins top-level
946
- `type:ai-title` / `aiTitle`, first valid explicit title, duplicate stability,
947
- foreign session rejection, blank/full-ID/short-ID rejection followed by a
948
- valid title, and the line-100 inclusive bound with no line-101 read. An
949
- ordinary Claude prompt cannot stop the scan before a later explicit title.
950
- Explicit titles normalize whitespace without prompt-noise filtering.
951
- - `TestCodexFirstRealPromptHasDerivedTitleProvenance` pins the first typed
952
- `user_message` event or user-role response text after scaffold, assistant,
953
- tool-carrier, untyped, and wrong-provider records. The label is marked
954
- `derived-user-prompt`; Claude `ai-title` and Codex app-server non-ID titles
955
- are `explicit-provider-title`. `TestNativeTitleProvenanceRejectsBlankAndIDCandidates`
956
- keeps blank/full-ID/short-ID native candidates without title authority.
957
- - `TestAIResumeTitleAuthorityProviderSourceProvenanceMatrix` closes the
958
- provider × source × provenance × ID/context table. For Claude/Codex the
959
- conversation label order is explicit title, derived Codex rollout prompt,
960
- exact-bound Agent topic, exact-bound Agent provider, localized Untitled.
961
- Canonical titles never lose to the bound Agent's topic/provider; stable
962
- binding names remain independent. Antigravity keeps its existing known-source
963
- title and context-first behavior.
964
- This narrowly supersedes the earlier Resume Picker prohibition on prompt
965
- inference and context-before-provider-title policy: only bounded Codex
966
- typed real-user prompts gain derived display authority, matching the adopted
967
- native Codex resume-title policy. Ordinary Claude prompts, missing/unknown
968
- provenance, unknown/mismatched sources, and ID-like titles remain rejected;
969
- no summary generation or additional transcript read is introduced.
970
- `TestAIResumeMissingProvenanceDoesNotInferConversationTitle` preserves the
971
- rejection boundary when a caller provides only a transcript/rollout string.
972
- - `TestResumeSummaryPreservesTitleProvenanceWithoutAdditionalReads` and
973
- `TestAIResumeTitleDiscoverySummaryRoundTripNeedsNoTranscriptReread` carry the
974
- content-free discriminator through SessionMeta → ResumeSummary → SessionMeta.
975
- Real disposable Claude/Codex discovery is projected after its transcript has
976
- been removed; row/detail title and exact provider/ID Value remain available.
977
- `TestAIResumeBindingSnapshotIsInvocationLocalAndAddsNoReadsOrWrites` now
978
- exercises a canonical title with binding while keeping one Registry load,
979
- cached detail reads, zero extra reads/writes, frozen row/order/Value/footer,
980
- and next-open binding deletion behavior.
981
- - `TestAIResumeProviderTitleBindingNativeFramesGolden` exercises the public
982
- native runner on isolated PTYs at 80x24 and 120x40 in EN/KO for Claude explicit,
983
- Codex rollout derived, and Codex app-server explicit rows. Each source is
984
- selected in turn; the golden requires combined binding/title rows, the exact
985
- provider/title detail heading, and full conversation ID independently of
986
- preview text. Existing Phase 0 binding, provider settlement/latency, privacy,
987
- frozen-frame, and exact-routing suites retain their unchanged contracts.
988
-
989
- ### Codex observer disconnect-reason Phase 0 tests
990
-
991
- - `TestCodexObserverEventLoopExitsCarryTheirOwnReasonToken` owns the exit-path
992
- table: every way out of the observer event loop publishes the vocabulary
993
- token for that path, and the authority writes it pins prove the recovery
994
- strategy did not move when the tokens became precise. A cancelled observer,
995
- a closed notification stream, and a real endpoint disconnect no longer share
996
- a word.
997
- - `TestCodexObserverExitPathsAreOneToOneWithTokens` owns the converse
998
- property: the exit tokens are distinct, neither the `unrecorded` nor the
999
- retired `disconnected` bucket is reachable from an exit, and `hold` is a
1000
- stored decision rather than a comparison against the published reason.
1001
- - `TestCodexObserverReasonVocabularyIsClosed` owns the single vocabulary.
1002
- `codexNativeReason`'s four open-failure tokens are members of it, every
1003
- token round-trips through `safeCodexAuthorityReason`, and anything outside
1004
- it renders as `bounded reason unavailable`.
1005
- - `TestCodexBrokerEpochRecordsWhyItsStreamClosed` owns the close cause at the
1006
- layer that knows it. All five calls that end a broker epoch record a
1007
- different token, which is what separates an upstream connection that went
1008
- away from a rotated barrier, a revoked binding, a backlog overflow, and an
1009
- observer that closed its own connection.
1010
- - `TestCodexObserverJournalRecordsConnectDisconnectReconnectInOrder` owns the
1011
- durable history the pane option cannot hold: connect, disconnect, and
1012
- reconnect records in time order, each carrying the epoch label, and no
1013
- fallback record behind a held disconnect.
1014
- - `TestCodexObserverJournalRecordFieldsAreWhitelisted` is the negative audit.
1015
- It names every column an observer record may carry and refuses an
1016
- out-of-vocabulary reason and an unnamed transition.
1017
- - `TestCodexObserverJournalCoalescesRepeatedTransitions` owns the write-rate
1018
- boundary on the shared `ai-ingest.log` sink: one record per
1019
- (transition, reason) pair per window, with the suppressed count riding the
1020
- next record so the flap rate stays readable.
1021
- - `TestObserverRecoversIntoANewEpochAfterConsumerBacklogOverflow`,
1022
- `TestBacklogOverflowStopIsDistinguishableFromARecoveringInvalidating`, and
1023
- `TestBacklogOverflowRecoverySurvivesATransientBindingReadFailure` own
1024
- recovery after a consumer-side backlog overflow. All three drive the real
1025
- broker runtime, the real binding session, and the real observer loop: the
1026
- observer is held inside one sink write until its epoch's bounded stream
1027
- overruns, which is the actual `end(backlog-overflow)` plus `resync` pair
1028
- rather than an injected close cause. A sibling Agent holds its own binding on
1029
- the same shared connection throughout, because the overflowing binding being
1030
- the broker's last one retires the upstream connection and would make these
1031
- measure an endpoint reconnect instead. The first pins the guarantee - Open is
1032
- called again after the resync, the replacement barrier publishes a new epoch
1033
- label, and the overflowed epoch's records are exactly connected/ready,
1034
- disconnected/backlog-overflow, reconnecting/backlog-overflow. The second owns
1035
- the operator distinction: when the exact binding stops being current the
1036
- scheduler is right to stop, but the Pane keeps the
1037
- `invalidating|<epoch>|backlog-overflow` projection and `SetAuthority` refuses
1038
- every later correction, so `observer.stopped` with result `stuck` is the only
1039
- surface on which a stuck Pane differs from one still reconnecting on the same
1040
- projection. The third owns the defect behind that shape: `BindingCurrent`
1041
- returns false both for a replaced binding and for a Registry or `tmux` read
1042
- that merely failed, and the recovery scheduler took its only non-cancel exit
1043
- on a single such sample. It now makes the same bounded wait the loop head has
1044
- always made, so a transient read failure no longer retires a live activation.
1045
- - `TestObserverRecoversWhenTheOverflowedBindingWasTheBrokersLast` and
1046
- `TestLastBindingOverflowRecordsEveryFailedReplacementOpen` own the same
1047
- overflow under the opposite premise: no sibling binding, so the resync takes
1048
- the runtime to zero bindings. `Broker.wanted()` is `len(b.bindings) > 0` and
1049
- therefore broker-global, so `serve` folds the shared upstream connection and
1050
- no replacement barrier can exist until it is reopened. That premise is not
1051
- exotic - it holds permanently for a user who runs one Codex pane - and the
1052
- fold itself is a race, because a rebind that lands before `serve` processes
1053
- the unbind wake leaves the connection up. The fixture gates every replacement
1054
- Open on the fold so the premise is a fact rather than a hope. The first pins
1055
- that the fold self-heals whenever upstream is reachable. The second owns the
1056
- defect: while upstream is away every replacement Open blocks to its own open
1057
- timeout - 15s in production - and the scheduler computed the failure's
1058
- vocabulary token and discarded it, so a Pane sat on
1059
- `invalidating|<epoch>|backlog-overflow` with no record of any attempt, which
1060
- is byte-identical to the stuck Pane `observer.stopped` exists to name except
1061
- that recovery really was running and so no terminal record arrived either.
1062
- The failure is now recorded on the retry transition, keyed apart from the
1063
- epoch-loss token so the journal's coalescing window folds a long outage into
1064
- one line plus a repeat count, and the disconnect's reason column is untouched.
1065
- - `TestLastBindingRevokedByOverflowFoldsTheUpstreamConnection`
1066
- (`internal/integrations/agents/codexbroker`) owns the invariant `wanted()`
1067
- exists for, against the involuntary revocation paths. `Bind` and
1068
- `Binding.Close` both signal the supervisor because both change what the
1069
- connection has left to serve; the involuntary paths - a bounded delivery queue
1070
- that overflowed, a thread-scoped snapshot refusal - deleted the binding from
1071
- routing with no wake, so a runtime could reach zero bindings with nothing
1072
- pending on the wake channel and `serve` would stay in its select holding an
1073
- upstream connection nothing is bound to until the host idle timer recycled the
1074
- whole runtime. A one-deep queue makes the overflow exact rather than a volume
1075
- guess, and the test binds exactly one thread and never replaces it, because a
1076
- rebind carries its own signal and would hide the gap.
1077
- - `TestLiveEpochSurvivesATransientBindingReadFailure` and
1078
- `TestLiveEpochBindingLossLeavesATerminalRecord` own the live epoch's
1079
- `bindingTicker` exit, which carried the same single-sample defect the recovery
1080
- scheduler had. One false from `BindingCurrent` ended the observer, and that
1081
- exit wrote no authority - correctly, because `SetAuthority` refuses once the
1082
- predicate is false - and no record, so the Pane kept
1083
- `provider-control-plane|ready` with no producer behind it. That is the inverse
1084
- of a stuck Pane: nothing looks wrong at all. The path now makes the same
1085
- bounded wait as the loop head, and a binding that is provably gone leaves an
1086
- `observer.stopped` record with result `stuck` and reason `binding-replaced`.
1087
- That token is deliberately not `binding-revoked`: the four close-cause tokens
1088
- answer "who closed the stream" and come from the broker epoch, and here the
1089
- stream was never closed at all.
1090
- - `test/e2e/codex-lifecycle.sh` (C01) exercises its actual projection barrier on
1091
- an inert fixture pane with an already-ready or later-ready level and repeated
1092
- option hooks before waiting. An atomic claim shared by the level check and
1093
- hook emits exactly one signal; duplicate `wait-for -S` calls would otherwise
1094
- lose the pending wakeup. The existing barrier deadline is unchanged.
1095
- - `test/e2e/codex-lifecycle.sh` (C01) pins the exact disconnect token at its
1096
- disconnect projection barrier and at the reconnect-gap hook comparison. It
1097
- waited on the literal `disconnected` before, which was the bucket published
1098
- when the observer recorded no reason at all; killing the exact E1 proxy takes
1099
- the upstream connection away, so the projection now reads
1100
- `endpoint-suspended`. The barrier is still an exact string match, so a path
1101
- that stops capturing its reason lands on a different token and fails it. That
1102
- barrier also now gates on `@projmux_attention_state`: the projection writes
1103
- its three pane options as three separate `set-option` calls, so releasing on
1104
- the first two let the five-field assertion 46 lines later read a torn
1105
- projection under load. The assertion is unchanged and still owns the verdict.
1106
- - `TestManagedCodexAuthorityDoctorSeparatesFlappingFrozenAndStopped` owns the
1107
- operator surface. The census carries a count-only reason distribution and
1108
- `doctor` renders it, which is what tells flapping, frozen, and stopped
1109
- apart; `TestManagedCodexAuthorityCensusSeparatesDeclaredFromUnexplainedFallback`
1110
- keeps that distribution pinned alongside the source counts.
1111
-
1112
- ### Claude coordination endpoint Phase 1 tests
1113
-
1114
- - `TestAgentRouteClaudeIdentityTransitions`, `TestClaudeRegistrationAdmissionAndCleanupCAS`,
1115
- and the `TestAgentRoute` / `TestClaudeRegistration` model tests own the sealed
1116
- provider authority union, existing Codex composite fence, exact Claude process
1117
- and registration claims, same-UID rename preservation, and stale registration
1118
- admission/cleanup replay. The property model retains historical routes and
1119
- requires stale replay to leave current state unchanged.
1120
- - `TestClaudeEndpoint` unit tests own private lease probes, kernel peer identity,
1121
- socket owner/mode/inode checks, nested producer refusal, creator-selected
1122
- activation context, read-only capability observation, and token/locator
1123
- residue scans. Private readiness probes never connect to a provider inbox.
1124
- - `test/integration/claude-endpoint-binding.sh`, included in
1125
- `make test-integration`, builds the product and runs
1126
- `TestClaudeEndpointProcessIntegration` through supervisor, activation gate,
1127
- public SessionStart hook, and detached helper. It covers registration
1128
- replacement, exact-version qualification without inheritance, two exact
1129
- auth+frozen-user-frame pushes, provider exit cleanup, and zero token/locator
1130
- residue in output and live lease files. The synthetic socket runs no model,
1131
- account, connector, tool, MCP server, or external service.
1132
- - `TestClaudeEndpointInstalledSourceGate` is opt-in evidence from a disposable
1133
- installed Claude one-shot. It checks public SessionStart identity, exact
1134
- readiness, exit invalidation, empty tools/MCP/plugins, and zero tool-use.
1135
- Raw provider streams remain in memory. This is the Phase 1 registration source
1136
- gate; it does not satisfy or relax Phase 4's separate exact-version,
1137
- hook-enabled, long-lived provider qualification. A safe-mode one-shot cannot
1138
- substitute because safe mode disables the hooks under test. Setup and limits are in
1139
- [claude-coordination-endpoints.md](claude-coordination-endpoints.md).
1140
-
1141
- ### Claude coordination endpoint lifecycle tests
1142
-
1143
- - `TestClaudeEndpoint*`, `TestClaudeCoordinationSocketReplacementInvalidatesWithoutRemovingReplacement`,
1144
- and `TestCleanupClaudeActivationLeasesPreservesCoordinationReplacement` retain
1145
- exact SessionStart registration, process/Pane/generation authority, private
1146
- lease and coord-socket inode cleanup, helper-death invalidation, and
1147
- same-path replacement preservation.
1148
- - `TestClaudeEndpointProcessIntegration` uses a synthetic provider process and
1149
- the real supervisor/helper entrypoints. It accepts exactly the auth line and
1150
- one frozen user frame, completes exact Stop-marker qualification, pushes a
1151
- durable broker record, replaces the registration without inheriting
1152
- qualification, freshly qualifies again, and proves secret/output/file
1153
- residue zero. It runs no model or network provider.
1154
- - `TestClaudeCoordinationServerHasNoWaiterIngressOperations` refuses historical
1155
- `wait`, `waiter-ready`, `begin-handoff`, and `receipt` operations. Integration
1156
- tests require SessionStart registration plus short synchronous Stop reply and
1157
- UserPromptSubmit boundary hooks, with zero `asyncRewake` ingress commands.
1158
-
1159
- ### Provider-neutral message broker Phase 3 tests
1160
-
1161
- - `TestAgentCapabilityCatalogPinsCurrentGroupsAndDeferredVocabulary`,
1162
- `TestAgentCapabilitiesClaudeMessageCellsReflectMissingRegistrationLease`, and
1163
- `TestAgentCapabilityCatalogRoutesMatchExecutableHelpGraph` supersede the Phase
1164
- 0 placeholder boundary: `agent message send|wait|status` and `agent wait` are
1165
- callable through one provider-neutral catalog, while Antigravity and Claude
1166
- inbox claim remain explicit unsupported cells and exact Claude availability
1167
- follows its current registration lease.
1168
- - `TestAuthorityMatrixIsExhaustiveAndPeerNeverEscalates`,
1169
- `TestEnvelopeRetryAndReplyCorrelationRandomizedProperties`, and
1170
- `TestPublicReducerMatchesReferenceModelForRandomSequences` own the exhaustive
1171
- authority table, immutable versioned envelope, exact reversed reply route and
1172
- conversation, idempotent message reference, and terminal-once public reducer.
1173
- Peer input remains untrusted coordination and cannot become a user turn,
1174
- approval, configuration, tool, connector, or model-history write.
1175
- - The `TestStore*` suite owns the private 0700/0600 store, advisory locking,
1176
- atomic replace, bounded payload/file/record count, terminal retention,
1177
- unknown-field refusal, concurrent claim, restart, timeout, and post-handoff
1178
- `failed` plus unknown-outcome contract. Codex claim touches only the exact
1179
- activation-scoped inbox and leaves unrelated Claude records unchanged.
1180
- - `TestAgentMessageSendGatesBothStaticCellsAndRoutesBeforeStoreOrAdapter`,
1181
- `TestAgentMessageUnsupportedProviderStopsBeforeRouteAndStore`,
1182
- `TestAgentMessageSendSameReferenceRetryKeepsBrokerCorrelationAndDoesNotResend`,
1183
- `TestAgentMessageConcurrentSameReferenceUsesOneConversation`, and
1184
- `TestAgentMessageReplyReturnsOnlyToOriginalSourceConversation` pin exact
1185
- current-Pane source authority, exact target eligibility before effects,
1186
- same-envelope retry without resend, and broker-owned reply correlation.
1187
- - `TestCoordinationContentLabelsSourceClaimsForBothProviders` pins claimed,
1188
- unverified source Agent/provider notices, including wrong UID/provider
1189
- fixtures, while preserving exact routing metadata, untrusted payload, and
1190
- peer permission and reply boundaries in both provider contents.
1191
- - `TestAgentMessageSendSourceAnchorAndOmittedFallbackPreserveRouteAndPeerAuthority`
1192
- pins explicit source anchors, omitted-source active Pane fallback, refusal
1193
- without an anchor, and unchanged exact routes and peer authority.
1194
- - `TestAgentMessageSourceAnchorCatalogHelpAndGeneratedDocsParity` pins
1195
- `--source` usage and its anchor (not caller authentication) meaning across
1196
- agent/message/send catalog, help, and generated CLI reference sections;
1197
- `TestGeneratedReferenceMatchesTheCommandManifest` rejects stale docs.
1198
- - `TestClaudePrivateProjectionPreservesPublicBoundaryAndAmbiguity`,
1199
- `TestLiveClaudeAdapterMapsTerminalResponseKindsWithoutDelivery`,
1200
- `TestLiveClaudeAdapterRefusesPublicValidEnvelopeThatExceedsPrivateFrame`, and
1201
- `TestAgentMessageBrokerHasNoCodexOrUserTurnWritePath` own the Phase 2 Claude
1202
- projection, transport-bound refusal, foreign receipt no-op, and zero Codex
1203
- app-server, user-turn, raw-terminal, or model-history write path.
1204
- - `TestAgentMessageWaitClaimsOnlyCurrentExactActivation`,
1205
- `TestAgentMessageClaimDefaultOutputEscapesTerminalControlsAndKeepsEnvelope`,
1206
- `TestAgentMessageWaitDetectsGenerationChangeBeforeClaim`, and
1207
- `TestAgentWaitRegistryIdleReadyTimeoutAndStale` pin full-envelope self-claim,
1208
- terminal-safe output, activation fencing, and deterministic Registry-only
1209
- idle, timeout, and stale waits. Codex `delivered` ends at target self-claim;
1210
- it does not assert model processing or reply.
1211
- - `TestCodexCoordinationPushClassifiesNativeOutcomesForSenders` and
1212
- `TestCodexCoordinationPushExpiresBeforeAnyProviderCall` own the native Codex
1213
- push outcome table: start success and the `turn-in-progress` steer fallback
1214
- deliver as `provider-turn-push`, every known zero-write refusal code fails as
1215
- `codex-turn-push-refused`, every ambiguous or unrecognised code fails closed
1216
- as `codex-turn-push-outcome-unknown`, an unconfigured seam, a failed content
1217
- render and an unavailable binding fail with their own causes, and a
1218
- pre-dispatch deadline expires as `deadline-expired` with zero binding
1219
- resolutions and zero control calls. The refusal code is the discriminator and
1220
- `stale-turn` keeps its per-operation split.
1221
- - `TestCodexCoordinationPushFailuresExitNonzeroWithCauseAndStatusParity` pins
1222
- the sender contract: `agent message send` to a Codex target prints
1223
- `ref/state/reason/action` and then exits nonzero without a usage dump when the
1224
- native push did not deliver, and `agent message status` reports the same state
1225
- and reason. A failed terminal store write keeps the original cause token on
1226
- the receipt, and a delivered turn whose write failed projects
1227
- `broker-delivery-persist-failed` with an unknown outcome; those two cases
1228
- cannot reach status parity, because the store write is what failed.
1229
- - `TestCodexCoordinationPushKeepsTerminalReceiptAndSkipsExtraSteer` pins no
1230
- steer after a successful start, no new event or delivery change from a second
1231
- push over a terminal record, and a delivered reason that names only the
1232
- provider turn push, never model consumption, acknowledgement, or completion.
1233
- - The offline Claude dialogue fixtures reply to a Codex source, so they now
1234
- accept a terminal Codex push failure receipt from the qualification reply --
1235
- the four-field `ref/state/reason/action` line for a fresh reply ref, read
1236
- instead of the exit code -- because no Codex app-server exists in those
1237
- fixtures.
1238
-
1239
- ### Heterogeneous Agent dialogue Phase 4 tests
1240
-
1241
- - `TestClaudeCoordinationPrivateBridgeRequiresExactV5Route`,
1242
- `TestClaudeCoordinationV1ThroughV4HelpersCannotReceiveV5Traffic`, and
1243
- `TestHeterogeneousDialogueLifecycleUpgradeFenceMatrix` pin public/private v5
1244
- route equality, old generation, same-generation old incarnation, stale
1245
- provider process, foreign socket, helper exit, Codex endpoint replacement,
1246
- and fresh current-version requalification. Old routes write/claim zero; a new
1247
- endpoint writes zero before qualification and exactly one after it. The v4
1248
- fence applies only to the existing coordination UDS protocol: v3 Stop-qualified
1249
- helpers cannot pass current lease/eligibility checks. The frozen vendor
1250
- auth-plus-user frame is unchanged; recovery retains the public same-UID path.
1251
- - `TestHeterogeneousDialogueIdleActiveSafeBoundaryMatrix` and
1252
- `TestClaudePushDuringOpenHumanTurnNeverCorrelatesThatTurnsStop` preserve
1253
- immediate idle/active push without a waiter or interruption. Every ordinary,
1254
- recursive, late, or unrelated Stop publishes zero replies.
1255
- - `TestClaudeQualificationRequiresBrokerChallengeAndExplicitReply` requires a
1256
- stored original challenge, exact current source and target, a full frozen
1257
- frame, and a broker-committed explicit reply before opening the general reply
1258
- execution gate. Current qualification responses stay pending across ordinary
1259
- delivery and Stop text, become qualified after the exact reply, and report
1260
- helper restart after close while the execution gate refuses further actions.
1261
- - `TestClaudeExplicitReplyRejectsForeignStaleAndAlteredCorrelationBeforeCommit`
1262
- covers foreign refs, altered conversations, stale generations/incarnations,
1263
- reversed-route mismatch, and a wrong qualification response with commit zero.
1264
- - `TestClaudeExplicitReplyKnownZeroManualRetryPublicReceiptsAndReload` and
1265
- `TestStoreExplicitReplyKnownZeroManualRetrySurvivesReload` pin the documented
1266
- [manual reply recovery](agent-message-replies.md): a known-zero failure keeps
1267
- its immutable receipt, same-ref calls write zero, and a fresh corrected ref
1268
- delivers once with the original conversation, reversed routes, deadline and
1269
- peer authority. Public stderr and text/JSON status preserve cause and ref.
1270
- - `TestClaudeExplicitReplyPartialUnknownExpiredAndStaleNeverDuplicate`,
1271
- `TestStoreExplicitReplyNonzeroUnknownAndExpiredNeverRetry`, and
1272
- `TestStoreExplicitReplyRetryKeepsDeadlineAndExactRoute` reject delivered,
1273
- partial/unknown, pending, expired and stale retries, including store reload,
1274
- false-unknown-flag and unrecognized-reason cases; Codex retry policy stays
1275
- unchanged. `TestStoreExplicitReplyConcurrentAttemptsAndCapacityPreserveReceipts`
1276
- permits only one concurrent new attempt and prevents capacity pruning from
1277
- reopening a live reply relationship.
1278
- - `TestClaudeExplicitReplyGuardRetryRetainsQualificationAndRouteFences` keeps
1279
- known-zero recovery behind the existing exact qualification, provider caller,
1280
- route and bounded-command gates. `TestClaudeEndpointProcessIntegration/explicit-reply-known-zero-manual-retry`
1281
- runs the public CLI as the synthetic provider's real descendant: invalid
1282
- content fails with stderr/ref/action, a corrected manual reply reaches the
1283
- provider once, same/different-ref duplicates write zero, and independent
1284
- public status processes preserve both attempts. The provider checks for
1285
- unexpected queued connections before exit.
1286
- - `TestClaudeExplicitReplyAcknowledgementRequiresFreshDispatchProof` refuses
1287
- legacy or malformed acknowledgements without granting a push; replay replies
1288
- use a distinct response kind that older callers also cannot dispatch.
1289
- `TestClaudeExplicitReplyStoreRefusalsKeepCauseAndUnknownReservation` preserves
1290
- bounded busy/capacity/missing/malformed store causes, frees only proven
1291
- pre-write refusals, and keeps an ambiguous persistence attempt reserved.
1292
- - `TestClaudeExplicitMultipleRequestsAndHumanOverlapSelectOnlyNamedOriginal`
1293
- permits an explicit reply to B then A across human activity, rejects a second
1294
- reply to either request, and never chooses correlation by arrival order.
1295
- - `TestClaudeQualificationPublishesOriginalBeforeConcurrentExplicitReply`
1296
- synchronizes an immediate provider response with durable challenge publication;
1297
- reply timing cannot expose a missing original.
1298
- - `TestClaudeExplicitQualificationMissingToolProofOrOriginalWritesZero`,
1299
- `TestClaudeExplicitQualificationPartialAndTimeoutNeverResend`, and
1300
- `TestClaudeQualificationResponseRequiresExactStableClosedShape` pin exact
1301
- version/process/lease evidence, the single Bash execution-gate assertion,
1302
- missing original request, partial write, timeout, and no automatic resend.
1303
- - `TestStoreMigratesV1EnvelopeToUnqualifiedStaleFence` uses literal v1 JSON
1304
- without incarnation fields. `TestStoreReplyIsAtomicCorrelatedAndReplayIdempotent`
1305
- pins reversed routes, original conversation/replyTo, crash-before-rename
1306
- recovery, restart replay, and exactly one committed reply.
1307
- - `TestClaudeBrokerStoreLayoutAndImmutableEnvelopeMismatchAreExact` and the mismatched
1308
- same-reference case require the helper and public CLI to share one canonical
1309
- message store and reject an immutable-envelope mismatch without mutation.
1310
- `TestClaudePushNeverHoldsAndQualificationBrokerFencePrecedesProviderWrite`,
1311
- `TestClaudePushWriteOutcomeAndDurableReceiptAreTerminalOnce`,
1312
- `TestClaudePushRechecksDeadlineAfterDurableHandoffBeforeProviderWrite`,
1313
- `TestClaudeProviderPushFinalRouteCheckPrecedesSoleWrite`,
1314
- `TestLiveClaudeAdapterClosesMalformedSubmitResponsesAsAmbiguousTerminal`, and
1315
- `TestClaudeCoordinationBrokerPersistenceFencesProviderWriteAndIsTerminalOnce`
1316
- compose the persistence proof: durable handoff failure leaves provider bytes
1317
- zero, durable delivered failure after a full write remains ambiguous and
1318
- reply-ineligible, and a successful durable terminal repeats without another
1319
- provider write.
1320
- `TestAgentMessageLifecycleLeavesInteractionAndBadgeAuthorityUntouched`
1321
- carries both in-progress and approval-required Registry projections through
1322
- send/handoff/delivered/reply/claim, proves terminal-once, and proves Registry
1323
- and badge writes remain zero.
1324
- - `TestAIIntegrateClaude*` pins short compatibility Stop and
1325
- `UserPromptSubmit` commands, zero SessionStart/Stop ingress waiter and zero
1326
- `asyncRewake`, v1/v2-to-v3 marker convergence, user-hook preservation,
1327
- remove, and idempotence.
1328
- `TestClaudeAutomaticMigrationPreservesEveryCoordinationVersionUntilExplicitIntegration`
1329
- and `TestObsoleteAndCurrentClaudeCoordinationHooksNeverRunAutomaticMigration`
1330
- keep config apply/install and already-running historical hook children from
1331
- rewriting global settings; only explicit integration converges v1/v2 to v3.
1332
- - `TestAgentCapabilitiesClaudeMessageCellsReflectMissingRegistrationLease`,
1333
- `TestProviderResumeExecutionPreservesExactAgentAndLaunchesOneExactArgv`, and
1334
- the pre-revalidation rows of
1335
- `TestHeterogeneousDialogueLifecycleUpgradeFenceMatrix` compose the rollout
1336
- recovery proof: a missing lease exposes the exact integrate/normal-exit/resume
1337
- action and permits delivery zero; Claude resume rematerializes one exact child
1338
- while preserving Agent UID/name/count; and delivery remains zero until the
1339
- new Ready lease and route incarnation are revalidated.
1340
- - Required selectorless E2E scenario `L20` is the only deterministic full
1341
- heterogeneous round trip. It uses isolated XDG/tmux roots, a payload-free
1342
- long-lived fake Codex child plus typed `StageCodexEndpoint` /
1343
- `BindCodexActivation` setup for one pre-existing synthetic route, and official
1344
- Claude registration plus explicit public reply commands. A public capability barrier proves the exact Codex
1345
- composite authority before send; semantic barriers replace sleeps. The
1346
- scenario requires exact UID/Pane/generation/message/conversation/reply
1347
- receipts, Agent count two, Codex provider writes zero, and pre-removal
1348
- Registry/tmux/helper/socket/process residual checks. Its strict Claude content
1349
- fixture preserves `sourceNotice` and verifies claimed/unverified source,
1350
- caller-authentication limits, and untrusted payload semantics. It does not
1351
- require a provider binary, model, network, or version matrix.
1352
- - The real-provider path is opt-in through `scripts/agent-dialogue-canary-setup.py`
1353
- and `docs/heterogeneous-dialogue-canary.md`. It uses public reply-only activation,
1354
- product observer/helper-memory evidence and separate qualification/idle claims.
1355
- Actual model action is compared with the pinned guard and successful broker
1356
- commit; delivered alone is insufficient. Partial setup and run failures share
1357
- the owned pidfd writer barrier. Candidate and runner digests are pinned before
1358
- launch, and success is published only after root removal. Active/human/recovery
1359
- and installed cases remain distinct observations. Provider/version stress is
1360
- separately opt-in and never joins required selectorless E2E.
1361
-
1362
- ### Provider hook pane identity tests
1363
-
1364
- - `TestProviderHookCommandsCarryTheirOwnPaneIdentity` and
1365
- `TestProviderHookPaneArgumentIsProviderNeutral` own the installed hook command
1366
- string: every provider hook is handed the Pane it belongs to through the same
1367
- argument, the argument reads the activation envelope rather than the tmux
1368
- environment, and an app-server shared by several Panes expands it to an empty
1369
- value instead of breaking the hook.
1370
- - `TestCodexIntegrationStillOwnsThePaneBlindHookCommand` and
1371
- `TestCodexIntegrationConvergesThePaneBlindHookCommandForward` own the
1372
- convergence of an already installed pane-blind command: it stays recognized as
1373
- projmux-authored and is rewritten forward instead of being refused as
1374
- hand-edited wiring.
1375
- - `TestMatchAIPaneResolvesTheHandedPaneWithoutTmuxEnvironment` and
1376
- `TestMatchAIPanePrefersTheHandedPaneOverInheritedEvidence` own attribution
1377
- when the hook was handed an identity: the Registry resolves it with no tmux
1378
- server read at all, and it wins over inherited `TMUX_PANE`, cwd, and thread
1379
- evidence.
1380
- - `TestMatchAIPaneKeepsTheEstablishedFallbackWhenNothingWasHandedOver` owns the
1381
- unchanged three-step ladder for hooks that were handed nothing.
1382
- - `TestMatchAIPaneRefusesAHandedPaneThatIsGone` owns the four ways a handed
1383
- identity can be stale and proves none of them redirects the event onto another
1384
- live Pane.
1385
- - `TestAttributionFailureReasonsAreDistinctAndClosed` owns the failure
1386
- vocabulary: an unreachable Pane inventory, a genuine no-match, an unreachable
1387
- Registry, an unregistered Pane, a conversation no Pane holds, and a
1388
- conversation two Panes hold each name themselves, and every token stays a
1389
- bounded phrase.
1390
- - `TestHookIngestRoutesAcceptTheExplicitPaneArgument` owns the route contract:
1391
- `--pane` is optional, an empty value is a valid answer, and a positional
1392
- payload argument is still refused.
1393
-
1394
- ### Shared-host hook attribution tests
1395
-
1396
- A provider host shared by several Panes has no Pane to hand over and no tmux
1397
- environment to inherit, so both the explicit argument and the three-step ladder
1398
- above come up empty for it. These tests own the one further step that reads the
1399
- conversation the Registry already records.
1400
-
1401
- - `TestMatchAIPaneAttributesASharedHostHookThroughTheRegistry` owns the normal
1402
- path: with nothing handed over and no readable Pane inventory, the thread or
1403
- session the payload carries resolves to the live runtime handle of the Pane
1404
- that holds that conversation.
1405
- - `TestMatchAIPaneRefusesAConversationNoPaneHolds` and
1406
- `TestMatchAIPaneRefusesAConversationTwoPanesHold` own the refusals: an
1407
- unclaimed conversation and an ambiguous one both fail by name rather than
1408
- landing on a neighbouring Pane that merely shares a working directory.
1409
- - `TestRegistryAttributionRequiresAnIntactBinding` owns the round trip. A Pane
1410
- that is no longer materialized, one whose Agent no longer points back at it,
1411
- and one whose Agent is gone are all torn state, never attribution targets.
1412
- - `TestRegistryAttributionIsProviderNeutral` owns the shape of the lookup: it
1413
- reads the resource model, so every provider whose conversation the Registry
1414
- records resolves through the same code and none is named in it.
1415
- - `TestMatchAIPaneKeepsTheRegistryStepBehindTheEstablishedLadder` and
1416
- `TestMatchAIPaneLeavesTheLadderReasonWhenThereIsNoConversation` own the order.
1417
- A handed identity, inherited `TMUX_PANE`, and the Pane inventory each still
1418
- answer first, and a payload carrying no conversation keeps the ladder's own
1419
- failure reason instead of being relabelled.
1420
- - `TestRegistryAttributionReportsAnUnreachableRegistry` and
1421
- `TestRegistryAttributionDefinesNoBinding` own the boundary: the step reads the
1422
- Registry exactly once, writes nothing, defines no binding, and reports an
1423
- unreachable Registry as its own failure.
1424
- - `TestCodexHookIngestAttributesASharedHostEventThroughTheRegistry` owns the
1425
- whole route rather than the matcher alone: a payload arriving with an empty
1426
- `--pane` produces a record naming its own Pane, and an unclaimed conversation
1427
- produces a record naming why it has none.
1428
-
1429
- ### Hook reflection write tests
1430
-
1431
- Attribution and reflection are two layers. A hook that found its Pane still has
1432
- to write the event onto it, and every one of those tmux writes used to discard
1433
- its error — 47 of them across `internal/app/ai.go` and
1434
- `internal/app/ai_ingest.go`. A hook whose route to tmux was broken therefore
1435
- produced a `result:"state"` record: the log reported a delivery that never
1436
- happened, which is a stronger failure than an unattributed hook, because an
1437
- unattributed hook at least says so.
1438
-
1439
- These tests own the reporting, not the repair. Retry, recovery, and the route
1440
- decision itself are separate concerns.
1441
-
1442
- - `TestHookRecordRefusesToReportADeliveryThePaneWritesMissed` owns the record in
1443
- the three shapes an operator meets: nothing lands, only markers were ever
1444
- written, and — the live one — the reflection lands through a working route
1445
- while the markers written without it do not. All three end as `error` with the
1446
- bounded reason that names which kind of write failed, and the Pane they were
1447
- attributed to stays in the record.
1448
- - `TestHookRecordIsUnchangedWhenThePaneWritesLand` owns the normal path. Same
1449
- result word, same empty reason, and the same number of attempted writes as the
1450
- broken route — the writes stay best-effort and the sequence of attempts did
1451
- not change.
1452
- - `TestPaneWriteFailureReasonIsAClosedTokenCarryingNoOpaqueValue` owns the
1453
- vocabulary. There are two tokens and the split is structural — the reflection
1454
- of the event, whose failure its caller reports, and the markers laid down
1455
- alongside it, whose callers have no error channel — because those two really
1456
- do fail apart. tmux answers a rejected write with an exit status and a socket
1457
- path; the classification keeps both out of either token, and the log file is
1458
- checked for the fragments directly.
1459
- - `TestObserverRecordsAreNotColouredByAReflectionWriteFailure` owns the
1460
- boundary. The observer journal writes into the same file from a process that
1461
- lives for hours, and its records never inherit a failure this process saw on
1462
- another path.
1463
- - `TestHookIngestSourcesNeverDiscardAReflectionWriteError` is the machine check
1464
- that keeps the fix. It parses `ai.go`, `ai_ingest.go`, and `ai_pane_write.go`
1465
- and fails on any tmux write whose error is dropped, because that defect is
1466
- invisible by construction: the record it produces claims the hook succeeded.
1467
- Its scan list is those three sources and its name says so; the package-wide
1468
- equivalent belongs to the recurrence gate, so green here is not a claim about
1469
- any other file.
1470
-
1471
- ### Settled Codex authority admission tests
1472
-
1473
- `aiCodexLifecycleSink.SetAuthority` publishes one native authority transition as
1474
- three separate tmux writes — `@projmux_codex_authority`, then
1475
- `@projmux_codex_authority_epoch`, then `@projmux_codex_authority_reason` — under
1476
- a per-Pane kernel fence. That fence serializes writers against each other only.
1477
- Turn and approval admission reads the same three options with one
1478
- `display-message`, so a read that lands between two of those writes can see an
1479
- authority that is already `provider-control-plane` paired with the epoch or
1480
- reason of the transition being replaced, and refuse the turn for an unavailable
1481
- epoch while the native connection is alive.
1482
-
1483
- Admission now takes that same fence for the live read, so the triple it judges
1484
- is one completed transition:
1485
-
1486
- - `TestSettledCodexAuthorityAdmissionIgnoresTornAuthorityWrites` owns the
1487
- mid-write-to-verdict mapping. Each row injects the triple that exists while
1488
- the writer still holds the fence, records whether judging that triple directly
1489
- would have refused a live connection, and then asserts the verdict reached
1490
- from the settled triple. A control plane that is genuinely lost — the provider
1491
- hook, a pending connection, an invalidation with no epoch — still refuses, and
1492
- the refusal still names the observer's reason.
1493
- - `TestCodexAuthorityAdmissionTakesTheExactWriterFence` owns the fence identity.
1494
- The reader derives the same lock file as the writer, waits while a transition
1495
- is in flight, holds it exclusively while it samples, and gives the wait up at
1496
- the control deadline rather than stalling `projmux agent turn` behind a wedged
1497
- transition.
1498
- - `TestConcurrentCodexAuthorityTransitionsNeverExposeATornAdmissionSnapshot`
1499
- owns the race. A transition writer and the real admission path run
1500
- concurrently, and every snapshot admission read must be one of the complete
1501
- triples the writer published; a control-plane authority with no epoch is the
1502
- false refusal this closes.
1503
-
1504
- The fence covers the live read only, not the judgment that follows it. A
1505
- transition that lands after admission returns is not prevented: the control
1506
- epoch still owns provider-side fencing, and `revalidateControlConsumerFence`
1507
- still re-checks the Registry half immediately before the transport call.
1508
- Admission also does not reduce how often the native connection drops, and it
1509
- says nothing about the `@projmux_ai_state` / `@projmux_ai_badge_kind` /
1510
- `@projmux_attention_state` triple, which has no fence of its own.
1511
-
1512
- ### Inherited pane identity refusal tests
1513
-
1514
- A provider host shared by several Panes inherits the activation envelope of
1515
- whichever Pane happened to launch it, tmux environment included. The `--pane`
1516
- argument therefore arrives holding a live, perfectly resolvable Pane that
1517
- belongs to a different provider, and resolving it verbatim attributes the event
1518
- to that Pane. Having an explicit value and owning it are different things; only
1519
- the Registry, which already records which provider an Agent runs, can tell them
1520
- apart.
1521
-
1522
- - `TestResolveExplicitAIPaneRefusesAnInheritedForeignIdentity` owns the decision
1523
- itself. Both spellings of the handed identity are refused whenever the
1524
- Registry positively records a different provider for that Pane, in every
1525
- direction between the catalogued providers, so the rule is symmetric rather
1526
- than a guard around one of them.
1527
- - `TestMatchAIPaneNeverAttributesAnInheritedForeignIdentity` owns the same
1528
- fixture through the whole matcher: with nothing else able to answer, a refused
1529
- inherited identity ends as a named failure and never as the foreign Pane.
1530
- - `TestMatchAIPaneKeepsAnExplicitPaneWithNoRecordedProvider` owns the silence
1531
- case. A Pane the Registry records no provider for — an unbound shell where
1532
- somebody ran a provider by hand — is not a contradiction, and neither is an
1533
- unrecognized spelling on either side; all of them resolve exactly as before.
1534
- - `TestMatchAIPaneContinuesPastARefusedInheritedIdentity` owns the priority
1535
- ladder behind the refusal: the Registry conversation record answers first, the
1536
- established inventory ladder answers behind it, and only a fall-through that
1537
- also fails ends as a failure.
1538
- - `TestForeignExplicitFallThroughIgnoresTheInheritedEnvironment` owns the one
1539
- step the fall-through deliberately skips. `TMUX_PANE` arrives in the same
1540
- envelope whose Pane identity was just refused, so honouring it would
1541
- reintroduce the misattribution through a second door; the path that was handed
1542
- nothing keeps that step exactly as before.
1543
- - `TestForeignExplicitRefusalLeavesTheRegistryPathUnchanged` is the negative
1544
- audit. The stage writes nothing and defines no binding, and the Registry
1545
- conversation index returns the same map whether or not an Agent records a
1546
- provider, so the shared-host attribution path above is unchanged.
1547
- - `TestAttributionFailureReasonsAreDistinctAndClosed` additionally owns the two
1548
- new tokens: the refusal itself, and the composite the matcher reports once the
1549
- fall-through has also failed. The composite is its own entry rather than the
1550
- downstream step's reason, so a record still says the hook was handed somebody
1551
- else's Pane. Every token stays provider-neutral in wording.
1552
-
1553
- The fall-through has a second door of its own. Its inventory step matches on
1554
- working directory alone, and where every provider Pane sits in the same
1555
- repository that step would hand the event straight back to a Pane of the very
1556
- provider just refused. Phase 8 is what routes a foreign-refused hook into that
1557
- ladder at all, so the coherence check is applied once more to whatever the
1558
- fall-through resolves — to the answer, never inside the shared ladder.
1559
-
1560
- - `TestForeignExplicitFallThroughDoesNotLandOnAnotherProvidersPane` owns that
1561
- door in both directions: a cwd-sharing Pane of the refused provider is not
1562
- taken, and the attempt ends with its own reason rather than with that Pane.
1563
- - `TestForeignExplicitFallThroughStillTakesACoherentLadderAnswer` owns the
1564
- fail-open direction that keeps the Phase 2 contract intact: a ladder answer
1565
- running the hook's own provider, one the Registry records no provider for, and
1566
- one recording an unrecognized spelling are all still taken.
1567
- - `TestForeignExplicitFallThroughDoesNotTakeAForeignConversationPane` owns the
1568
- same check on the Registry conversation record, and proves refusing it does
1569
- not consume the inventory ladder's turn behind it.
1570
- - `TestNoExplicitPathStillTakesACwdMatchRegardlessOfProvider` is the proof that
1571
- the shared ladder is untouched: with no explicit value there is no refused
1572
- envelope and therefore no coherence question, and the cwd and thread steps
1573
- resolve exactly as before for every provider.
1574
- ### Codex control-plane regression-detection gate tests
1575
-
1576
- Three control-plane defects lived through a neighbouring eight-phase track
1577
- closing green from end to end. Nothing reached those surfaces, and the one test
1578
- that did — `test/e2e/codex-lifecycle.sh` — asserted the literal `disconnected`
1579
- for a managed Codex Pane's authority reason. `disconnected` is the bucket that
1580
- means no reason was captured, so capturing a real one would have turned the job
1581
- red. Shipping the fixes without this gate would leave the same blind spot under
1582
- new code.
1583
-
1584
- The gate has three parts.
1585
-
1586
- - `TestControlPlaneTestsNeverExpectAnUncapturedReason` sweeps every
1587
- `internal/**/*_test.go` and `test/e2e/*.sh` for values that mean "nothing was
1588
- recorded" — `unrecorded`, the pre-instrumentation `disconnected` literal, the
1589
- `bounded reason unavailable` read fallback, and the `target-unmatched`
1590
- operations bucket — and fails on any that is an expectation rather than an
1591
- input. A survivor is admitted only by an `uncaptured-default: <reason>`
1592
- comment on its line or in the comment block above it; every current survivor
1593
- is a different vocabulary that happens to spell a token the same way.
1594
- - `TestControlPlaneContractCellsNameLiveTests` resolves every test named in
1595
- `codexControlPlaneContractEnforcement` against the declarations in the tree.
1596
- Squash merges erase the branch that tied a fix to the test holding it, so a
1597
- contract cell naming a renamed test reads as enforced while enforcing nothing.
1598
- - `TestControlPlaneContractEnforcementCoversEverySurface` holds the two
1599
- directions of that map closed: every rendered surface names a contract, and
1600
- every contract in the map names a surface the diagnosis renders.
1601
-
1602
- `projmux doctor` gained the operator half under **Codex control-plane
1603
- surfaces**. Seven verdicts, one per surface, each with the counters it was
1604
- reached on:
1605
-
1606
- - `TestCodexBrokerDiagnosticsSurfaceNamesThePublishedButUnreachableRuntime`
1607
- separates a domain that published nothing — the ordinary resting state — from
1608
- one whose published endpoint answered nothing, which used to render as
1609
- `absent` and was read as "no broker is running".
1610
- - `TestHookAttributionSurfaceSeparatesTotalFailureFromPartial` keeps "no hook
1611
- reached its Pane" apart from "a few did not". `TestAttributionHealth*` own the
1612
- bounded tail read of `ai-ingest.log` behind it, including that an unreadable
1613
- log is reported as unobserved rather than as zero failures.
1614
- - `TestObserverReasonSurfaceRefusesToCallAnUncapturedReasonHealthy` is the
1615
- product-side half of the sweep: a reason distribution made entirely of
1616
- uncaptured tokens is never a healthy row.
1617
- - `TestConnectionContinuitySurfaceReportsCumulativeReconnects` reports the count
1618
- as the total it is and derives no rate from a single sample.
1619
- - `TestTurnAdmissionSurfaceReportsATornAuthoritySnapshotAsBroken`, with
1620
- `TestSettledAuthorityObservationNamesATornTripleOnlyUnderTheFence` and
1621
- `TestAuthorityFenceObservation*`, owns the C-5 read. Doctor samples each
1622
- Pane's authority triple under a **shared, non-creating** hold of the writer's
1623
- own fence, so a torn pairing is a producer fact rather than a transition
1624
- caught in flight. A Pane with no fence file is reported `unfenced`, separately
1625
- from torn: its writer predates the fence, which is a deployment answer.
1626
-
1627
- - `TestHookDeliverySurfaceTreatsAnOpaqueFailureAsBroken` owns the layer after
1628
- attribution. A hook that found its Pane has not yet changed it, and the write
1629
- that follows can fail on its own. A failure naming its cause is a fault an
1630
- operator acts on; one reporting only `exit status 1` says a process ended and
1631
- nothing else. `TestOpaqueDeliveryReasonsNeverReachTheDiagnosis` pins both
1632
- halves of that: the raw string is counted rather than repeated, and a reason
1633
- carrying a path is opaque for the second reason that it is a leak.
1634
- - `TestPaneOwnershipSurfaceReportsAForeignAttributionAsBroken` owns the failure
1635
- that looks like success from every other angle — the event was attributed, the
1636
- write landed, and the Pane it moved belongs to another provider.
1637
- `TestOwnershipHealthCallsOnlyAPositivelyForeignPaneForeign` pins the predicate
1638
- as one-sided and provider-neutral: an attribution is foreign only when the
1639
- Registry positively records a *different* provider, so a Pane recording none
1640
- is not a finding, and the judgment is a table rather than a branch.
1641
-
1642
- Two things the attribution verdict counts, and one it does not.
1643
-
1644
- The section carries the record span its hook rows were counted over. Every
1645
- hook-layer number is cumulative across the whole reading window, and a
1646
- deployment inside that window leaves records from both binaries in the same
1647
- counts — so a repair that lands mid-window moves nothing until its predecessors
1648
- age out, and a reader without the span reads the delay as the repair having
1649
- failed and the eventual drop as time having healed it. Splitting the counts at
1650
- a deployment boundary would need something this reader does not have: the hook
1651
- layer is a short-lived process re-resolved from PATH on every firing, so the
1652
- binary vintage of a long-running process says nothing about which image wrote a
1653
- given record. That is also why the vintage line above qualifies the broker and
1654
- observer rows and not these three.
1655
-
1656
- It is reached **per source** rather than over the total. The defect it detects
1657
- was one provider's, and a busy neighbour attributing everything would carry the
1658
- aggregate and render the dead provider as a few stale panes — which is the
1659
- reading that let it live for eight phases.
1660
-
1661
- It counts only events the contract owed a Pane. `aiPaneMatchRefusals` names the
1662
- answers that decline something the contract never promised — a conversation or
1663
- an explicit Pane that no longer exists — and those are reported beside the
1664
- failure count, never inside it.
1665
- `TestAttributionHealthSeparatesAContractualRefusalFromAFailure` pins the split.
1666
- A retired conversation whose thread keeps firing hooks is the mechanism working;
1667
- reporting it as breakage made `doctor` call a machine behaving to spec broken,
1668
- and a gate that cries wolf gets switched off — which is how the defects this
1669
- section exists to catch survived next door. What stays a failure is the
1670
- mechanism not answering: an unreadable inventory or Registry, or the ladder
1671
- running over readable data and finding nothing, which is the shape a re-broken
1672
- hook identity would take.
1673
-
1674
- `TestEverySurfaceDeclaresWhatItsNumbersAreOver` and
1675
- `TestEverySurfaceRendersTheScopeItDeclares` fold four separate corrections into
1676
- one rule. Each of the four was the same mistake wearing a different face: a
1677
- refusal the contract never promised counted among the failures it did; a judged
1678
- count standing without its population; a lane that writes nothing sitting in a
1679
- write rate's denominator; records from two binaries counted in one window. Every
1680
- number was arithmetically right and none said what it ranged over, so a reader
1681
- supplied the wrong range — and all four were found by a person, none by a test.
1682
- A surface must now declare the phrase its rendering carries to answer "over
1683
- what", and adding one without that answer fails the completeness check. The
1684
- rule cannot judge whether a scope is *correct*; it makes stating one
1685
- unavoidable, which is what none of the four had to do.
1686
-
1687
- `TestHookReflectionWritesNeverDiscardTheirErrorSilently` covers what none of the
1688
- log-derived verdicts can. A reflection path that runs a tmux write and throws
1689
- the error away records `result:"state"` for an event whose Pane never moved, so
1690
- the log itself reports success and every diagnosis built on it inherits the lie.
1691
- It is the strongest form of the failure this whole section exists to catch:
1692
- `disconnected` and `no matching pane` were at least wrong out loud.
1693
-
1694
- The gate is zero within the hook reflection path, and it carries no baseline.
1695
- Recording the current count as an allowance was considered and rejected:
1696
- pinning today's state as the passing condition is the exact shape of the E2E
1697
- assertion that made a silent control plane the condition for green, and
1698
- building that trap inside the gate written to stop it is not a tradeoff worth
1699
- making. An exception is admitted by a stated `best-effort-write: <reason>`
1700
- comment, never by a count — the difference being that a reason is a judgment a
1701
- reviewer can dispute, while a number just defers on volume. Three writes carry
1702
- one today: two cosmetic (a pane title, a one-shot message on the operator's own
1703
- screen) and one global cleanup marker whose failure simply causes a retry. None
1704
- of the three can make a surface report a state a Pane does not hold.
1705
-
1706
- Three things about the scan itself. It covers the whole package rather than a
1707
- named list of files, so a discarded write appearing somewhere new is caught
1708
- rather than skipped for being somewhere nobody thought to look. It counts a
1709
- bare call statement as well as an assignment to blank identifiers — that second
1710
- form appears nowhere in the tree today and is covered as prevention, so that
1711
- deleting the assignment rather than checking the error does not quiet the gate.
1712
- And it reads the syntax tree, not the text: a textual version counted the pattern inside the
1713
- comment explaining why a checked helper replaced it, so the sentence describing
1714
- the fix registered as the defect.
1715
- `TestDiscardedWriteScanNeverCountsProseAboutCode` pins that, because a gate
1716
- which cannot tell code from prose about code is one people route around.
1717
-
1718
- The `pane-ownership` foreign count is broken down by direction for a related
1719
- reason. A hook landing on the Pane that launched its host is the identity leak
1720
- the contract closes; a provider's events arriving under another provider's
1721
- source were misrouted before attribution ever ran, which a stale hook config on
1722
- the machine can produce and no code change here would fix. One number would let
1723
- either be mistaken for the other.
1724
-
1725
- The section leads with a deployment-vintage line, and
1726
- `TestControlPlaneVintage*` owns it. `make install` replaces the executable on
1727
- disk and never replaces the image of a running process, so a green surface read
1728
- from a replaced-image process describes code that process never loaded. This
1729
- track lost two acceptance criteria to that twice. On Linux the diagnosis
1730
- resolves `/proc/<pid>/exe` for the broker runtime and the lifecycle observers
1731
- and reports how many run a replaced image; on a platform with no such table it
1732
- reports the answer as unknown rather than claiming currency.
1733
-
1734
- That line answers which image the Codex verdicts under it were read from, so it
1735
- counts only those two roles. It is not the fleet answer, and reading it as one
1736
- understated the fleet by a factor of five: on 2026-09-05 it named six processes
1737
- while thirty-two children of the same executable were live, eighteen of them
1738
- per-pane `internal supervise` supervisors that matched no named route and were
1739
- dropped at a map lookup. `projmux doctor` therefore carries a second census
1740
- under `Runtime`, rendered as `Projmux process vintage` and owned by
1741
- `TestProjmuxProcessVintage*`, `TestProjmuxProcessRole*` and
1742
- `TestDoctorSeparatesTheCodexDiagnosisVintageFromTheFleetCensus`.
1743
-
1744
- - It is provider-neutral, which is why it is not on the Codex line. A
1745
- supervisor supervises a Claude pane exactly as it supervises a Codex one, and
1746
- `TestCodexControlPlaneVintageIgnoresProviderNeutralProcesses` keeps it off the
1747
- line that qualifies Codex verdicts.
1748
- - It has no unmatched case. A child whose route this reader cannot name is
1749
- counted under `other` rather than dropped, so an unknown route can never make
1750
- the fleet look smaller than it is.
1751
- - Route words are argv without argv[0] and without anything behind a bare `--`.
1752
- `projmux agent turn ... -- <text>` carries arbitrary prose, including the very
1753
- route words this census matches on, and counting a message about the broker as
1754
- a broker is the same class of error in the other direction.
1755
- - Both censuses come from one process-table read, so the two lines can never
1756
- disagree about the same process, and both answer `unknown` where the platform
1757
- exposes no table.
1758
- - Neither line names a process. `TestProcessVintageNamesNoProcess` audits both
1759
- rendered strings for pids, paths and argv; the section is counts and nothing
1760
- else, which is what makes a process census printable at all.
1761
-
1762
- The gate does not change `projmux doctor`'s exit code. A broken surface is a
1763
- readable verdict, not a failed command, and the CI half of the detection is the
1764
- regression tests themselves.
1765
-
1766
- ### Shared AI pane option routing tests
1767
-
1768
- `TestSharedPaneRoutingCodexIngest` drives the marker (`PreToolUse`) and ordinary
1769
- state (`UserPromptSubmit`) caller paths through full Codex ingest. Both require
1770
- writes on the verified app route and honest records; removing the shared argv
1771
- prefix makes both fail. `TestSharedPaneRoutingSetClearRecord` pins exact inherited
1772
- `-S` and detached app `-L` argv. `TestSharedPaneRoutingDenialIsBoundedAndWritesNothing`
1773
- rejects unreachable servers, missing panes, foreign servers and absent resource
1774
- identity without writes or success records. Existing Codex delivery authority,
1775
- semantic, refusal-vocabulary and diagnostics gates remain in force.
1776
-
1777
- `TestSharedPaneRoutingProviderConsumers` covers Codex, Claude, Antigravity and
1778
- bell hook consumers, including state and resume markers.
1779
- `TestSharedPaneRoutingLaunchBeforeFirstMarker` checks canonical detached
1780
- materialization through its supplied exact runner before any managed marker or
1781
- Pane UID exists. Existing topic/status semantic fixtures retain
1782
- their option expectations while their mock server supplies explicit containment.
1783
- `TestManagedProjectionRoutingCallers` enters managed interaction through Claude
1784
- `UserPromptSubmit` hook ingest and managed topic through the internal
1785
- `aiCommand.Run` compatibility set/clear path. Its internal status-idle companion
1786
- covers badge/attention unset; hooks do not emit idle. Caller result, committed
1787
- Registry state, exact route argv, target values and actual unset are asserted
1788
- together. `TestManagedProjectionRoutingFailureKeepsCommit` refuses disappeared,
1789
- denied and foreign routes after commit, with no fallback writes, and retains the
1790
- existing committed-mirror error and first-write-failure behavior.
1791
-
1792
- `TestManagedProjectionRoutingRealTmux` repeats these managed caller cases on two
1793
- isolated real servers with identical pane IDs and opposite-server sentinels.
1794
- Detached attribution reads use the fixture's exact route; projection writes run
1795
- unaltered. Baseline detached raw projections and a shared-prefix-removal mutant
1796
- both fail the target assertions. Inherited raw baseline writes can already work
1797
- through the tmux environment; the argv fixture additionally pins explicit routing.
1798
- An optional `PROJMUX_TEST_MANAGED_ROUTE_BINARY=<absolute binary>` executes
1799
- the reachable hook case and a separate canonical public status/topic parity
1800
- subtest through that binary. Internal topic and idle cases always
1801
- compile this source; there is no installed target-topic CLI. Canonical public
1802
- `agent status/topic` uses its separate already-routed mirror and is parity evidence,
1803
- not proof of these compatibility projections.
1804
-
1805
- `TestSharedPaneRoutingRealTmux` uses two real isolated servers with the same pane
1806
- ID and checks set, clear, marker and full hook state against target readback and
1807
- opposite-server sentinels for detached and inherited routes. It refuses a missing
1808
- inherited route without fallback. Every server starts with a unique socket name
1809
- inside a dedicated tmux root; only a verified private alias exposes the fixed app
1810
- locator. Cleanup rechecks the observed exact socket before `-S` shutdown. The test
1811
- runs with `make test` where tmux is available; an explicit installed smoke runs the
1812
- same hook assertions against `PROJMUX_TEST_PANE_ROUTE_BINARY=<absolute binary>`.
1813
- This is a test-only selector, not a product environment contract. The fixture
1814
- seeds and strips the caller's internal activation Pane/generation envelope so a
1815
- managed test process cannot suppress the isolated hook as a stale activation.
1816
-
1817
- ### Attributed Codex hook delivery route tests
1818
-
1819
- Attribution and reflection are different layers. A Codex hook attributed to its
1820
- Pane still has to write that Pane's options, and the hook the shared app-server
1821
- launches inherits no tmux environment at all: its `tmux set-option` carried no
1822
- socket argument, probed the operator's `default` server, found none, and handed
1823
- back `exit status 1`. `ai-ingest.log` recorded that exit status where a cause
1824
- belongs.
1825
-
1826
- `applyCodexHookSemanticDelivery` now resolves one exact server before it writes
1827
- anything, and refuses by name when it cannot.
1828
-
1829
- - `TestCodexHookDeliveryWritesItsPaneWithoutAnyTmuxEnvironment` owns the case
1830
- the failure came from: with no `TMUX` at all the delivery still resolves a
1831
- route, and the argv it produces is pinned to the app-owned socket rather than
1832
- left unprefixed.
1833
- - `TestCodexHookDeliveryPrefersTheInheritedReceiptOverTheAppRoute` owns the
1834
- precedence. A pane client's own `$TMUX` receipt names its server without a
1835
- search, is taken as an explicit socket path, and needs no containment probe.
1836
- - `TestCodexHookDeliveryRefusesARouteItCannotProve` owns the proof. The
1837
- app-owned route is never trusted on its name: an unreachable runtime, an empty
1838
- answer, a server that is not app-owned, a runtime holding a different pane,
1839
- and a pane that is not a projmux Pane each produce their own reason and write
1840
- nothing.
1841
- - `TestCodexHookDeliveryFailureReasonsAreConcreteAndClosed` owns the vocabulary.
1842
- Every token a refusal can render — the three reasons, the two route kinds, the
1843
- four tmux causes — is non-empty and carries no socket path, no routing flag,
1844
- and no process exit status.
1845
- - `TestCodexHookDeliveryLeavesNoTransportDetailInTheIngestLog` owns the record
1846
- rather than the error value. It injects a transport failure whose tmux output
1847
- names its socket, drives the real ingest route, and reads the whole log file
1848
- back: a reason that leaks is caught there even when every assertion above
1849
- still passes.
1850
- - `TestCodexHookDeliveryExplainsARejectedWriteByRereadingItsRoute` owns the
1851
- residual failure. A `set-option` that fails after the route was proven is
1852
- explained by re-reading the route — a pure read that repeats no write — so the
1853
- record says whether the pane vanished or the runtime rejected the option.
1854
-
1855
- tmux explains a refusal on stderr and that text carries the socket path it
1856
- tried, so `codexHookDeliveryCause` is the only reader of tmux's own words here:
1857
- it classifies them into a closed set and returns none of them. A reason names
1858
- which lane answered — inherited or app-owned — and never which file backs it,
1859
- because this track's change boundary keeps paths out of durable records.
1860
-
1861
- The route decides where a reflection is written, not which Pane it belongs to;
1862
- attribution stays with the matcher and the Registry step above. Two other hook
1863
- writers on the same shared-host path, `applyAIStatusInternalWithActivationPolicy`
1864
- and `markAIHookPane`, still issue unrouted tmux calls and discard their errors,
1865
- so their failures are invisible rather than fixed.
1866
-
1867
- ### Install residue census tests
1868
-
1869
- `make install` and `npm install` replace the executable and leave every
1870
- long-lived projmux child on the image it started with. `projmux internal
1871
- install-residue` measures how much of the fleet that is, appends one record to
1872
- `install-residue.jsonl`, and prints at most one stderr notice; see
1873
- [operational-diagnostics.md](operational-diagnostics.md#install-residue-ledger)
1874
- for the ledger format and the mapping from its fields to the questions a
1875
- bounded drain of those processes has to answer.
1876
-
1877
- The deliverable is the measurement, not the message, so the tests are weighted
1878
- toward the record rather than the wording.
1879
-
1880
- - `TestProjmuxProcessVintageMeasuresTheResidualAgeDistribution` owns the age
1881
- half of the census: only replaced-image processes contribute an age, the
1882
- distribution is stored ascending and whole, and a clock that moved backwards
1883
- floors at zero rather than reporting a process started after the census that
1884
- observed it.
1885
- - `TestProjmuxProcessVintageKeepsAnUnknownStartTimeOutOfTheDistribution` owns
1886
- both halves of the unknown case: the process still counts as residue, and no
1887
- age is invented for it.
1888
- - `TestProjmuxProcessVintageCapsOneRoleAgeDistribution` owns the per-role sample
1889
- bound and pins that a census which hit it says so, rather than reporting a
1890
- prefix as a whole distribution.
1891
- - `TestDoctorProcessVintageProjectionCarriesNoAges` keeps `projmux doctor`'s
1892
- record exactly what it was: the age fields populate only when a caller
1893
- supplies a reference instant.
1894
- - `TestProcessImageListerReadsAPlausibleStartTime` (Linux) covers the one part
1895
- a synthetic process table cannot. A reader that silently returned the zero
1896
- time would leave every distribution empty, which is indistinguishable from a
1897
- platform where start times are unknowable.
1898
- - `TestInstallResidueReportsPerRoleResidualCountsAndAges` owns the record
1899
- field-for-field, including the per-role distribution being as long as that
1900
- role's residual count and ascending.
1901
- - `TestInstallResidueZeroResidualPrintsNothing` owns the boundary that keeps the
1902
- notice worth reading: an install that reached the whole fleet prints
1903
- absolutely nothing, and still records that it did.
1904
- - `TestInstallResidueUnsupportedPlatformPrintsNothingButRecordsTheGap` owns the
1905
- macOS path: no terminal output where the census cannot be taken, and a
1906
- `supported:false` record so the coverage gap is measured rather than absent.
1907
- - `TestInstallResidueCarriesNoProcessIdentity` is the privacy gate, asserted
1908
- negatively over the rendered notice and the ledger bytes at once: sentinel
1909
- pids, executable path, and argv appear in neither.
1910
- - `TestInstallResidueLedgerAppendsAndDerivesTheInstallGap` owns the frequency
1911
- half — records accumulate and each carries the gap to the one before it.
1912
- - `TestInstallResidueLedgerRotatesAtItsRecordBound` owns growth: the oldest rows
1913
- go, the newest stay, and the file keeps mode `0600` across the rotation.
1914
- - `TestInstallResidueNoticeOrdersRolesByResidualCount` and
1915
- `TestInstallResidueNoticeTiesBreakOnTheCensusRoleOrder` own the render
1916
- contract: biggest residual first, census role order on a tie, zero-residual
1917
- roles omitted entirely.
1918
- - `TestInstallResidueAgeTextRendersDurationsAnOperatorReads` owns the duration
1919
- format, the median of both odd and even distributions, and the unknown case,
1920
- which must not render as a zero age.
1921
- - `TestInstallResidueNeverFailsAnInstall` owns the hard edge: an unresolvable
1922
- state directory, an unwritable ledger, and the route entrypoint itself all end
1923
- in a silent success.
1924
- - `TestInstallResidueInstallerComesFromTheExistingEnvVar` pins that installer
1925
- identity is read from `PROJMUX_INSTALLER` and that no new `PROJMUX_*` variable
1926
- is introduced, since any such variable is a minor-release input to the hook
1927
- contract.
1928
- - `TestInstallResidueRouteIsRegisteredAsInternalPlumbing` pins the spelling the
1929
- installer invokes and keeps the census out of the legacy hook migration path.
1930
-
1931
- Nothing in this feature terminates, starts, signals, or restarts a process.
1932
- The census is two file reads per process; replacing the residual processes is a
1933
- separate decision this measurement exists to inform.
1934
-
1935
- ## Review Checklist
1936
- - The branch stays within its stated scope.
1937
- - The change preserves boundaries between portable `projmux` behavior and local machine policy.
1938
- - The required `make` targets were run in order.
1939
- - Test inventory updates are included when behavior changed.
1940
- - Known parity gaps are explicit.
1941
-
1942
- - `TestClaudePushSourceReplacementAfterDurableHandoffWritesZero` and
1943
- `TestClaudeProviderPushFinalRouteCheckPrecedesSoleWrite` cover source/target
1944
- authority loss across durable work and the final provider pre-write fence.
1945
- - `TestClaudeProviderFrameSerializedByteBoundaryAndSafeRejection` fixes the
1946
- complete auth/user frame boundary at 8191/8192/8193 serialized bytes.
1947
- `TestClaudeProviderPostPreservesConstructionReasonBeforeAnyRouteOrWrite`
1948
- separates invalid auth/content from size rejection before route or write.
1949
- `TestClaudeProviderWriteReasonsRequireOneActualWrite` distinguishes actual
1950
- zero/partial writes and invalid-count/full-with-error unknown outcomes.
1951
- `TestClaudeProviderRejectionReasonsSurviveHubReceiptStatusAndStoreReload`
1952
- preserves reasons through helper and public receipts, terminal store reload,
1953
- sender recovery text, secret exclusion, and duplicate sends with zero retries.
1954
- `TestClaudeProviderRejectionResponseValidationIsClosedAndLegacyCompatible`
1955
- admits canonical numeric size details and legacy reasons while rejecting
1956
- inconsistent ambiguity and secret-bearing suffixes.
1957
- `TestClaudeEndpointProcessIntegration/provider-frame-rejection-receipt`
1958
- exercises the built binary's public send/status against the isolated provider:
1959
- size/action survive, credentials stay absent, and the rejected frame adds no
1960
- provider connection before the next expected message.
1961
- - `TestClaudeExplicitMultipleRequestsAndHumanOverlapSelectOnlyNamedOriginal`
1962
- requires an explicit choice of the original request; Stop text has no reply
1963
- authority regardless of human activity or pending request count.
1964
- - `TestClaudeReplyToolLiteralCommandAndOpaqueCarrier` and
1965
- `TestClaudeReplyToolRejectsMalformedAndOtherOfficialTools` reject shell
1966
- expansion, extra commands, nonallowlisted tools and malformed hook input.
1967
- A shell-prefix carrier supplies only one canonical memory ticket; its other
1968
- bytes are never executed or retained.
1969
- - `TestClaudeReplyToolTicketsChooseExactActionAndConsumeOnce` and
1970
- `TestClaudeReplyToolForeignStaleExpiredAndReplacedExecutableExecuteZero`
1971
- bind parallel actions to separate tickets and one exact process birth,
1972
- executable inode, current route and argv. Replays, expired tickets, foreign
1973
- callers, replaced executables and missing hooks obtain no execution witness.
1974
- - `TestClaudeReplyToolRequestedCaptureFailureRefusesAndSecretsAreAbsent` keeps
1975
- requested guard capture failures closed and messaging credentials out of the
1976
- fixed public-command environment.
1977
- - `TestClaudeExplicitReplyUnsupportedTargetCannotCorruptStore` rejects a
1978
- non-Codex reply target before a durable write. The process integration fixture
1979
- now owns a real Codex broker binding for its original source.
1980
-
1981
- - `TestAuthorityCheckUsesExistingExactLeaseWithoutProviderTraffic` proves the
1982
- dialogue source's exact live Codex broker runtime, connection and binding
1983
- fence with no new binding or provider request.
1984
- - `TestNonblockingHookStoreRefusesContentionWithoutLateWrite` and
1985
- `TestClaudeOfficialHookContentionInvalidatesReplyWithoutWaiting` keep compatibility hooks bounded. Explicit reply commits use broker original
1986
- request validation independently of hook timing.
1987
-
1988
- - `test/agent_dialogue_canary_test.py` covers exact public claim/schema mutations,
1989
- runtime-first owner/generation negatives, prepare-without-provider-launch,
1990
- environment isolation, and automatic partial-setup delayed-writer/early-error
1991
- cleanup, plus registered Project session projection/foreign-scope refusals
1992
- before runtime creation and automatic cleanup of a registered Project when
1993
- setup fails before tmux creation. Audit tests preserve closed stage/exit and exact writer birth evidence
1994
- outside the disposable root, reject unsafe candidate permissions before
1995
- credential copying, and retain roots after uncertain cleanup without retry. Product `TestClaudeDialogueStream*` tests own public output parsing;
1996
- the obsolete separate canary collector is removed.
1997
- - `TestClaudeDialogueCanaryAcceptsProductionStoreClaimAndPublicReceipt` validates
1998
- production Store.PutReply/Claim records and current public send/status receipt
1999
- JSON with the Python companion, including the actual `target-self-claim`
2000
- reason and version-2 envelope, without a public claim command or serializer.
2001
-
2002
- - L20 owns a real authenticated Codex broker host and keeps its observed snapshot
2003
- binding alive for the round trip; Registry-only synthetic authority is refused.
2004
- The offline endpoint records and rejects every provider request/answer.
2005
- First-failure diagnostics accept L20 and keep its replay attribution.
2006
-
2007
- - `test/agent_dialogue_canary_test.py` also exercises Linux-only failed-canary
2008
- cleanup with explicit delayed-writer barriers: the actual EXIT trap retains
2009
- its root until the captured writer exits, preserves the original failure,
2010
- tracks reparented descendants, and refuses stubborn or replaced births.
2011
- These regressions use no live provider and skip when Linux pidfd is absent.
2012
-
2013
- - `TestClaudeQualificationRequiresCurrentPinnedMemoryGuard` rejects asserted
2014
- evidence with a missing, replaced or writable guard image before any push.
2015
- Private coord v5 fences earlier unguarded qualification; the frozen provider
2016
- auth/user frame is unchanged. L20 and the process fixture now execute the
2017
- guarded public CLI, with real isolated tmux identity and Codex broker binding.
2018
-
2019
- - `TestLifecycleDiscardedASCIIKeepsStringBoundariesAndValidation` covers the
2020
- body-free projector's buffered ASCII scan across chunk, quote, escape,
2021
- control-character and UTF-8 boundaries. The optimized path retains no body
2022
- and leaves byte/depth/scalar limits and the 750 ms operation limit unchanged.
2023
- `TestLifecycleDiscardedASCIIChecksCancellationWithinBoundedBufferedRun`
2024
- requires cancellation within one 4 KiB buffered run and preserves the
2025
- retained-state refusal before scanning.
2026
-
2027
- - Offline L20 cleanup (`test/e2e/dialogue-cleanup.py`) reuses the failed-canary
2028
- pidfd barrier and also captures exact owned cwd paths before canonical delete.
2029
- It waits for captured pane/supervisor/helper births after reparenting; only
2030
- role-validated fixture jobs and its exact broker receive pidfd TERM. The outer
2031
- EXIT trap retains the root on missing exit proof. `OfflineDialogueCleanupTest`
2032
- synchronizes a cwd-only late receipt writer with actual delete intent, checks
2033
- original failure exit preservation, and rejects stubborn/foreign/replaced
2034
- births without broad signals. Historical L20 late-writer attribution remains
2035
- unknown; process exit proof does not depend on that attribution.
2036
- - `TestStoreReplyIsAtomicCorrelatedAndReplayIdempotent` pins both store instances
2037
- to the fixture clock so the replay test remains independent of wall-clock
2038
- terminal retention while retaining the explicit one-hour replay timestamp.
2039
-
2040
- - `TestClaudeDialogueCreateOptInRefusesUnsupportedOrUnconfiguredLaunchBeforeWrites`,
2041
- `TestClaudeDialogueResumeOptInKeepsRunningAndNonClaudeRefusalsReadOnly`, and
2042
- `TestClaudeDialogueOptInIsAnExplicitSupervisorEnvelopeNotAnInheritedDefault`
2043
- cover the next-activation `--dialogue-reply-only` preflight and supervisor/gate
2044
- envelope. No AgentSpec or default tool policy persists this opt-in. The
2045
- production launcher creates a private generation profile and an owned
2046
- stdout/stderr observer. Missing launcher adapters still fail before allocation.
2047
- - `TestAgentMessageAndReplyOnlyPreflightDoNotMigrateGlobalOrProjectHooks` checks
2048
- that public message authentication and explicit reply-only preflight never
2049
- migrate unrelated global/project legacy hooks. Existing normal create/resume
2050
- pre-dispatch behavior remains unchanged.
2051
-
2052
- - `TestClaudeDialogueProfilePinsFilesAliasAndInvocationMode` and
2053
- `TestClaudeDialogueOrdinaryEnvironmentKeepsOfficialPrefixWithoutInheritingOptIn`
2054
- cover immutable profile inputs, pinned candidate alias, FIFO refusal, and
2055
- ordinary environment compatibility. `TestAgentMessageAndReplyOnlyPreflightDoNotMigrateGlobalOrProjectHooks`
2056
- also covers the profile's exact lifecycle/register/helper callbacks.
2057
- - `TestClaudeDialogueCurrentHelperEvidenceAndObserverLossPrecludeEffects` uses
2058
- the current helper UDS to obtain qualification evidence and complete a valid
2059
- guarded reply baseline. Replacing only the observer descriptor then refuses
2060
- otherwise-valid prepare, consume, commit and push requests without effects.
2061
- - `TestClaudeDialogueStreamDiscardsTextThinkingAndPairsOnlyExactReplyTool` and
2062
- `TestClaudeDialogueStreamRejectsUnknownEffectsAndBoundsState` cover transient
2063
- public JSON validation, action/result pairing, zero pre-inbound tool effects,
2064
- unknown metadata refusal and finite hook/tool state.
2065
- - `TestClaudeDialogueStreamReplaysObservedFrameCorpus` replays
2066
- `internal/app/testdata/claude-dialogue-observed-frames.json`, the top-level
2067
- key shapes preserved from a Claude Code 2.1.263 run, through a fresh stream in
2068
- each item's declared state. Items carry provenance labels and the measured
2069
- verdict. A reject must stop once its recorded rule keys are removed, so any
2070
- validator change that moves a recorded verdict fails with the item name and rule.
2071
- - `TestClaudeDialoguePipeEOFClosesInputButWaitsCurrentTurn`,
2072
- `TestClaudeDialoguePipeDiagnosticLossFailsClosedWithoutBusyLoop`,
2073
- `TestClaudeDialogueCleanupWaitsExactWriterAndHandlesZombieWithoutSignals`, and
2074
- `TestClaudeDialoguePartialSetupAndUnknownFilesRetainExactRoot` cover Linux
2075
- terminal EOF, current-turn completion, stderr loss, exact-birth pidfd exit
2076
- proof and retained evidence on uncertain cleanup. No provider runs in these tests.
2077
-
2078
- - L20 now launches the synthetic Claude through public `--dialogue-reply-only`,
2079
- exercises the real profile executor, observer and pinned prefix dispatch, and
2080
- qualifies from current helper-memory evidence. It creates no hand-authored
2081
- public-init evidence file. Qualification and ordinary replies are separately
2082
- claimed by the original Codex route. This is deterministic integration
2083
- evidence, not actual model execution. Wrapped pane output is joined for the
2084
- readiness marker; cancellation closes the fixture's own first-frame listener.
2085
- - `TestClaudeEndpointProcessIntegration` intentionally remains the private
2086
- registration/bootstrap/process fixture. Its owned registration child opts in
2087
- after activation; it does not depend on a supervisor-inherited guard flag,
2088
- which production correctly scrubs. It does not substitute for public launch
2089
- or actual provider qualification evidence.
2090
-
2091
- - `TestClaudeDialogueToolEvidenceIsReadOnlyAndIndependentOfArrivalOrder` and
2092
- `TestClaudeDialogueObservedToolCannotGrantOrForgeCommitEvidence` distinguish
2093
- observed model tool ID/original selection and paired result from the pinned
2094
- execution witness and successful broker commit. A failed commit, wrong result
2095
- ref or forged observation cannot become correlated execution evidence; reading
2096
- that bounded snapshot performs no delivery or reply action.
2097
- - `TestAgentCapabilitiesClaudeMessageCellsReflectMissingRegistrationLease` and
2098
- `TestAgentCapabilitiesClaudeUnqualifiedRecoveryUsesCurrentPublicProfile` keep
2099
- recovery read-only and actionable: normal exit, same-UID explicit reply-only
2100
- resume, then current Codex qualification from helper-memory evidence.
2101
-
2102
- - `test/agent_dialogue_codex_observation_test.py` checks the frozen public Codex
2103
- 0.153.2 schema, explicit observed source, one thread/turn/item, paired command
2104
- completion and closed result correlation, unknown/body privacy negatives, and
2105
- bounded read-only connection EOF/deadline/peer replacement. It supplies no
2106
- actual provider or model-action evidence.
2107
- - `test/agent_dialogue_source_action_test.py` checks exact public qualification,
2108
- original-source inbox claims and one idle send against production-shaped
2109
- reply fixtures; changed composite routes fail before dispatch and unknown
2110
- send outcomes are never retried. The source action returns closed facts and
2111
- performs no fleet/root cleanup. Parent orchestrator wiring remains pending.
2112
-
2113
- - `test/agent_dialogue_native_source_test.py`: private native launch argv/image/environment, public genuine initial task, bounded initialization, source release before effects and closed tool return before parent cleanup; fake endpoints/launches and owned inert child processes only.
2114
-
2115
- - `test/agent_dialogue_native_policy_test.py`: pinned public config/read projection, missing/mismatched policy, foreign or unknown origins, private-value exclusion, raw-layer/extra-execution-input refusal, bounds/EOF; native source tests also refuse an otherwise-valid action when policy changes before release.
2116
-
2117
- - Policy diagnostics: `test/agent_dialogue_native_policy_test.py` checks closed schema/request/value/origin codes and exception redaction; native source tests preserve a valid baseline while independently failing config/socket/request/schema boundaries. `test/agent_dialogue_canary_test.py` exercises real setup finally after a policy code, audit privacy, uncertain-writer retention and audit-write failure cleanup without a success receipt. All use offline fixtures.
2118
-
2119
- - `test/agent_dialogue_rpc_envelope_test.py`: pinned public JSON-RPC forms classify server requests, errors and notifications without admitting them. Exact success shape and expected ID remain mandatory; wrong/foreign IDs, unknown fields, mixed forms and malformed method/error/trace types refuse. All 64 known-field combinations and nested type branches verify the independent fixed `envelopeFacts` projection, simultaneous defect visibility and dynamic-name/value exclusion. Policy/native initialization tests propagate only those facts; audit tests reject malformed projections before writing. Real parent setup/finally tests preserve external facts before once cleanup, retain uncertain roots or failed audit writes, and produce no source effect or success receipt.
2120
- - `test/agent_dialogue_websocket_test.py`: memory-only RFC6455 upgrade/masked writes/message framing, Unicode fragmentation with interleaved control frames, cumulative bounds/EOF/deadline and malformed transport privacy; pinned full initialization with exact owned codexHome precedes framed config/read and thread/read. Canary audit tests connect upgrade refusal/socket close to once parent cleanup and external code retention. Closed substage/rejection-kind regressions distinguish connect/peer, upgrade, initialize/initialized, config/read, EOF/deadline/frame and error/ID/notification envelope rejection; unknown labels remain unknown, raw exception/RPC content is excluded, and audit failure still retains the root.
2121
-
2122
- - `make test`: Codex recovery diagnostics retain the original request through RPC classification, catalog wrappers, bounded broker lifecycle IPC and observer fallback. `TestRequestFailureDiagnosticPreservesOriginalThroughCatalogWrapping`, `TestFailureDiagnosticIPCProjectionIsClosedAndBounded`, `TestLifecycleRPCDiagnosticPreservesCodeAndRejectsUnrepresentableCode`, `TestObserverFallbackJournalPreservesSafeFailureAndClosedStartup`, `TestObserverDiagnosticRejectedAuthorityWritesZero`, `TestObserverSessionFailurePreservesTerminalReasonAfterAuthorityRejection`, `TestAIIngestTextPreservesBoundedRecoveryDiagnosticsAndLegacyRecords`, and `TestRecoveryDiagnosticGoldenInventoryAndBounds` cover unsupported/catalog/protocol/transport causes, missing/null codes, secret/invalid UTF-8/oversized inputs, existing reason tokens, terminal session-construction fallback after a rejected authority write, bounded public text projection with legacy-row compatibility, and commit-before-journal refusal. `TestManagerEvidenceContradictionAndOwnershipMatrix` and `TestManagerEvidenceCommandFailureAndTimeout` (with `TestManagerDiagnosticDeadlineProcess`) separate independent attach and official read-only manager observations, with unknown/contradictory refusal and zero lifecycle argv. The existing startup parser, parent fallback, exact authority, Phase 0 timing/close and Phase 1 recovery tests remain required.
2123
- - Opt-in installed zero-turn fixture: `CODEX_DIAGNOSTIC_INPUT=/absolute/input.json go test ./internal/app -run '^TestInstalledCodexRecoveryDiagnosticFixture$' -count=1 -v`, with inherited `TMUX` and `TMUX_PANE` unset. Input fields are `root` (new short `/tmp` directory), `binary`, `binarySHA256`, `sourceHead`, `sourceTree`, and `evidence` (outside root). The binary must come from that clean committed source. Synthetic manager/provider responses are explicitly labeled; the actual executable runs its observer and doctor against a private Registry, exact private tmux socket and real bounded broker IPC. `TestCodexDiagnosticProxyProcess` is the read-only synthetic subprocess helper. The fixture never uses authentication, starts a turn, resumes a live session, or mutates a manager. It checks journal/doctor agreement, the actual public `diagnostics agent-hook` JSON and text consumers (including a labeled synthetic legacy row), old startup parsing, version/ownership refusals, output bounds and exact socket cleanup, then removes its private root. Evidence retains the public JSON records and text lines alongside observer rows and doctor reports. Run the identical fixture against the candidate and the isolated merged-main install; preserve the input, binary/source/tree hashes and evidence JSON.