projmux 0.14.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-ko.md +58 -114
- package/README.md +49 -110
- package/docs/agent-workflow.md +1420 -37
- package/docs/architecture.md +95 -70
- package/docs/assets/projmux-ai-attention-ko.gif +0 -0
- package/docs/assets/projmux-ai-attention.gif +0 -0
- package/docs/assets/projmux-overview-ko.gif +0 -0
- package/docs/assets/projmux-overview.gif +0 -0
- package/docs/assets/projmux-three-pane-workflow-ko.gif +0 -0
- package/docs/assets/projmux-three-pane-workflow.gif +0 -0
- package/docs/claude-coordination-endpoints.md +254 -0
- package/docs/cli-guide.md +283 -39
- package/docs/cli.md +2059 -56
- package/docs/codex-generation-pool.md +618 -0
- package/docs/codex-installed-compatibility.md +69 -0
- package/docs/codex-native-required-migration.md +98 -9
- package/docs/column-profiles.md +83 -0
- package/docs/configuration.md +43 -4
- package/docs/heterogeneous-dialogue-canary.md +339 -0
- package/docs/hooks.md +48 -15
- package/docs/npm-distribution.md +31 -0
- package/docs/operational-diagnostics.md +125 -0
- package/docs/pr-guideline.md +3 -2
- package/docs/replacement-contract.md +555 -0
- package/docs/session-restore.md +15 -5
- package/docs/settings-ia.md +5 -4
- package/docs/testing.md +12 -7
- package/docs/troubleshooting.md +19 -0
- package/docs/upgrading.md +48 -3
- package/npm/projmux.js +65 -0
- package/package.json +5 -5
package/docs/agent-workflow.md
CHANGED
|
@@ -27,6 +27,26 @@
|
|
|
27
27
|
|
|
28
28
|
## Maintained Test List
|
|
29
29
|
|
|
30
|
+
- `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.
|
|
31
|
+
|
|
32
|
+
- `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.
|
|
33
|
+
|
|
34
|
+
- `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.
|
|
35
|
+
|
|
36
|
+
- `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.
|
|
37
|
+
|
|
38
|
+
- `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`.
|
|
39
|
+
|
|
40
|
+
- `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.
|
|
41
|
+
|
|
42
|
+
- `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.
|
|
43
|
+
- `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.
|
|
44
|
+
- `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.
|
|
45
|
+
- `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.
|
|
46
|
+
- `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.
|
|
47
|
+
- `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.
|
|
48
|
+
- `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.
|
|
49
|
+
|
|
30
50
|
- `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`.
|
|
31
51
|
- `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.
|
|
32
52
|
- `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.
|
|
@@ -35,14 +55,19 @@
|
|
|
35
55
|
- `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.
|
|
36
56
|
- `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.
|
|
37
57
|
- `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.
|
|
38
|
-
- `make test`: Codex native control plane Phase 1 pins the closed trigger × probe × start × readiness lifecycle table: Doctor, Settings, and support reports repeat with zero daemon mutation; only a native user action whose official local socket is missing or refuses a connection may share one in-process `codex app-server daemon start`; and already-running, executable-missing, nonzero, timeout, socket-unavailable, unsupported, protocol, and other endpoint failures retain exact content-free reasons and the existing fallback. Fake CLI/proxy fixtures cover exact argv, cold and warm initialize, bounded readiness, concurrent callers, per-caller cancellation, late-arrival handoff, zero waiter leaks, and discarded process output.
|
|
39
|
-
- `make test
|
|
40
|
-
- `make test
|
|
41
|
-
- `make test`: Codex
|
|
42
|
-
- `make test
|
|
58
|
+
- `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.
|
|
59
|
+
- `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.
|
|
60
|
+
- `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.
|
|
61
|
+
- `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.
|
|
62
|
+
- `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.
|
|
63
|
+
- `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.
|
|
64
|
+
- `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.
|
|
65
|
+
- `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.
|
|
66
|
+
- `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.
|
|
67
|
+
- `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.
|
|
43
68
|
- `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.
|
|
44
69
|
- 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.
|
|
45
|
-
- 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
|
|
70
|
+
- 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.
|
|
46
71
|
- `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.
|
|
47
72
|
- `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.
|
|
48
73
|
- `make fmt`: repository formatting for Go, shell snippets, and generated docs where applicable.
|
|
@@ -53,10 +78,10 @@
|
|
|
53
78
|
- `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.
|
|
54
79
|
- `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.
|
|
55
80
|
- `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.
|
|
56
|
-
- `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`; 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.
|
|
81
|
+
- `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.
|
|
57
82
|
- `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.
|
|
58
83
|
- `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.
|
|
59
|
-
- `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 restore publishes the verified bytes verbatim so uids, owner relations, and name reservations round-trip through `LoadReadOnly`, a
|
|
84
|
+
- `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.
|
|
60
85
|
- `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.
|
|
61
86
|
- `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.
|
|
62
87
|
- `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.
|
|
@@ -72,7 +97,7 @@
|
|
|
72
97
|
- `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.
|
|
73
98
|
- `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.
|
|
74
99
|
- `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.
|
|
75
|
-
- `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.
|
|
100
|
+
- `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.
|
|
76
101
|
- `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.
|
|
77
102
|
- `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.
|
|
78
103
|
- `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.
|
|
@@ -103,8 +128,14 @@
|
|
|
103
128
|
- `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.
|
|
104
129
|
- `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.
|
|
105
130
|
- `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.
|
|
106
|
-
- `make test
|
|
107
|
-
- `make test`:
|
|
131
|
+
- `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.
|
|
132
|
+
- `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.
|
|
133
|
+
- `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.
|
|
134
|
+
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.
|
|
135
|
+
- `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.
|
|
136
|
+
- `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.
|
|
137
|
+
- `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.
|
|
138
|
+
- `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.
|
|
108
139
|
- `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.
|
|
109
140
|
- `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.
|
|
110
141
|
- `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.
|
|
@@ -140,8 +171,12 @@
|
|
|
140
171
|
- `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.
|
|
141
172
|
- `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.
|
|
142
173
|
- `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.
|
|
174
|
+
- `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.
|
|
143
175
|
- `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.
|
|
144
176
|
- `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.
|
|
177
|
+
- `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.
|
|
178
|
+
- `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.
|
|
179
|
+
- `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.
|
|
145
180
|
- 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.
|
|
146
181
|
- `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.
|
|
147
182
|
- `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.
|
|
@@ -185,7 +220,7 @@
|
|
|
185
220
|
- `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.
|
|
186
221
|
- `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.
|
|
187
222
|
- `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.
|
|
188
|
-
- `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; common journal retention/rotation regression; and the file/surface legacy diagnostic inventory without breaking removal.
|
|
223
|
+
- `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.
|
|
189
224
|
- `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.
|
|
190
225
|
- `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.
|
|
191
226
|
- `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.
|
|
@@ -240,7 +275,7 @@
|
|
|
240
275
|
- `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.
|
|
241
276
|
|
|
242
277
|
- `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.
|
|
243
|
-
- `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:
|
|
278
|
+
- `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.
|
|
244
279
|
- `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.
|
|
245
280
|
- `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.
|
|
246
281
|
|
|
@@ -316,6 +351,47 @@
|
|
|
316
351
|
command, path, permission, and request content out of Registry, tmux, notify,
|
|
317
352
|
diagnostics, support, and Archive sinks.
|
|
318
353
|
|
|
354
|
+
- `make test`: Exact Codex turn-start admission reads one fresh, content-free
|
|
355
|
+
lifecycle snapshot through the bound control epoch immediately before the
|
|
356
|
+
provider mutation. `TestExactAgentControlStartFreshLifecycleAdmissionTable`
|
|
357
|
+
pins the cached/fresh matrix, exact `turn-in-progress` and
|
|
358
|
+
`turn-state-unavailable` tokens, read-before-start ordering, cross-thread and
|
|
359
|
+
structurally inconsistent snapshot refusal, private-detail exclusion, and
|
|
360
|
+
zero mutation on every refusal.
|
|
361
|
+
`TestExactAgentControlStartRechecksBindingAfterFreshLifecycleRead` keeps the
|
|
362
|
+
Agent/Pane/runtime/generation/thread binding current across the fresh read,
|
|
363
|
+
while `TestExactAgentControlFreshTerminalReconcileClearsCachedApproval`
|
|
364
|
+
proves that an idle/terminal snapshot repairs stale turn and approval state
|
|
365
|
+
before the same request starts exactly one new turn. Start-specific behavior,
|
|
366
|
+
interrupt, approval, old-epoch, and canonical generation-consumer fences are
|
|
367
|
+
unchanged by the steer admission slice below.
|
|
368
|
+
|
|
369
|
+
- `make test`: Exact Codex steer admission reuses the content-free lifecycle
|
|
370
|
+
read and accepts only the cached turn that a fresh snapshot still proves is
|
|
371
|
+
the same exact active turn. `TestExactAgentControlSteerFreshLifecycleAcceptanceTable`
|
|
372
|
+
pins the active/idle/terminal/different-turn/unavailable matrix plus exact
|
|
373
|
+
lifecycle-read-before-steer order and one-or-zero steer wire count;
|
|
374
|
+
`TestExactAgentControlSteerDifferentTurnReconcilesWithoutRetargetingCurrentRequest`
|
|
375
|
+
proves reconciliation cannot retarget the current request and only an
|
|
376
|
+
explicit retry may address the newly observed turn.
|
|
377
|
+
`TestExactAgentControlSteerPostReadFenceMismatchIsTurnStateUnavailable` and
|
|
378
|
+
`TestExactAgentControlSteerOldOrCrossEpochReadsAndWritesZero` keep the
|
|
379
|
+
Agent/Pane/runtime/generation/thread/control-epoch fences closed before the
|
|
380
|
+
provider write. `TestBodyFreeSteerSuccessIsProviderAcceptanceWithDeliveryUnconfirmed`
|
|
381
|
+
kills a body-free-success-to-delivery-confirmed mutant by requiring the
|
|
382
|
+
structured `acceptance=provider`, `delivery=unconfirmed` receipt, while
|
|
383
|
+
`TestAgentControlCLISteerExactAcceptanceRefusalOutputAndExit` fixes the exact
|
|
384
|
+
CLI receipt, refusal tokens, and exit 0/1 mapping.
|
|
385
|
+
`TestAgentControlCLISteerRejectsMissingOrConfirmedDeliveryReceipt` prevents a
|
|
386
|
+
missing or fabricated delivery acknowledgement from reaching successful CLI
|
|
387
|
+
output. `make test-e2e E2E_SCENARIO=C01` runs the maintained
|
|
388
|
+
`test/e2e/codex-lifecycle.sh` replacement-sibling steer: it boundedly retries
|
|
389
|
+
only `turn-state-unavailable` with stdout and the exact sibling steer ledger
|
|
390
|
+
held at zero, then requires one qualified provider write and the exact
|
|
391
|
+
acceptance/unconfirmed-delivery receipt. Retry time is pacing only, never
|
|
392
|
+
lifecycle authority. Start, interrupt, approval, payload privacy, TUI
|
|
393
|
+
input/resume, and upstream delivery acknowledgement remain unchanged.
|
|
394
|
+
|
|
319
395
|
- `make test` / `make test-integration`: Codex public turn control live-binding
|
|
320
396
|
compatibility uses one strict six-field tmux frame. Unit tests accept only
|
|
321
397
|
literal `\037` and raw unit-separator spellings without generic escape
|
|
@@ -376,12 +452,25 @@
|
|
|
376
452
|
- `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.
|
|
377
453
|
|
|
378
454
|
- `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.
|
|
379
|
-
- `make test`: Codex native lifecycle and control cutover Phase 3 makes the endpoint broker the product's endpoint producer. `TestBrokerBoundAgentsShareOneUpstreamConnectionWithIndependentBindingEpochs` binds a prompted-create Agent and a stored-resume Agent through the real runtime host and requires one upstream connection between them, one shared connection epoch, two different binding epochs, and interleaved events that reach only the thread that named them. `TestBrokerThreadlessAndForeignEventsReachNoExactAgentBinding` runs the ambiguous fixtures - no thread id, a blank one, a foreign thread, and a foreign thread sharing the bound Agent's working directory - and requires the exact binding to receive none of them while a following attributed event proves the stream stayed live. `TestBrokerEpochRotationFencesTheOldEpochOutWithZeroEndpointWrites` replaces the connection under a live binding: the retired epoch's stream closes, its steer and its approval answer are both refused, the replacement connection records zero steer requests and zero answers from it, the binding epoch survives the reconnect unchanged, and the replacement epoch starts a turn normally. `TestBrokerRuntimeLossEndsTheEpochAndRefusesEveryLateMutation` takes the runtime away from a bound client and requires the epoch to end before any caller can present its fence, every late mutation and answer to be refused with zero endpoint writes, and a reopen against an unpublished runtime to refuse rather than start one. `TestBrokerDisconnectEndsTheEpochBeforeAnyReplacementBarrier` covers the promptness half of the disconnect contract: a binding survives an outage so the broker can keep reconnecting, so the binding's out-of-band suspension - not the ordered stream, which has nothing to say until the next barrier - retires the live epoch immediately, refuses its late mutations with zero endpoint writes, and keeps the stale barrier from being re-served while the endpoint is gone. `TestBrokerEpochBacklogOverflowEndsOnlyThatEpochStream` overruns one Agent's bounded stream and requires that Agent alone to be cut to a resync while its sibling on the same shared connection keeps receiving its own events. `TestBrokerApprovalLeaseAnswersTheExactRawRequestExactlyOnce` carries an inbound server request through the broker to the app layer with its byte-faithful raw id and its normalized rendering intact, decodes it with the existing envelope decoder, answers it once, and requires the repeat to be refused with exactly one entry in the endpoint's answer ledger. `TestBrokerEpochReadsTheExactLifecycleSnapshotThroughItsOwnFence` pins the control epoch's state input to the same fence its mutations use. `TestNativeLifecycleProducerIsExactlyOnePerActivationGeneration` and `TestBrokerProducerKeepsRecoveringPastTheLegacyAttemptCeiling` are the single-producer and persistent-reconnect audits: the producer decision is total and never yields both, and a producer that owns its own reconnect keeps recovering well past the legacy six-attempt budget without ever publishing the terminal `reconnect-exhausted` fallback, while that budget stays byte-identical for the producer that still needs it. `TestBrokerCutoverPreservesTheHookFallbackVocabulary` is the compatibility audit for the authority sources, the hook-suppression rule, and the pre-mutation convergence to `provider-hook`. On the endpoint side, `TestNativeCreateAndResumeAttachToTheExactCurrentUnmanagedEndpoint` runs the real create and resume paths against a fake Codex whose every argv is recorded and requires an exact-current endpoint the official manager does not own to accept one `thread/start`, one `turn/start`, and one `thread/resume` with a daemon start/stop/restart/kill/bootstrap/remote-control/login/config argv count of zero, while `TestNativeCreateAndResumeKeepSafeFallbackOnEveryRefusedAttachRow` keeps a skewed and an ownership-unknown endpoint refusing before the connection opens, as a safe fallback carrying its typed reason and with zero thread or turn requests. `TestEndpointAttachAuthorityIsTheOnlyWidenedProductRow` walks the availability x ownership x version table behind the product's own source and availability preconditions and requires exactly one row to differ from the retired native-action gate: the ready, exact-current, unmanaged one. `TestInstalledIsolatedBrokerNativeBindingSmoke` is opt-in through `PROJMUX_CODEX_CUTOVER_SMOKE_ROOT` with a matching contained `CODEX_HOME`, an isolated state domain, and inherited `TMUX`/`TMUX_PANE` stripped, and it is the only check that runs the cutover against a real Codex app-server: a direct `codex app-server --listen unix://` under that contained `CODEX_HOME`, which upstream reports as running with no daemon backend. It requires that endpoint to be unmanaged, exact-current, attach-allowed, and to carry no daemon lifecycle authority; runs the product's own prompted create there so the thread's rollout materializes; binds that exact thread through the product session over a runtime it publishes itself; starts one turn through the resulting broker epoch and requires the epoch's own fenced snapshot to show that exact turn in progress before steering it through the same wire; interrupts it; and then requires the runtime to leave no artifact behind. Every `codex` argv the whole path ran is recorded by a PATH shim the test installs, and the run fails unless all of them are `app-server proxy` or `app-server daemon version`, which is the zero-daemon-lifecycle-mutation half of the acceptance against a real endpoint rather than a fake one.
|
|
380
|
-
- `make test
|
|
455
|
+
- `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.
|
|
456
|
+
- `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.
|
|
381
457
|
- `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.
|
|
382
|
-
- `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
|
|
458
|
+
- `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.
|
|
383
459
|
- `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.
|
|
384
|
-
- `make test-e2e-shards`: the Linux
|
|
460
|
+
- `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.
|
|
461
|
+
- `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.
|
|
462
|
+
- `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.
|
|
463
|
+
- `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.
|
|
464
|
+
- `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.
|
|
465
|
+
- `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.
|
|
466
|
+
- `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.
|
|
467
|
+
- `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.
|
|
468
|
+
- `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.
|
|
469
|
+
|
|
470
|
+
- `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.
|
|
471
|
+
- `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.
|
|
472
|
+
- `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.
|
|
473
|
+
- `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.
|
|
385
474
|
|
|
386
475
|
## When To Update This List
|
|
387
476
|
|
|
@@ -396,13 +485,17 @@
|
|
|
396
485
|
|
|
397
486
|
- `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.
|
|
398
487
|
|
|
399
|
-
- `make test` / `make test-integration` / `make test-e2e`: Native Codex exact-selector mutation route authority carries the activation gate's exact raw `%N` through the existing private `__PROJMUX_RUNTIME_ANCHOR_PANE` only; the public `PROJMUX_*` environment, activation generation, sessionRef, thread/turn, and lifecycle meanings stay unchanged. `TestNativeActivationEnvironmentAddsOnlyThePrivateExactRouteAnchor`, `TestExactThreeUIDCreateAgentRouteIgnoresUnrelatedAmbientPane`, `TestNativePrivateActivationAnchorNegativeAuthorityMatrixIsFirstWriteZero`, and `TestStandaloneRouteRequiresBlankClassAndExactInheritedPaneReceipt` pin exact anchor production, UID owner-chain independence from an unrelated client, stale/malformed/wrong-server/foreign first-write refusal, and unchanged standalone receipt strength. `TestOutsideTmuxExactThreeUIDCreateAgentUsesOnlyQuietAppRoute`, `TestOutsideTmuxExactThreeUIDCreateRoutesProductionAIPresentationWrites`, `TestOutsideTmuxProductionAIPresentationFailureIsVisibleAndRollsBack`, `TestAgentResumeRoutesProductionAIPresentationWritesThroughExactRuntime`, and `TestProductionAIPaneBindersUseOnlySuppliedExactRuntimeRoute` require
|
|
488
|
+
- `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.
|
|
400
489
|
|
|
401
490
|
- `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.
|
|
402
491
|
|
|
403
492
|
- `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.
|
|
404
493
|
|
|
405
494
|
- `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.
|
|
495
|
+
- `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`.
|
|
496
|
+
- 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.
|
|
497
|
+
- `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.
|
|
498
|
+
- `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.
|
|
406
499
|
|
|
407
500
|
## E2E Required-Gate Evidence
|
|
408
501
|
|
|
@@ -475,21 +568,35 @@
|
|
|
475
568
|
a stable scenario ID, owner, ISO-8601 deadline, and at least three observations;
|
|
476
569
|
this closed zero-set contract rejects a non-empty row until execution/evidence
|
|
477
570
|
and non-gating wiring exist. Therefore the quarantine execution Negative is
|
|
478
|
-
N/A: its antecedent does not exist.
|
|
479
|
-
|
|
480
|
-
project-wide `Test` gate. Go
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
evidence and a separate
|
|
571
|
+
N/A: its antecedent does not exist. `test/e2e/shard-contract.sh` is the single
|
|
572
|
+
canonical owner proving that both E2E child families still feed the exact
|
|
573
|
+
`E2E Tests` compatibility context and the project-wide `Test` gate. Go
|
|
574
|
+
`Unit Tests` remain an exact required child and outside automatic
|
|
575
|
+
classification/quarantine: they have no per-test attempt artifact or `class`
|
|
576
|
+
surface, so assigning an observed-flake count would invent evidence. A future
|
|
577
|
+
unit quarantine first needs attempt-preserving per-test evidence and a separate
|
|
578
|
+
contract.
|
|
579
|
+
- `test/e2e/admission-contract.sh` (`make test-e2e-contract`) pins the local
|
|
580
|
+
full-suite capacity boundary used by default `make test-e2e`: one invocation
|
|
581
|
+
owns the active state while another waits, and the wait diagnostic plus state
|
|
582
|
+
file expose `reason=local-full-suite-capacity`, capacity one, owner pid,
|
|
583
|
+
start time, cwd, and exact state path. Both callers retain their own terminal
|
|
584
|
+
status after admission. `PROJMUX_E2E_ADMISSION_BYPASS=1` is the explicit
|
|
585
|
+
stress path and is required to overlap a live admitted owner. Single-scenario
|
|
586
|
+
replay and CI shard/suite selectors remain outside this local full-suite
|
|
587
|
+
boundary; `test/e2e/shard-contract.sh` keeps the four Linux plus two suite
|
|
588
|
+
jobs on independent runners with `fail-fast: false` and the stable required
|
|
589
|
+
name `E2E Tests`, while also proving both selector families enter with a local
|
|
590
|
+
slot already occupied. A dead owner is diagnosed as `stale-owner` and reclaimed
|
|
591
|
+
only while the active hard link still identifies its exact owner record.
|
|
485
592
|
- `test/e2e/evidence-contract.sh` (`make test-e2e-contract`) and
|
|
486
593
|
`test/e2e/reliability-contract.sh` (`make test-e2e-reliability`) keep the persisted
|
|
487
594
|
`projmux.e2e-attempt/v1` evidence and success result hash stable while adding
|
|
488
595
|
a separate stderr-only `projmux.e2e-terminal/v1` first-failure record. Its
|
|
489
596
|
closed fields identify scenario, phase, owner, shard, status, repository
|
|
490
597
|
source/line, sanitized single-scenario command, binary/state hashes, and exact
|
|
491
|
-
replay without raw argv. Synthetic L06/L08/L16 fixtures pin
|
|
492
|
-
state, bounded Registry changed paths plus pending owned state, and child/file/
|
|
598
|
+
replay without raw argv. Synthetic L06/L08/L16 fixtures pin cardinality-neutral
|
|
599
|
+
holder/racer event state, bounded Registry changed paths plus pending owned state, and child/file/
|
|
493
600
|
exact tmux state plus sanitized hashed tails; denylist checks reject token,
|
|
494
601
|
raw HOME/path, full Registry, and raw command leakage. Normal scenario meaning,
|
|
495
602
|
one-build/four-shard topology, and `.bin/e2e-evidence` retention are unchanged.
|
|
@@ -521,11 +628,12 @@
|
|
|
521
628
|
exposes the predicate as a truth table, and `e2e-evidence.py flake-rate <dir>`
|
|
522
629
|
reports per-scenario flake rate over downloaded attempt evidence plus an
|
|
523
630
|
`E2E_FLAKE_FLIP` line for every stored class the other attempts contradict.
|
|
524
|
-
L06 diagnostics are derived the same way: `attribution` names
|
|
525
|
-
|
|
526
|
-
no
|
|
527
|
-
|
|
528
|
-
|
|
631
|
+
L06 diagnostics are derived the same way: `attribution` names the event-time
|
|
632
|
+
racer outcome (`registry-lock-deadline`, `command-failure`, no observed racer,
|
|
633
|
+
or no racer failure) with no `other`/`unknown` fallback. Holder held/released
|
|
634
|
+
timestamps and racer PID/start/finish values are captured while those processes
|
|
635
|
+
run; a leftover compatibility marker cannot invent holder identity or timing.
|
|
636
|
+
The cardinality-neutral payload travels as `projmux.e2e-diagnostic/v3` while the
|
|
529
637
|
unchanged L08/L16 payloads stay `v1`, and evidence retained from before the
|
|
530
638
|
rename still validates and still aggregates. `PROJMUX_E2E_ATTEMPT` now defaults to `GITHUB_RUN_ATTEMPT`, so a rerun's
|
|
531
639
|
evidence is no longer written as attempt 1.
|
|
@@ -554,11 +662,19 @@
|
|
|
554
662
|
refuse a writer that never reached the lock.
|
|
555
663
|
`TestAConcurrentWriterIsNotRefusedInsideAnotherCommitsMarkerWindow` builds that
|
|
556
664
|
window directly and requires the second writer to wait through it. The
|
|
557
|
-
`
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
665
|
+
`TestHostStartupAndShutdownHaveDeterministicHappensBefore` pins `StartHost`
|
|
666
|
+
return as the startup barrier: immediate and concurrent `Close` stop accept
|
|
667
|
+
before waiting for sessions, stop the broker before publishing the shared
|
|
668
|
+
terminal `Done`, and return every caller through that same channel.
|
|
669
|
+
`TestStaleRuntimeArtifactsAreReclaimedOnlyByExactOwnerProof/late_old_host_close_preserves_replacement_artifacts`
|
|
670
|
+
keeps shutdown cleanup behind `SameFile`, so an old Host cannot remove or
|
|
671
|
+
change a replacement socket or record. The stable `Race Tests` CI aggregate
|
|
672
|
+
now fails closed over separate core, Codex broker, and Codex app-server
|
|
673
|
+
children; the core child preserves metadata, notify, and recent-window
|
|
674
|
+
coverage, and `CIWorkflowContractTest` pins both Codex failure paths red.
|
|
675
|
+
Required real-tmux L06 owns only the deterministic holder/released-before-
|
|
676
|
+
deadline/waiter convergence guarantee. Eight-way throughput and fairness stay
|
|
677
|
+
outside required acceptance in the explicit stress command documented above.
|
|
562
678
|
- `make test` / `make test-integration`: attention omitted-target convergence
|
|
563
679
|
- `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.
|
|
564
680
|
is enforced by `TestAttentionMutationOmittedTargetMatchesExplicitPaneLedger`,
|
|
@@ -575,9 +691,1276 @@
|
|
|
575
691
|
have non-zero status, stdout zero, bounded actionable stderr, and unchanged
|
|
576
692
|
attention state.
|
|
577
693
|
|
|
694
|
+
### Codex app-server generation Phase 0 tests
|
|
695
|
+
|
|
696
|
+
- `TestCodexEndpointAndAuthorityRefsRoundTripExactlyAndIgnorePaneGeneration`,
|
|
697
|
+
`TestLegacyCodexRefsRemainGenerationUnavailableWithoutInference`,
|
|
698
|
+
`TestLegacyHookReobservationPreservesAnExactCodexEndpointRef`, and
|
|
699
|
+
`TestCodexEndpointIdentityRejectsNonCanonicalWhitespace` own the additive
|
|
700
|
+
Registry schema: refs reopen exactly, remain independent of Pane generation,
|
|
701
|
+
reject noncanonical identities, preserve future exact refs across legacy
|
|
702
|
+
hook observations, and never infer a current endpoint for legacy state.
|
|
703
|
+
- `TestBoundedPoolRejectsEveryInvalidV1Topology`,
|
|
704
|
+
`TestBoundedPoolStateSpaceProperty`, and
|
|
705
|
+
`FuzzBoundedPoolNeverAcceptsMoreThanTwoLiveSlots` own the current-one plus
|
|
706
|
+
draining-one state model.
|
|
707
|
+
- `TestCompositeAuthorityRejectsSameNumberCrossGenerationAndLegacyWithZeroWrites`
|
|
708
|
+
and `TestOldStopBarrierKeepsSuccessorResumeAtZeroUntilSafe` own the Phase 0
|
|
709
|
+
write-zero fences for equal numeric epochs, missing generation identity, and
|
|
710
|
+
same-thread successor resume before old-stop.
|
|
711
|
+
- `TestReadOnlyUpgradePlanIsRepeatableContentFreeAndNamesExactBlockers` freezes
|
|
712
|
+
exact JSON/text blockers and all five zero mutation counters;
|
|
713
|
+
`TestReadOnlyUpgradePlanNeverRendersInvalidIdentitiesOrQualificationFields`
|
|
714
|
+
keeps hostile invalid input out of both projections.
|
|
715
|
+
- `TestQualificationDecisionTableClosesPhase2OnEitherSharedStateFailure` and
|
|
716
|
+
`TestQualificationReceiptRetainsOnlyContentFreeAllowlistedFields` own the
|
|
717
|
+
persisted go/no-go and redaction schema.
|
|
718
|
+
- `TestContentAddressedBundleSurvivesSourceRemovalForServerTUIAndHelpers`,
|
|
719
|
+
`TestBundleDriftAndProtocolMismatchRefuseBeforeFinalCommit`, and
|
|
720
|
+
`TestCommittedBundleTamperingIsRefusedBeforeLaunch`,
|
|
721
|
+
`TestCommittedBundleRefusesMatchingSymlinksBeforeLaunch`, and
|
|
722
|
+
`TestBundleRefusesNonCanonicalRootsAndVersions` own immutable complete
|
|
723
|
+
release-bundle retention and refusal, including manifest/artifact links and
|
|
724
|
+
noncanonical mutation roots.
|
|
725
|
+
- Opt-in `TestInstalledIsolatedGenerationPoolQualification` is the
|
|
726
|
+
declared-pair shared-state conformance. The pair under test comes from
|
|
727
|
+
`PROJMUX_CODEX_GENERATION_OLD_VERSION`/`_NEW_VERSION` and each declared
|
|
728
|
+
version must match what its binary reports; the canonical receipt is written
|
|
729
|
+
to `PROJMUX_CODEX_GENERATION_RECEIPT` before the verdict is asserted, so a
|
|
730
|
+
refusal is on disk too. It strips inherited tmux identity, uses one unique
|
|
731
|
+
private state root and two private sockets, records no provider content or
|
|
732
|
+
secrets, requires distinct-thread overlap plus the old-stop same-thread
|
|
733
|
+
barrier, tallies the receipt's coverage counters from the ledgers it already
|
|
734
|
+
writes, and performs exact cleanup. See
|
|
735
|
+
[codex-generation-pool.md](codex-generation-pool.md).
|
|
736
|
+
- `TestQualificationGateRefusesEvidenceCountersNoObservationBacks`,
|
|
737
|
+
`TestQualificationEvidenceIntegrityIsFixedByCounterAndClaim`, and
|
|
738
|
+
`TestQualificationSchemaVersionRefusesTheCoverageFreeReceipt` own the
|
|
739
|
+
evidence-counter integrity the gate checks and `Validate` cannot: a
|
|
740
|
+
self-consistent YES whose coverage counters back nothing is refused as forged,
|
|
741
|
+
and a coverage-free version-1 receipt does not decode.
|
|
742
|
+
- `TestActivateManagedCurrentRefusesEveryUnqualifiedRequestBeforeDraining`,
|
|
743
|
+
`TestActivateManagedCurrentPublishesTheQualificationItRanUnder`,
|
|
744
|
+
`TestActivateManagedCurrentRefusesAnExistingPoolWithNoReceipt`,
|
|
745
|
+
`TestResumeRefusesAnUnqualifiedPairBeforeAnyHandoverEffect`, and
|
|
746
|
+
`TestResumeStillRetiresAVacantGenerationWithoutAReceipt` own the two entry
|
|
747
|
+
gates: each refuses before its own irreversible step with a path-specific
|
|
748
|
+
token, the accepted receipt is published into the journal, and the vacancy
|
|
749
|
+
lane the planner leaves open stays open.
|
|
750
|
+
- `TestQualificationStoreAnswersOnlyForThePairAReceiptNames`,
|
|
751
|
+
`TestQualificationStoreRefusesAFileThatIsNotItsOwnReceipt`,
|
|
752
|
+
`TestQualificationStorePathStaysInsideItsOwnDirectory`,
|
|
753
|
+
`TestUpgradeQualifyCarriesAProducedReceiptToTheEntryPathStore`,
|
|
754
|
+
`TestUpgradeQualifyStoresNothingItWouldNotHonor`, and
|
|
755
|
+
`TestUpgradeQualifyRequiresExactlyOneAbsoluteReceipt` own the route from a
|
|
756
|
+
produced receipt to the store the entry paths read.
|
|
757
|
+
- `TestDeclaredGenerationPairAcceptsAnyDistinctReceiptVersionPair`,
|
|
758
|
+
`TestDeclaredGenerationPairRefusesEmptyEqualAndNonVersionTokens`,
|
|
759
|
+
`TestEmittedReceiptFileRoundTripsToTheIdenticalResult`,
|
|
760
|
+
`TestEmittedReceiptReplacesAnEarlierVerdictAndRefusesRelativePaths`, and
|
|
761
|
+
`TestGenerationQualificationSourcesCarryNoVersionLiteral` own the declared
|
|
762
|
+
pair and the receipt file: any distinct receipt version pair is accepted, the
|
|
763
|
+
emitted file is `0600` and byte-identical to the canonical result, a rerun
|
|
764
|
+
never republishes a stale verdict, and neither harness source may pin a
|
|
765
|
+
version literal again. Only the pair is declared — every evidence boolean and
|
|
766
|
+
counter stays measured.
|
|
767
|
+
- `TestEmittedQualificationReceiptEntersTheUpgradeRequestVerbatim` carries the
|
|
768
|
+
emitted receipt file into the `qualification` field of an `agent app-server
|
|
769
|
+
upgrade --request` document and reloads it through the real request loader.
|
|
770
|
+
The declared pair, every counter, `Validate`, and `GateQualification` survive
|
|
771
|
+
the hop, and a tampered evidence counter stops decoding instead of keeping
|
|
772
|
+
its verdict.
|
|
773
|
+
- `CIWorkflowContractTest.test_generation_pool_lane_is_dispatch_only_and_fail_closed`
|
|
774
|
+
and `test_generation_pool_runner_types_every_terminal_outcome` own the
|
|
775
|
+
dispatch-only lane and `scripts/test-generation-pool-qualification.sh`: the
|
|
776
|
+
lane never schedules, never joins a required aggregate, always uploads its
|
|
777
|
+
typed record, and is green only on a measured `pass`; the runner refuses a
|
|
778
|
+
non-version pair before creating any root and still types an unavailable
|
|
779
|
+
binary as `unsupported`.
|
|
780
|
+
|
|
781
|
+
### Codex app-server generation Phase 1 tests
|
|
782
|
+
|
|
783
|
+
- `TestGenerationLifecycleProjectionClosedTable` and
|
|
784
|
+
`FuzzGenerationLifecycleProjectionMatchesClosedTable` own the single 6×7
|
|
785
|
+
effective-interaction × generation-state tuple. Planned drain, handover,
|
|
786
|
+
recovery, and blocked projections require an exact operation endpoint;
|
|
787
|
+
`TestPlannedGenerationProjectionRequiresExactDurableOperationRef`,
|
|
788
|
+
`TestMarkerlessCrashAndVersionDriftRemainOrdinaryFailure`,
|
|
789
|
+
`TestMarkerlessCrashAndVersionDriftRemainOrdinaryFailureThroughProductionReconcile`, and
|
|
790
|
+
`TestLifecycleProjectionAuthorityInputsExcludeProcessHeuristics` keep
|
|
791
|
+
process exit, version, executable, path, socket, and PID evidence from
|
|
792
|
+
becoming maintenance authority.
|
|
793
|
+
- `TestRuntimeMutationEquivalenceTableIsClosed`,
|
|
794
|
+
`TestRuntimeMutationClassesMatchDecisionKernel`, and
|
|
795
|
+
`TestRuntimeMutationCompositeFenceAndSiblingRecorder` own the eight
|
|
796
|
+
owner/foreign × current/stale × target/sibling classes. Exactly one class
|
|
797
|
+
has a semantic effect; equal numeric epochs from another endpoint generation
|
|
798
|
+
or restarted broker invoke neither Registry nor tmux writer.
|
|
799
|
+
- `TestGenerationLifecycleSinkCompositeAuthorityHasZeroCrossWrites` exercises
|
|
800
|
+
the application sink with a Registry, fake tmux, and the exact-Pane authority
|
|
801
|
+
fence. Endpoint generation, broker runtime, connection epoch, binding epoch,
|
|
802
|
+
and stored operation mismatches return the managed-observation refusal with
|
|
803
|
+
zero Registry and tmux writes. A syntactically valid provider-supplied
|
|
804
|
+
operation ID cannot replace the durable operation authority.
|
|
805
|
+
- `TestNativeSemanticApplyAndInvalidationShareExactPaneFence` deterministically
|
|
806
|
+
pauses an older provider-control Apply after its Registry commit and before
|
|
807
|
+
its Pane writes, starts disconnect invalidation behind the same exact-Pane
|
|
808
|
+
fence, and requires invalidation to remain the final Registry/Pane tuple.
|
|
809
|
+
It then pins the recovery order separately: a replacement snapshot may Apply
|
|
810
|
+
while authority is `invalidating`, followed by the exact ready authority.
|
|
811
|
+
- `TestGenerationLifecycleProjectionReconcileWritesOnceThenZero`,
|
|
812
|
+
`TestGenerationLifecycleProductionReconcileRejectsForeignOrSiblingAuthorityWithZeroWrites`, and
|
|
813
|
+
`TestGenerationLifecycleProjectionUsesIsolatedRealTmuxAndExactCleanup` own
|
|
814
|
+
production fake and isolated-real-tmux convergence for draining,
|
|
815
|
+
handover-pending, recovering, and blocked tuples. They prove the durable
|
|
816
|
+
lifecycle/activation input survives a later full resource reconcile and
|
|
817
|
+
makes its second pass zero-write. The real fixture strips inherited
|
|
818
|
+
`TMUX`/`TMUX_PANE`, starts on one unique contained physical `-S` path, accepts
|
|
819
|
+
that path only from the successful `new-session` Pane/socket receipt, waits
|
|
820
|
+
on tmux command completion as its semantic barrier, and kills/removes only
|
|
821
|
+
that queried and containment-proven socket; there is no logical-name cleanup
|
|
822
|
+
fallback.
|
|
823
|
+
- `TestPlanOnlyMutationProductSurfaceInventoryIsBidirectionalAndClosed` keeps
|
|
824
|
+
generation presentation under the existing `agent.presentation` typed
|
|
825
|
+
surface and requires the complete composite fence in its guard. Existing
|
|
826
|
+
pure reducer PBT, Phase 0 endpoint/authority tests, broker reconnect tests,
|
|
827
|
+
and C01 remain their canonical unique boundaries. The complete
|
|
828
|
+
old/new-mutant migration receipt is in
|
|
829
|
+
[codex-generation-pool.md](codex-generation-pool.md#mapping-and-authority-test-migration-ledger).
|
|
830
|
+
|
|
831
|
+
### Provider capability matrix Phase 0 tests
|
|
832
|
+
|
|
833
|
+
- `TestAgentCapabilityCatalogIsClosedCartesianMatrix`,
|
|
834
|
+
`TestAgentCapabilityCatalogPinsCurrentGroupsAndDeferredVocabulary`, and
|
|
835
|
+
`TestAgentCapabilityCatalogPinsCodexNativeAndSharedFamilies` own the single
|
|
836
|
+
action × provider catalog, including all eight app-server generation leaves,
|
|
837
|
+
the four unsupported future coordination cells, the four integration
|
|
838
|
+
targets, and the three usage providers.
|
|
839
|
+
- `TestAgentCapabilitiesProviderProjectionIsStaticAndReadOnly` and
|
|
840
|
+
`TestAgentCapabilitiesExactProjectionSeparatesRuntimeEpochAndAvailability`
|
|
841
|
+
own the provider-only versus exact-Agent projection and prove that Registry
|
|
842
|
+
reads, live transport, and provider writes are not smuggled into static
|
|
843
|
+
support. `TestUnsupportedNativeAgentActionsRefuseBeforeRuntimeOrProviderEffects`
|
|
844
|
+
owns the pre-runtime non-Codex refusal for turn, approval, and review.
|
|
845
|
+
- `TestProviderResumeExecutionPreservesExactAgentAndLaunchesOneExactArgv`
|
|
846
|
+
owns the three-provider resume execution matrix: one exact child argv, stable
|
|
847
|
+
Agent uid/name, and no fresh Agent creation.
|
|
848
|
+
- `TestAgentCapabilityCatalogRoutesMatchExecutableHelpGraph` and
|
|
849
|
+
`TestAgentIntegrationAndGenerationLeafHelpMatchesCapabilityCatalog` own
|
|
850
|
+
catalog ↔ CLI/help parity. The generated CLI reference test owns the docs
|
|
851
|
+
projection; provider-neutral `agent message` and `agent wait` are the only
|
|
852
|
+
coordination routes, and provider-specific Agent namespaces remain absent.
|
|
853
|
+
|
|
854
|
+
### Resume Picker exact projmux binding Phase 0 tests
|
|
855
|
+
|
|
856
|
+
- `TestAIResumeExactBindingProjectsCoherentAgentAndPaneNames` owns exact
|
|
857
|
+
provider/conversation sessionRef selection: prefer a round-trip Pane owner,
|
|
858
|
+
then lexical Agent UID, and project that same Agent's context and stable
|
|
859
|
+
name with its optional Pane stable name. Duplicate ordering cannot combine
|
|
860
|
+
different Agents' fields. Stable names come only from `metadata.name`.
|
|
861
|
+
- `TestAIResumeBindingDisplayFallsBackWithoutRoundTripPane` pins the three
|
|
862
|
+
binding-first row forms: `[agent → pane] conversation`, `[agent] conversation`,
|
|
863
|
+
and the unchanged conversation-only row. Missing/stale/foreign Pane bindings
|
|
864
|
+
omit only the Pane; missing Agent bindings retain exact Value, count, and
|
|
865
|
+
order. No cwd, topic, live tmux title, newest resource, or pane-order guess
|
|
866
|
+
recovers a deleted binding.
|
|
867
|
+
- `TestAIResumeBindingNamesPreserveFullSearchAndDetailWithinCellBudget` pins
|
|
868
|
+
the existing 90-cell trailing budget, balanced binding brackets, ANSI/CJK
|
|
869
|
+
clipping, 128-byte stable names, and full names in SearchKey and selected
|
|
870
|
+
detail projection. `TestAIResumeBindingLongNamesAreRecoverableByNativeDetailScroll`
|
|
871
|
+
reconstructs complete 128-byte and CJK names from the app-local, 64-cell
|
|
872
|
+
continuation lines and verifies native Shift-Down exposes every chunk at
|
|
873
|
+
80 columns in EN/KO. The canonical full binding line remains intact; only
|
|
874
|
+
overflowing bindings add continuation fields under Details.
|
|
875
|
+
`TestAIResumeBindingNativeFramesLocaleAndSizeGolden`
|
|
876
|
+
exercises the public native runner on an isolated PTY/Registry fixture at
|
|
877
|
+
80x24 and 120x40 in EN/KO; short Agent and Pane names are visible together in
|
|
878
|
+
both the row and detail. The PTY harness uses Python 3's standard library.
|
|
879
|
+
- `TestAIResumeBindingSnapshotIsInvocationLocalAndAddsNoReadsOrWrites`
|
|
880
|
+
pins one Registry load per invocation, cached detail reads, unchanged
|
|
881
|
+
row/order/Value/footer after focus, zero extra command/file reads and writes,
|
|
882
|
+
and next-open conversation fallback after Registry deletion. Binding names
|
|
883
|
+
are an invocation snapshot: rename/deletion is observed on the next open,
|
|
884
|
+
historical names are not persisted or reconstructed after cascade. Existing
|
|
885
|
+
title suppression/precedence, provider settlement/status, exact routing,
|
|
886
|
+
and first-frame bound suites remain the authority for those unchanged paths.
|
|
887
|
+
### Resume Picker provider session title Phase 1 tests
|
|
888
|
+
|
|
889
|
+
- `TestClaudeExplicitTitleProvenanceWithinBoundedScan` pins top-level
|
|
890
|
+
`type:ai-title` / `aiTitle`, first valid explicit title, duplicate stability,
|
|
891
|
+
foreign session rejection, blank/full-ID/short-ID rejection followed by a
|
|
892
|
+
valid title, and the line-100 inclusive bound with no line-101 read. An
|
|
893
|
+
ordinary Claude prompt cannot stop the scan before a later explicit title.
|
|
894
|
+
Explicit titles normalize whitespace without prompt-noise filtering.
|
|
895
|
+
- `TestCodexFirstRealPromptHasDerivedTitleProvenance` pins the first typed
|
|
896
|
+
`user_message` event or user-role response text after scaffold, assistant,
|
|
897
|
+
tool-carrier, untyped, and wrong-provider records. The label is marked
|
|
898
|
+
`derived-user-prompt`; Claude `ai-title` and Codex app-server non-ID titles
|
|
899
|
+
are `explicit-provider-title`. `TestNativeTitleProvenanceRejectsBlankAndIDCandidates`
|
|
900
|
+
keeps blank/full-ID/short-ID native candidates without title authority.
|
|
901
|
+
- `TestAIResumeTitleAuthorityProviderSourceProvenanceMatrix` closes the
|
|
902
|
+
provider × source × provenance × ID/context table. For Claude/Codex the
|
|
903
|
+
conversation label order is explicit title, derived Codex rollout prompt,
|
|
904
|
+
exact-bound Agent topic, exact-bound Agent provider, localized Untitled.
|
|
905
|
+
Canonical titles never lose to the bound Agent's topic/provider; stable
|
|
906
|
+
binding names remain independent. Antigravity keeps its existing known-source
|
|
907
|
+
title and context-first behavior.
|
|
908
|
+
This narrowly supersedes the earlier Resume Picker prohibition on prompt
|
|
909
|
+
inference and context-before-provider-title policy: only bounded Codex
|
|
910
|
+
typed real-user prompts gain derived display authority, matching the adopted
|
|
911
|
+
native Codex resume-title policy. Ordinary Claude prompts, missing/unknown
|
|
912
|
+
provenance, unknown/mismatched sources, and ID-like titles remain rejected;
|
|
913
|
+
no summary generation or additional transcript read is introduced.
|
|
914
|
+
`TestAIResumeMissingProvenanceDoesNotInferConversationTitle` preserves the
|
|
915
|
+
rejection boundary when a caller provides only a transcript/rollout string.
|
|
916
|
+
- `TestResumeSummaryPreservesTitleProvenanceWithoutAdditionalReads` and
|
|
917
|
+
`TestAIResumeTitleDiscoverySummaryRoundTripNeedsNoTranscriptReread` carry the
|
|
918
|
+
content-free discriminator through SessionMeta → ResumeSummary → SessionMeta.
|
|
919
|
+
Real disposable Claude/Codex discovery is projected after its transcript has
|
|
920
|
+
been removed; row/detail title and exact provider/ID Value remain available.
|
|
921
|
+
`TestAIResumeBindingSnapshotIsInvocationLocalAndAddsNoReadsOrWrites` now
|
|
922
|
+
exercises a canonical title with binding while keeping one Registry load,
|
|
923
|
+
cached detail reads, zero extra reads/writes, frozen row/order/Value/footer,
|
|
924
|
+
and next-open binding deletion behavior.
|
|
925
|
+
- `TestAIResumeProviderTitleBindingNativeFramesGolden` exercises the public
|
|
926
|
+
native runner on isolated PTYs at 80x24 and 120x40 in EN/KO for Claude explicit,
|
|
927
|
+
Codex rollout derived, and Codex app-server explicit rows. Each source is
|
|
928
|
+
selected in turn; the golden requires combined binding/title rows, the exact
|
|
929
|
+
provider/title detail heading, and full conversation ID independently of
|
|
930
|
+
preview text. Existing Phase 0 binding, provider settlement/latency, privacy,
|
|
931
|
+
frozen-frame, and exact-routing suites retain their unchanged contracts.
|
|
932
|
+
|
|
933
|
+
### Codex observer disconnect-reason Phase 0 tests
|
|
934
|
+
|
|
935
|
+
- `TestCodexObserverEventLoopExitsCarryTheirOwnReasonToken` owns the exit-path
|
|
936
|
+
table: every way out of the observer event loop publishes the vocabulary
|
|
937
|
+
token for that path, and the authority writes it pins prove the recovery
|
|
938
|
+
strategy did not move when the tokens became precise. A cancelled observer,
|
|
939
|
+
a closed notification stream, and a real endpoint disconnect no longer share
|
|
940
|
+
a word.
|
|
941
|
+
- `TestCodexObserverExitPathsAreOneToOneWithTokens` owns the converse
|
|
942
|
+
property: the exit tokens are distinct, neither the `unrecorded` nor the
|
|
943
|
+
retired `disconnected` bucket is reachable from an exit, and `hold` is a
|
|
944
|
+
stored decision rather than a comparison against the published reason.
|
|
945
|
+
- `TestCodexObserverReasonVocabularyIsClosed` owns the single vocabulary.
|
|
946
|
+
`codexNativeReason`'s four open-failure tokens are members of it, every
|
|
947
|
+
token round-trips through `safeCodexAuthorityReason`, and anything outside
|
|
948
|
+
it renders as `bounded reason unavailable`.
|
|
949
|
+
- `TestCodexBrokerEpochRecordsWhyItsStreamClosed` owns the close cause at the
|
|
950
|
+
layer that knows it. All five calls that end a broker epoch record a
|
|
951
|
+
different token, which is what separates an upstream connection that went
|
|
952
|
+
away from a rotated barrier, a revoked binding, a backlog overflow, and an
|
|
953
|
+
observer that closed its own connection.
|
|
954
|
+
- `TestCodexObserverJournalRecordsConnectDisconnectReconnectInOrder` owns the
|
|
955
|
+
durable history the pane option cannot hold: connect, disconnect, and
|
|
956
|
+
reconnect records in time order, each carrying the epoch label, and no
|
|
957
|
+
fallback record behind a held disconnect.
|
|
958
|
+
- `TestCodexObserverJournalRecordFieldsAreWhitelisted` is the negative audit.
|
|
959
|
+
It names every column an observer record may carry and refuses an
|
|
960
|
+
out-of-vocabulary reason and an unnamed transition.
|
|
961
|
+
- `TestCodexObserverJournalCoalescesRepeatedTransitions` owns the write-rate
|
|
962
|
+
boundary on the shared `ai-ingest.log` sink: one record per
|
|
963
|
+
(transition, reason) pair per window, with the suppressed count riding the
|
|
964
|
+
next record so the flap rate stays readable.
|
|
965
|
+
- `TestObserverRecoversIntoANewEpochAfterConsumerBacklogOverflow`,
|
|
966
|
+
`TestBacklogOverflowStopIsDistinguishableFromARecoveringInvalidating`, and
|
|
967
|
+
`TestBacklogOverflowRecoverySurvivesATransientBindingReadFailure` own
|
|
968
|
+
recovery after a consumer-side backlog overflow. All three drive the real
|
|
969
|
+
broker runtime, the real binding session, and the real observer loop: the
|
|
970
|
+
observer is held inside one sink write until its epoch's bounded stream
|
|
971
|
+
overruns, which is the actual `end(backlog-overflow)` plus `resync` pair
|
|
972
|
+
rather than an injected close cause. A sibling Agent holds its own binding on
|
|
973
|
+
the same shared connection throughout, because the overflowing binding being
|
|
974
|
+
the broker's last one retires the upstream connection and would make these
|
|
975
|
+
measure an endpoint reconnect instead. The first pins the guarantee - Open is
|
|
976
|
+
called again after the resync, the replacement barrier publishes a new epoch
|
|
977
|
+
label, and the overflowed epoch's records are exactly connected/ready,
|
|
978
|
+
disconnected/backlog-overflow, reconnecting/backlog-overflow. The second owns
|
|
979
|
+
the operator distinction: when the exact binding stops being current the
|
|
980
|
+
scheduler is right to stop, but the Pane keeps the
|
|
981
|
+
`invalidating|<epoch>|backlog-overflow` projection and `SetAuthority` refuses
|
|
982
|
+
every later correction, so `observer.stopped` with result `stuck` is the only
|
|
983
|
+
surface on which a stuck Pane differs from one still reconnecting on the same
|
|
984
|
+
projection. The third owns the defect behind that shape: `BindingCurrent`
|
|
985
|
+
returns false both for a replaced binding and for a Registry or `tmux` read
|
|
986
|
+
that merely failed, and the recovery scheduler took its only non-cancel exit
|
|
987
|
+
on a single such sample. It now makes the same bounded wait the loop head has
|
|
988
|
+
always made, so a transient read failure no longer retires a live activation.
|
|
989
|
+
- `TestObserverRecoversWhenTheOverflowedBindingWasTheBrokersLast` and
|
|
990
|
+
`TestLastBindingOverflowRecordsEveryFailedReplacementOpen` own the same
|
|
991
|
+
overflow under the opposite premise: no sibling binding, so the resync takes
|
|
992
|
+
the runtime to zero bindings. `Broker.wanted()` is `len(b.bindings) > 0` and
|
|
993
|
+
therefore broker-global, so `serve` folds the shared upstream connection and
|
|
994
|
+
no replacement barrier can exist until it is reopened. That premise is not
|
|
995
|
+
exotic - it holds permanently for a user who runs one Codex pane - and the
|
|
996
|
+
fold itself is a race, because a rebind that lands before `serve` processes
|
|
997
|
+
the unbind wake leaves the connection up. The fixture gates every replacement
|
|
998
|
+
Open on the fold so the premise is a fact rather than a hope. The first pins
|
|
999
|
+
that the fold self-heals whenever upstream is reachable. The second owns the
|
|
1000
|
+
defect: while upstream is away every replacement Open blocks to its own open
|
|
1001
|
+
timeout - 15s in production - and the scheduler computed the failure's
|
|
1002
|
+
vocabulary token and discarded it, so a Pane sat on
|
|
1003
|
+
`invalidating|<epoch>|backlog-overflow` with no record of any attempt, which
|
|
1004
|
+
is byte-identical to the stuck Pane `observer.stopped` exists to name except
|
|
1005
|
+
that recovery really was running and so no terminal record arrived either.
|
|
1006
|
+
The failure is now recorded on the retry transition, keyed apart from the
|
|
1007
|
+
epoch-loss token so the journal's coalescing window folds a long outage into
|
|
1008
|
+
one line plus a repeat count, and the disconnect's reason column is untouched.
|
|
1009
|
+
- `TestLastBindingRevokedByOverflowFoldsTheUpstreamConnection`
|
|
1010
|
+
(`internal/integrations/agents/codexbroker`) owns the invariant `wanted()`
|
|
1011
|
+
exists for, against the involuntary revocation paths. `Bind` and
|
|
1012
|
+
`Binding.Close` both signal the supervisor because both change what the
|
|
1013
|
+
connection has left to serve; the involuntary paths - a bounded delivery queue
|
|
1014
|
+
that overflowed, a thread-scoped snapshot refusal - deleted the binding from
|
|
1015
|
+
routing with no wake, so a runtime could reach zero bindings with nothing
|
|
1016
|
+
pending on the wake channel and `serve` would stay in its select holding an
|
|
1017
|
+
upstream connection nothing is bound to until the host idle timer recycled the
|
|
1018
|
+
whole runtime. A one-deep queue makes the overflow exact rather than a volume
|
|
1019
|
+
guess, and the test binds exactly one thread and never replaces it, because a
|
|
1020
|
+
rebind carries its own signal and would hide the gap.
|
|
1021
|
+
- `TestLiveEpochSurvivesATransientBindingReadFailure` and
|
|
1022
|
+
`TestLiveEpochBindingLossLeavesATerminalRecord` own the live epoch's
|
|
1023
|
+
`bindingTicker` exit, which carried the same single-sample defect the recovery
|
|
1024
|
+
scheduler had. One false from `BindingCurrent` ended the observer, and that
|
|
1025
|
+
exit wrote no authority - correctly, because `SetAuthority` refuses once the
|
|
1026
|
+
predicate is false - and no record, so the Pane kept
|
|
1027
|
+
`provider-control-plane|ready` with no producer behind it. That is the inverse
|
|
1028
|
+
of a stuck Pane: nothing looks wrong at all. The path now makes the same
|
|
1029
|
+
bounded wait as the loop head, and a binding that is provably gone leaves an
|
|
1030
|
+
`observer.stopped` record with result `stuck` and reason `binding-replaced`.
|
|
1031
|
+
That token is deliberately not `binding-revoked`: the four close-cause tokens
|
|
1032
|
+
answer "who closed the stream" and come from the broker epoch, and here the
|
|
1033
|
+
stream was never closed at all.
|
|
1034
|
+
- `test/e2e/codex-lifecycle.sh` (C01) pins the exact disconnect token at its
|
|
1035
|
+
disconnect projection barrier and at the reconnect-gap hook comparison. It
|
|
1036
|
+
waited on the literal `disconnected` before, which was the bucket published
|
|
1037
|
+
when the observer recorded no reason at all; killing the exact E1 proxy takes
|
|
1038
|
+
the upstream connection away, so the projection now reads
|
|
1039
|
+
`endpoint-suspended`. The barrier is still an exact string match, so a path
|
|
1040
|
+
that stops capturing its reason lands on a different token and fails it. That
|
|
1041
|
+
barrier also now gates on `@projmux_attention_state`: the projection writes
|
|
1042
|
+
its three pane options as three separate `set-option` calls, so releasing on
|
|
1043
|
+
the first two let the five-field assertion 46 lines later read a torn
|
|
1044
|
+
projection under load. The assertion is unchanged and still owns the verdict.
|
|
1045
|
+
- `TestManagedCodexAuthorityDoctorSeparatesFlappingFrozenAndStopped` owns the
|
|
1046
|
+
operator surface. The census carries a count-only reason distribution and
|
|
1047
|
+
`doctor` renders it, which is what tells flapping, frozen, and stopped
|
|
1048
|
+
apart; `TestManagedCodexAuthorityCensusSeparatesDeclaredFromUnexplainedFallback`
|
|
1049
|
+
keeps that distribution pinned alongside the source counts.
|
|
1050
|
+
|
|
1051
|
+
### Claude coordination endpoint Phase 1 tests
|
|
1052
|
+
|
|
1053
|
+
- `TestAgentRouteClaudeIdentityTransitions`, `TestClaudeRegistrationAdmissionAndCleanupCAS`,
|
|
1054
|
+
and the `TestAgentRoute` / `TestClaudeRegistration` model tests own the sealed
|
|
1055
|
+
provider authority union, existing Codex composite fence, exact Claude process
|
|
1056
|
+
and registration claims, same-UID rename preservation, and stale registration
|
|
1057
|
+
admission/cleanup replay. The property model retains historical routes and
|
|
1058
|
+
requires stale replay to leave current state unchanged.
|
|
1059
|
+
- `TestClaudeEndpoint` unit tests own private lease probes, kernel peer identity,
|
|
1060
|
+
socket owner/mode/inode checks, nested producer refusal, creator-selected
|
|
1061
|
+
activation context, read-only capability observation, and token/locator
|
|
1062
|
+
residue scans. Private readiness probes never connect to a provider inbox.
|
|
1063
|
+
- `test/integration/claude-endpoint-binding.sh`, included in
|
|
1064
|
+
`make test-integration`, builds the product and runs
|
|
1065
|
+
`TestClaudeEndpointProcessIntegration` through supervisor, activation gate,
|
|
1066
|
+
public SessionStart hook, and detached helper. It covers registration
|
|
1067
|
+
replacement, exact-version qualification without inheritance, two exact
|
|
1068
|
+
auth+frozen-user-frame pushes, provider exit cleanup, and zero token/locator
|
|
1069
|
+
residue in output and live lease files. The synthetic socket runs no model,
|
|
1070
|
+
account, connector, tool, MCP server, or external service.
|
|
1071
|
+
- `TestClaudeEndpointInstalledSourceGate` is opt-in evidence from a disposable
|
|
1072
|
+
installed Claude one-shot. It checks public SessionStart identity, exact
|
|
1073
|
+
readiness, exit invalidation, empty tools/MCP/plugins, and zero tool-use.
|
|
1074
|
+
Raw provider streams remain in memory. This is the Phase 1 registration source
|
|
1075
|
+
gate; it does not satisfy or relax Phase 4's separate exact-version,
|
|
1076
|
+
hook-enabled, long-lived provider qualification. A safe-mode one-shot cannot
|
|
1077
|
+
substitute because safe mode disables the hooks under test. Setup and limits are in
|
|
1078
|
+
[claude-coordination-endpoints.md](claude-coordination-endpoints.md).
|
|
1079
|
+
|
|
1080
|
+
### Claude coordination endpoint lifecycle tests
|
|
1081
|
+
|
|
1082
|
+
- `TestClaudeEndpoint*`, `TestClaudeCoordinationSocketReplacementInvalidatesWithoutRemovingReplacement`,
|
|
1083
|
+
and `TestCleanupClaudeActivationLeasesPreservesCoordinationReplacement` retain
|
|
1084
|
+
exact SessionStart registration, process/Pane/generation authority, private
|
|
1085
|
+
lease and coord-socket inode cleanup, helper-death invalidation, and
|
|
1086
|
+
same-path replacement preservation.
|
|
1087
|
+
- `TestClaudeEndpointProcessIntegration` uses a synthetic provider process and
|
|
1088
|
+
the real supervisor/helper entrypoints. It accepts exactly the auth line and
|
|
1089
|
+
one frozen user frame, completes exact Stop-marker qualification, pushes a
|
|
1090
|
+
durable broker record, replaces the registration without inheriting
|
|
1091
|
+
qualification, freshly qualifies again, and proves secret/output/file
|
|
1092
|
+
residue zero. It runs no model or network provider.
|
|
1093
|
+
- `TestClaudeCoordinationServerHasNoWaiterIngressOperations` refuses historical
|
|
1094
|
+
`wait`, `waiter-ready`, `begin-handoff`, and `receipt` operations. Integration
|
|
1095
|
+
tests require SessionStart registration plus short synchronous Stop reply and
|
|
1096
|
+
UserPromptSubmit boundary hooks, with zero `asyncRewake` ingress commands.
|
|
1097
|
+
|
|
1098
|
+
### Provider-neutral message broker Phase 3 tests
|
|
1099
|
+
|
|
1100
|
+
- `TestAgentCapabilityCatalogPinsCurrentGroupsAndDeferredVocabulary`,
|
|
1101
|
+
`TestAgentCapabilitiesClaudeMessageCellsReflectMissingRegistrationLease`, and
|
|
1102
|
+
`TestAgentCapabilityCatalogRoutesMatchExecutableHelpGraph` supersede the Phase
|
|
1103
|
+
0 placeholder boundary: `agent message send|wait|status` and `agent wait` are
|
|
1104
|
+
callable through one provider-neutral catalog, while Antigravity and Claude
|
|
1105
|
+
inbox claim remain explicit unsupported cells and exact Claude availability
|
|
1106
|
+
follows its current registration lease.
|
|
1107
|
+
- `TestAuthorityMatrixIsExhaustiveAndPeerNeverEscalates`,
|
|
1108
|
+
`TestEnvelopeRetryAndReplyCorrelationRandomizedProperties`, and
|
|
1109
|
+
`TestPublicReducerMatchesReferenceModelForRandomSequences` own the exhaustive
|
|
1110
|
+
authority table, immutable versioned envelope, exact reversed reply route and
|
|
1111
|
+
conversation, idempotent message reference, and terminal-once public reducer.
|
|
1112
|
+
Peer input remains untrusted coordination and cannot become a user turn,
|
|
1113
|
+
approval, configuration, tool, connector, or model-history write.
|
|
1114
|
+
- The `TestStore*` suite owns the private 0700/0600 store, advisory locking,
|
|
1115
|
+
atomic replace, bounded payload/file/record count, terminal retention,
|
|
1116
|
+
unknown-field refusal, concurrent claim, restart, timeout, and post-handoff
|
|
1117
|
+
`failed` plus unknown-outcome contract. Codex claim touches only the exact
|
|
1118
|
+
activation-scoped inbox and leaves unrelated Claude records unchanged.
|
|
1119
|
+
- `TestAgentMessageSendGatesBothStaticCellsAndRoutesBeforeStoreOrAdapter`,
|
|
1120
|
+
`TestAgentMessageUnsupportedProviderStopsBeforeRouteAndStore`,
|
|
1121
|
+
`TestAgentMessageSendSameReferenceRetryKeepsBrokerCorrelationAndDoesNotResend`,
|
|
1122
|
+
`TestAgentMessageConcurrentSameReferenceUsesOneConversation`, and
|
|
1123
|
+
`TestAgentMessageReplyReturnsOnlyToOriginalSourceConversation` pin exact
|
|
1124
|
+
current-Pane source authority, exact target eligibility before effects,
|
|
1125
|
+
same-envelope retry without resend, and broker-owned reply correlation.
|
|
1126
|
+
- `TestClaudePrivateProjectionPreservesPublicBoundaryAndAmbiguity`,
|
|
1127
|
+
`TestLiveClaudeAdapterMapsTerminalResponseKindsWithoutDelivery`,
|
|
1128
|
+
`TestLiveClaudeAdapterRefusesPublicValidEnvelopeThatExceedsPrivateFrame`, and
|
|
1129
|
+
`TestAgentMessageBrokerHasNoCodexOrUserTurnWritePath` own the Phase 2 Claude
|
|
1130
|
+
projection, transport-bound refusal, foreign receipt no-op, and zero Codex
|
|
1131
|
+
app-server, user-turn, raw-terminal, or model-history write path.
|
|
1132
|
+
- `TestAgentMessageWaitClaimsOnlyCurrentExactActivation`,
|
|
1133
|
+
`TestAgentMessageClaimDefaultOutputEscapesTerminalControlsAndKeepsEnvelope`,
|
|
1134
|
+
`TestAgentMessageWaitDetectsGenerationChangeBeforeClaim`, and
|
|
1135
|
+
`TestAgentWaitRegistryIdleReadyTimeoutAndStale` pin full-envelope self-claim,
|
|
1136
|
+
terminal-safe output, activation fencing, and deterministic Registry-only
|
|
1137
|
+
idle, timeout, and stale waits. Codex `delivered` ends at target self-claim;
|
|
1138
|
+
it does not assert model processing or reply.
|
|
1139
|
+
|
|
1140
|
+
### Heterogeneous Agent dialogue Phase 4 tests
|
|
1141
|
+
|
|
1142
|
+
- `TestClaudeCoordinationPrivateBridgeRequiresExactV5Route`,
|
|
1143
|
+
`TestClaudeCoordinationV1ThroughV4HelpersCannotReceiveV5Traffic`, and
|
|
1144
|
+
`TestHeterogeneousDialogueLifecycleUpgradeFenceMatrix` pin public/private v5
|
|
1145
|
+
route equality, old generation, same-generation old incarnation, stale
|
|
1146
|
+
provider process, foreign socket, helper exit, Codex endpoint replacement,
|
|
1147
|
+
and fresh current-version requalification. Old routes write/claim zero; a new
|
|
1148
|
+
endpoint writes zero before qualification and exactly one after it. The v4
|
|
1149
|
+
fence applies only to the existing coordination UDS protocol: v3 Stop-qualified
|
|
1150
|
+
helpers cannot pass current lease/eligibility checks. The frozen vendor
|
|
1151
|
+
auth-plus-user frame is unchanged; recovery retains the public same-UID path.
|
|
1152
|
+
- `TestHeterogeneousDialogueIdleActiveSafeBoundaryMatrix` and
|
|
1153
|
+
`TestClaudePushDuringOpenHumanTurnNeverCorrelatesThatTurnsStop` preserve
|
|
1154
|
+
immediate idle/active push without a waiter or interruption. Every ordinary,
|
|
1155
|
+
recursive, late, or unrelated Stop publishes zero replies.
|
|
1156
|
+
- `TestClaudeQualificationRequiresBrokerChallengeAndExplicitReply` requires a
|
|
1157
|
+
stored original challenge, exact current source and target, a full frozen
|
|
1158
|
+
frame, and a broker-committed explicit reply before opening general ingress.
|
|
1159
|
+
A text echo or Stop alone cannot qualify the endpoint.
|
|
1160
|
+
- `TestClaudeExplicitReplyRejectsForeignStaleAndAlteredCorrelationBeforeCommit`
|
|
1161
|
+
covers foreign refs, altered conversations, stale generations/incarnations,
|
|
1162
|
+
reversed-route mismatch, and a wrong qualification response with commit zero.
|
|
1163
|
+
- `TestClaudeExplicitMultipleRequestsAndHumanOverlapSelectOnlyNamedOriginal`
|
|
1164
|
+
permits an explicit reply to B then A across human activity, rejects a second
|
|
1165
|
+
reply to either request, and never chooses correlation by arrival order.
|
|
1166
|
+
- `TestClaudeQualificationPublishesOriginalBeforeConcurrentExplicitReply`
|
|
1167
|
+
synchronizes an immediate provider response with durable challenge publication;
|
|
1168
|
+
reply timing cannot expose a missing original.
|
|
1169
|
+
- `TestClaudeExplicitQualificationMissingToolProofOrOriginalWritesZero`,
|
|
1170
|
+
`TestClaudeExplicitQualificationPartialAndTimeoutNeverResend`, and
|
|
1171
|
+
`TestClaudeQualificationResponseRequiresExactStableClosedShape` pin exact
|
|
1172
|
+
version/process/lease evidence, the single Bash execution-gate assertion,
|
|
1173
|
+
missing original request, partial write, timeout, and no automatic resend.
|
|
1174
|
+
- `TestStoreMigratesV1EnvelopeToUnqualifiedStaleFence` uses literal v1 JSON
|
|
1175
|
+
without incarnation fields. `TestStoreReplyIsAtomicCorrelatedAndReplayIdempotent`
|
|
1176
|
+
pins reversed routes, original conversation/replyTo, crash-before-rename
|
|
1177
|
+
recovery, restart replay, and exactly one committed reply.
|
|
1178
|
+
- `TestClaudeBrokerStoreLayoutAndImmutableEnvelopeMismatchAreExact` and the mismatched
|
|
1179
|
+
same-reference case require the helper and public CLI to share one canonical
|
|
1180
|
+
message store and reject an immutable-envelope mismatch without mutation.
|
|
1181
|
+
`TestClaudePushNeverHoldsAndQualificationBrokerFencePrecedesProviderWrite`,
|
|
1182
|
+
`TestClaudePushWriteOutcomeAndDurableReceiptAreTerminalOnce`,
|
|
1183
|
+
`TestClaudePushRechecksDeadlineAfterDurableHandoffBeforeProviderWrite`,
|
|
1184
|
+
`TestClaudeProviderPushFinalRouteCheckPrecedesSoleWrite`,
|
|
1185
|
+
`TestLiveClaudeAdapterClosesMalformedSubmitResponsesAsAmbiguousTerminal`, and
|
|
1186
|
+
`TestClaudeCoordinationBrokerPersistenceFencesProviderWriteAndIsTerminalOnce`
|
|
1187
|
+
compose the persistence proof: durable handoff failure leaves provider bytes
|
|
1188
|
+
zero, durable delivered failure after a full write remains ambiguous and
|
|
1189
|
+
reply-ineligible, and a successful durable terminal repeats without another
|
|
1190
|
+
provider write.
|
|
1191
|
+
`TestAgentMessageLifecycleLeavesInteractionAndBadgeAuthorityUntouched`
|
|
1192
|
+
carries both in-progress and approval-required Registry projections through
|
|
1193
|
+
send/handoff/delivered/reply/claim, proves terminal-once, and proves Registry
|
|
1194
|
+
and badge writes remain zero.
|
|
1195
|
+
- `TestAIIntegrateClaude*` pins short compatibility Stop and
|
|
1196
|
+
`UserPromptSubmit` commands, zero SessionStart/Stop ingress waiter and zero
|
|
1197
|
+
`asyncRewake`, v1/v2-to-v3 marker convergence, user-hook preservation,
|
|
1198
|
+
remove, and idempotence.
|
|
1199
|
+
`TestClaudeAutomaticMigrationPreservesEveryCoordinationVersionUntilExplicitIntegration`
|
|
1200
|
+
and `TestObsoleteAndCurrentClaudeCoordinationHooksNeverRunAutomaticMigration`
|
|
1201
|
+
keep config apply/install and already-running historical hook children from
|
|
1202
|
+
rewriting global settings; only explicit integration converges v1/v2 to v3.
|
|
1203
|
+
- `TestAgentCapabilitiesClaudeMessageCellsReflectMissingRegistrationLease`,
|
|
1204
|
+
`TestProviderResumeExecutionPreservesExactAgentAndLaunchesOneExactArgv`, and
|
|
1205
|
+
the pre-revalidation rows of
|
|
1206
|
+
`TestHeterogeneousDialogueLifecycleUpgradeFenceMatrix` compose the rollout
|
|
1207
|
+
recovery proof: a missing lease exposes the exact integrate/normal-exit/resume
|
|
1208
|
+
action and permits delivery zero; Claude resume rematerializes one exact child
|
|
1209
|
+
while preserving Agent UID/name/count; and delivery remains zero until the
|
|
1210
|
+
new Ready lease and route incarnation are revalidated.
|
|
1211
|
+
- Required selectorless E2E scenario `L20` is the only deterministic full
|
|
1212
|
+
heterogeneous round trip. It uses isolated XDG/tmux roots, a payload-free
|
|
1213
|
+
long-lived fake Codex child plus typed `StageCodexEndpoint` /
|
|
1214
|
+
`BindCodexActivation` setup for one pre-existing synthetic route, and official
|
|
1215
|
+
Claude registration plus explicit public reply commands. A public capability barrier proves the exact Codex
|
|
1216
|
+
composite authority before send; semantic barriers replace sleeps. The
|
|
1217
|
+
scenario requires exact UID/Pane/generation/message/conversation/reply
|
|
1218
|
+
receipts, Agent count two, Codex provider writes zero, and pre-removal
|
|
1219
|
+
Registry/tmux/helper/socket/process residual checks. It does not require a
|
|
1220
|
+
provider binary, model, network, or version matrix.
|
|
1221
|
+
- The real-provider path is opt-in through `scripts/agent-dialogue-canary-setup.py`
|
|
1222
|
+
and `docs/heterogeneous-dialogue-canary.md`. It uses public reply-only activation,
|
|
1223
|
+
product observer/helper-memory evidence and separate qualification/idle claims.
|
|
1224
|
+
Actual model action is compared with the pinned guard and successful broker
|
|
1225
|
+
commit; delivered alone is insufficient. Partial setup and run failures share
|
|
1226
|
+
the owned pidfd writer barrier. Candidate and runner digests are pinned before
|
|
1227
|
+
launch, and success is published only after root removal. Active/human/recovery
|
|
1228
|
+
and installed cases remain distinct observations. Provider/version stress is
|
|
1229
|
+
separately opt-in and never joins required selectorless E2E.
|
|
1230
|
+
|
|
1231
|
+
### Provider hook pane identity tests
|
|
1232
|
+
|
|
1233
|
+
- `TestProviderHookCommandsCarryTheirOwnPaneIdentity` and
|
|
1234
|
+
`TestProviderHookPaneArgumentIsProviderNeutral` own the installed hook command
|
|
1235
|
+
string: every provider hook is handed the Pane it belongs to through the same
|
|
1236
|
+
argument, the argument reads the activation envelope rather than the tmux
|
|
1237
|
+
environment, and an app-server shared by several Panes expands it to an empty
|
|
1238
|
+
value instead of breaking the hook.
|
|
1239
|
+
- `TestCodexIntegrationStillOwnsThePaneBlindHookCommand` and
|
|
1240
|
+
`TestCodexIntegrationConvergesThePaneBlindHookCommandForward` own the
|
|
1241
|
+
convergence of an already installed pane-blind command: it stays recognized as
|
|
1242
|
+
projmux-authored and is rewritten forward instead of being refused as
|
|
1243
|
+
hand-edited wiring.
|
|
1244
|
+
- `TestMatchAIPaneResolvesTheHandedPaneWithoutTmuxEnvironment` and
|
|
1245
|
+
`TestMatchAIPanePrefersTheHandedPaneOverInheritedEvidence` own attribution
|
|
1246
|
+
when the hook was handed an identity: the Registry resolves it with no tmux
|
|
1247
|
+
server read at all, and it wins over inherited `TMUX_PANE`, cwd, and thread
|
|
1248
|
+
evidence.
|
|
1249
|
+
- `TestMatchAIPaneKeepsTheEstablishedFallbackWhenNothingWasHandedOver` owns the
|
|
1250
|
+
unchanged three-step ladder for hooks that were handed nothing.
|
|
1251
|
+
- `TestMatchAIPaneRefusesAHandedPaneThatIsGone` owns the four ways a handed
|
|
1252
|
+
identity can be stale and proves none of them redirects the event onto another
|
|
1253
|
+
live Pane.
|
|
1254
|
+
- `TestAttributionFailureReasonsAreDistinctAndClosed` owns the failure
|
|
1255
|
+
vocabulary: an unreachable Pane inventory, a genuine no-match, an unreachable
|
|
1256
|
+
Registry, an unregistered Pane, a conversation no Pane holds, and a
|
|
1257
|
+
conversation two Panes hold each name themselves, and every token stays a
|
|
1258
|
+
bounded phrase.
|
|
1259
|
+
- `TestHookIngestRoutesAcceptTheExplicitPaneArgument` owns the route contract:
|
|
1260
|
+
`--pane` is optional, an empty value is a valid answer, and a positional
|
|
1261
|
+
payload argument is still refused.
|
|
1262
|
+
|
|
1263
|
+
### Shared-host hook attribution tests
|
|
1264
|
+
|
|
1265
|
+
A provider host shared by several Panes has no Pane to hand over and no tmux
|
|
1266
|
+
environment to inherit, so both the explicit argument and the three-step ladder
|
|
1267
|
+
above come up empty for it. These tests own the one further step that reads the
|
|
1268
|
+
conversation the Registry already records.
|
|
1269
|
+
|
|
1270
|
+
- `TestMatchAIPaneAttributesASharedHostHookThroughTheRegistry` owns the normal
|
|
1271
|
+
path: with nothing handed over and no readable Pane inventory, the thread or
|
|
1272
|
+
session the payload carries resolves to the live runtime handle of the Pane
|
|
1273
|
+
that holds that conversation.
|
|
1274
|
+
- `TestMatchAIPaneRefusesAConversationNoPaneHolds` and
|
|
1275
|
+
`TestMatchAIPaneRefusesAConversationTwoPanesHold` own the refusals: an
|
|
1276
|
+
unclaimed conversation and an ambiguous one both fail by name rather than
|
|
1277
|
+
landing on a neighbouring Pane that merely shares a working directory.
|
|
1278
|
+
- `TestRegistryAttributionRequiresAnIntactBinding` owns the round trip. A Pane
|
|
1279
|
+
that is no longer materialized, one whose Agent no longer points back at it,
|
|
1280
|
+
and one whose Agent is gone are all torn state, never attribution targets.
|
|
1281
|
+
- `TestRegistryAttributionIsProviderNeutral` owns the shape of the lookup: it
|
|
1282
|
+
reads the resource model, so every provider whose conversation the Registry
|
|
1283
|
+
records resolves through the same code and none is named in it.
|
|
1284
|
+
- `TestMatchAIPaneKeepsTheRegistryStepBehindTheEstablishedLadder` and
|
|
1285
|
+
`TestMatchAIPaneLeavesTheLadderReasonWhenThereIsNoConversation` own the order.
|
|
1286
|
+
A handed identity, inherited `TMUX_PANE`, and the Pane inventory each still
|
|
1287
|
+
answer first, and a payload carrying no conversation keeps the ladder's own
|
|
1288
|
+
failure reason instead of being relabelled.
|
|
1289
|
+
- `TestRegistryAttributionReportsAnUnreachableRegistry` and
|
|
1290
|
+
`TestRegistryAttributionDefinesNoBinding` own the boundary: the step reads the
|
|
1291
|
+
Registry exactly once, writes nothing, defines no binding, and reports an
|
|
1292
|
+
unreachable Registry as its own failure.
|
|
1293
|
+
- `TestCodexHookIngestAttributesASharedHostEventThroughTheRegistry` owns the
|
|
1294
|
+
whole route rather than the matcher alone: a payload arriving with an empty
|
|
1295
|
+
`--pane` produces a record naming its own Pane, and an unclaimed conversation
|
|
1296
|
+
produces a record naming why it has none.
|
|
1297
|
+
|
|
1298
|
+
### Hook reflection write tests
|
|
1299
|
+
|
|
1300
|
+
Attribution and reflection are two layers. A hook that found its Pane still has
|
|
1301
|
+
to write the event onto it, and every one of those tmux writes used to discard
|
|
1302
|
+
its error — 47 of them across `internal/app/ai.go` and
|
|
1303
|
+
`internal/app/ai_ingest.go`. A hook whose route to tmux was broken therefore
|
|
1304
|
+
produced a `result:"state"` record: the log reported a delivery that never
|
|
1305
|
+
happened, which is a stronger failure than an unattributed hook, because an
|
|
1306
|
+
unattributed hook at least says so.
|
|
1307
|
+
|
|
1308
|
+
These tests own the reporting, not the repair. Retry, recovery, and the route
|
|
1309
|
+
decision itself are separate concerns.
|
|
1310
|
+
|
|
1311
|
+
- `TestHookRecordRefusesToReportADeliveryThePaneWritesMissed` owns the record in
|
|
1312
|
+
the three shapes an operator meets: nothing lands, only markers were ever
|
|
1313
|
+
written, and — the live one — the reflection lands through a working route
|
|
1314
|
+
while the markers written without it do not. All three end as `error` with the
|
|
1315
|
+
bounded reason that names which kind of write failed, and the Pane they were
|
|
1316
|
+
attributed to stays in the record.
|
|
1317
|
+
- `TestHookRecordIsUnchangedWhenThePaneWritesLand` owns the normal path. Same
|
|
1318
|
+
result word, same empty reason, and the same number of attempted writes as the
|
|
1319
|
+
broken route — the writes stay best-effort and the sequence of attempts did
|
|
1320
|
+
not change.
|
|
1321
|
+
- `TestPaneWriteFailureReasonIsAClosedTokenCarryingNoOpaqueValue` owns the
|
|
1322
|
+
vocabulary. There are two tokens and the split is structural — the reflection
|
|
1323
|
+
of the event, whose failure its caller reports, and the markers laid down
|
|
1324
|
+
alongside it, whose callers have no error channel — because those two really
|
|
1325
|
+
do fail apart. tmux answers a rejected write with an exit status and a socket
|
|
1326
|
+
path; the classification keeps both out of either token, and the log file is
|
|
1327
|
+
checked for the fragments directly.
|
|
1328
|
+
- `TestObserverRecordsAreNotColouredByAReflectionWriteFailure` owns the
|
|
1329
|
+
boundary. The observer journal writes into the same file from a process that
|
|
1330
|
+
lives for hours, and its records never inherit a failure this process saw on
|
|
1331
|
+
another path.
|
|
1332
|
+
- `TestHookIngestSourcesNeverDiscardAReflectionWriteError` is the machine check
|
|
1333
|
+
that keeps the fix. It parses `ai.go`, `ai_ingest.go`, and `ai_pane_write.go`
|
|
1334
|
+
and fails on any tmux write whose error is dropped, because that defect is
|
|
1335
|
+
invisible by construction: the record it produces claims the hook succeeded.
|
|
1336
|
+
Its scan list is those three sources and its name says so; the package-wide
|
|
1337
|
+
equivalent belongs to the recurrence gate, so green here is not a claim about
|
|
1338
|
+
any other file.
|
|
1339
|
+
|
|
1340
|
+
### Settled Codex authority admission tests
|
|
1341
|
+
|
|
1342
|
+
`aiCodexLifecycleSink.SetAuthority` publishes one native authority transition as
|
|
1343
|
+
three separate tmux writes — `@projmux_codex_authority`, then
|
|
1344
|
+
`@projmux_codex_authority_epoch`, then `@projmux_codex_authority_reason` — under
|
|
1345
|
+
a per-Pane kernel fence. That fence serializes writers against each other only.
|
|
1346
|
+
Turn and approval admission reads the same three options with one
|
|
1347
|
+
`display-message`, so a read that lands between two of those writes can see an
|
|
1348
|
+
authority that is already `provider-control-plane` paired with the epoch or
|
|
1349
|
+
reason of the transition being replaced, and refuse the turn for an unavailable
|
|
1350
|
+
epoch while the native connection is alive.
|
|
1351
|
+
|
|
1352
|
+
Admission now takes that same fence for the live read, so the triple it judges
|
|
1353
|
+
is one completed transition:
|
|
1354
|
+
|
|
1355
|
+
- `TestSettledCodexAuthorityAdmissionIgnoresTornAuthorityWrites` owns the
|
|
1356
|
+
mid-write-to-verdict mapping. Each row injects the triple that exists while
|
|
1357
|
+
the writer still holds the fence, records whether judging that triple directly
|
|
1358
|
+
would have refused a live connection, and then asserts the verdict reached
|
|
1359
|
+
from the settled triple. A control plane that is genuinely lost — the provider
|
|
1360
|
+
hook, a pending connection, an invalidation with no epoch — still refuses, and
|
|
1361
|
+
the refusal still names the observer's reason.
|
|
1362
|
+
- `TestCodexAuthorityAdmissionTakesTheExactWriterFence` owns the fence identity.
|
|
1363
|
+
The reader derives the same lock file as the writer, waits while a transition
|
|
1364
|
+
is in flight, holds it exclusively while it samples, and gives the wait up at
|
|
1365
|
+
the control deadline rather than stalling `projmux agent turn` behind a wedged
|
|
1366
|
+
transition.
|
|
1367
|
+
- `TestConcurrentCodexAuthorityTransitionsNeverExposeATornAdmissionSnapshot`
|
|
1368
|
+
owns the race. A transition writer and the real admission path run
|
|
1369
|
+
concurrently, and every snapshot admission read must be one of the complete
|
|
1370
|
+
triples the writer published; a control-plane authority with no epoch is the
|
|
1371
|
+
false refusal this closes.
|
|
1372
|
+
|
|
1373
|
+
The fence covers the live read only, not the judgment that follows it. A
|
|
1374
|
+
transition that lands after admission returns is not prevented: the control
|
|
1375
|
+
epoch still owns provider-side fencing, and `revalidateControlConsumerFence`
|
|
1376
|
+
still re-checks the Registry half immediately before the transport call.
|
|
1377
|
+
Admission also does not reduce how often the native connection drops, and it
|
|
1378
|
+
says nothing about the `@projmux_ai_state` / `@projmux_ai_badge_kind` /
|
|
1379
|
+
`@projmux_attention_state` triple, which has no fence of its own.
|
|
1380
|
+
|
|
1381
|
+
### Inherited pane identity refusal tests
|
|
1382
|
+
|
|
1383
|
+
A provider host shared by several Panes inherits the activation envelope of
|
|
1384
|
+
whichever Pane happened to launch it, tmux environment included. The `--pane`
|
|
1385
|
+
argument therefore arrives holding a live, perfectly resolvable Pane that
|
|
1386
|
+
belongs to a different provider, and resolving it verbatim attributes the event
|
|
1387
|
+
to that Pane. Having an explicit value and owning it are different things; only
|
|
1388
|
+
the Registry, which already records which provider an Agent runs, can tell them
|
|
1389
|
+
apart.
|
|
1390
|
+
|
|
1391
|
+
- `TestResolveExplicitAIPaneRefusesAnInheritedForeignIdentity` owns the decision
|
|
1392
|
+
itself. Both spellings of the handed identity are refused whenever the
|
|
1393
|
+
Registry positively records a different provider for that Pane, in every
|
|
1394
|
+
direction between the catalogued providers, so the rule is symmetric rather
|
|
1395
|
+
than a guard around one of them.
|
|
1396
|
+
- `TestMatchAIPaneNeverAttributesAnInheritedForeignIdentity` owns the same
|
|
1397
|
+
fixture through the whole matcher: with nothing else able to answer, a refused
|
|
1398
|
+
inherited identity ends as a named failure and never as the foreign Pane.
|
|
1399
|
+
- `TestMatchAIPaneKeepsAnExplicitPaneWithNoRecordedProvider` owns the silence
|
|
1400
|
+
case. A Pane the Registry records no provider for — an unbound shell where
|
|
1401
|
+
somebody ran a provider by hand — is not a contradiction, and neither is an
|
|
1402
|
+
unrecognized spelling on either side; all of them resolve exactly as before.
|
|
1403
|
+
- `TestMatchAIPaneContinuesPastARefusedInheritedIdentity` owns the priority
|
|
1404
|
+
ladder behind the refusal: the Registry conversation record answers first, the
|
|
1405
|
+
established inventory ladder answers behind it, and only a fall-through that
|
|
1406
|
+
also fails ends as a failure.
|
|
1407
|
+
- `TestForeignExplicitFallThroughIgnoresTheInheritedEnvironment` owns the one
|
|
1408
|
+
step the fall-through deliberately skips. `TMUX_PANE` arrives in the same
|
|
1409
|
+
envelope whose Pane identity was just refused, so honouring it would
|
|
1410
|
+
reintroduce the misattribution through a second door; the path that was handed
|
|
1411
|
+
nothing keeps that step exactly as before.
|
|
1412
|
+
- `TestForeignExplicitRefusalLeavesTheRegistryPathUnchanged` is the negative
|
|
1413
|
+
audit. The stage writes nothing and defines no binding, and the Registry
|
|
1414
|
+
conversation index returns the same map whether or not an Agent records a
|
|
1415
|
+
provider, so the shared-host attribution path above is unchanged.
|
|
1416
|
+
- `TestAttributionFailureReasonsAreDistinctAndClosed` additionally owns the two
|
|
1417
|
+
new tokens: the refusal itself, and the composite the matcher reports once the
|
|
1418
|
+
fall-through has also failed. The composite is its own entry rather than the
|
|
1419
|
+
downstream step's reason, so a record still says the hook was handed somebody
|
|
1420
|
+
else's Pane. Every token stays provider-neutral in wording.
|
|
1421
|
+
|
|
1422
|
+
The fall-through has a second door of its own. Its inventory step matches on
|
|
1423
|
+
working directory alone, and where every provider Pane sits in the same
|
|
1424
|
+
repository that step would hand the event straight back to a Pane of the very
|
|
1425
|
+
provider just refused. Phase 8 is what routes a foreign-refused hook into that
|
|
1426
|
+
ladder at all, so the coherence check is applied once more to whatever the
|
|
1427
|
+
fall-through resolves — to the answer, never inside the shared ladder.
|
|
1428
|
+
|
|
1429
|
+
- `TestForeignExplicitFallThroughDoesNotLandOnAnotherProvidersPane` owns that
|
|
1430
|
+
door in both directions: a cwd-sharing Pane of the refused provider is not
|
|
1431
|
+
taken, and the attempt ends with its own reason rather than with that Pane.
|
|
1432
|
+
- `TestForeignExplicitFallThroughStillTakesACoherentLadderAnswer` owns the
|
|
1433
|
+
fail-open direction that keeps the Phase 2 contract intact: a ladder answer
|
|
1434
|
+
running the hook's own provider, one the Registry records no provider for, and
|
|
1435
|
+
one recording an unrecognized spelling are all still taken.
|
|
1436
|
+
- `TestForeignExplicitFallThroughDoesNotTakeAForeignConversationPane` owns the
|
|
1437
|
+
same check on the Registry conversation record, and proves refusing it does
|
|
1438
|
+
not consume the inventory ladder's turn behind it.
|
|
1439
|
+
- `TestNoExplicitPathStillTakesACwdMatchRegardlessOfProvider` is the proof that
|
|
1440
|
+
the shared ladder is untouched: with no explicit value there is no refused
|
|
1441
|
+
envelope and therefore no coherence question, and the cwd and thread steps
|
|
1442
|
+
resolve exactly as before for every provider.
|
|
1443
|
+
### Codex control-plane regression-detection gate tests
|
|
1444
|
+
|
|
1445
|
+
Three control-plane defects lived through a neighbouring eight-phase track
|
|
1446
|
+
closing green from end to end. Nothing reached those surfaces, and the one test
|
|
1447
|
+
that did — `test/e2e/codex-lifecycle.sh` — asserted the literal `disconnected`
|
|
1448
|
+
for a managed Codex Pane's authority reason. `disconnected` is the bucket that
|
|
1449
|
+
means no reason was captured, so capturing a real one would have turned the job
|
|
1450
|
+
red. Shipping the fixes without this gate would leave the same blind spot under
|
|
1451
|
+
new code.
|
|
1452
|
+
|
|
1453
|
+
The gate has three parts.
|
|
1454
|
+
|
|
1455
|
+
- `TestControlPlaneTestsNeverExpectAnUncapturedReason` sweeps every
|
|
1456
|
+
`internal/**/*_test.go` and `test/e2e/*.sh` for values that mean "nothing was
|
|
1457
|
+
recorded" — `unrecorded`, the pre-instrumentation `disconnected` literal, the
|
|
1458
|
+
`bounded reason unavailable` read fallback, and the `target-unmatched`
|
|
1459
|
+
operations bucket — and fails on any that is an expectation rather than an
|
|
1460
|
+
input. A survivor is admitted only by an `uncaptured-default: <reason>`
|
|
1461
|
+
comment on its line or in the comment block above it; every current survivor
|
|
1462
|
+
is a different vocabulary that happens to spell a token the same way.
|
|
1463
|
+
- `TestControlPlaneContractCellsNameLiveTests` resolves every test named in
|
|
1464
|
+
`codexControlPlaneContractEnforcement` against the declarations in the tree.
|
|
1465
|
+
Squash merges erase the branch that tied a fix to the test holding it, so a
|
|
1466
|
+
contract cell naming a renamed test reads as enforced while enforcing nothing.
|
|
1467
|
+
- `TestControlPlaneContractEnforcementCoversEverySurface` holds the two
|
|
1468
|
+
directions of that map closed: every rendered surface names a contract, and
|
|
1469
|
+
every contract in the map names a surface the diagnosis renders.
|
|
1470
|
+
|
|
1471
|
+
`projmux doctor` gained the operator half under **Codex control-plane
|
|
1472
|
+
surfaces**. Seven verdicts, one per surface, each with the counters it was
|
|
1473
|
+
reached on:
|
|
1474
|
+
|
|
1475
|
+
- `TestCodexBrokerDiagnosticsSurfaceNamesThePublishedButUnreachableRuntime`
|
|
1476
|
+
separates a domain that published nothing — the ordinary resting state — from
|
|
1477
|
+
one whose published endpoint answered nothing, which used to render as
|
|
1478
|
+
`absent` and was read as "no broker is running".
|
|
1479
|
+
- `TestHookAttributionSurfaceSeparatesTotalFailureFromPartial` keeps "no hook
|
|
1480
|
+
reached its Pane" apart from "a few did not". `TestAttributionHealth*` own the
|
|
1481
|
+
bounded tail read of `ai-ingest.log` behind it, including that an unreadable
|
|
1482
|
+
log is reported as unobserved rather than as zero failures.
|
|
1483
|
+
- `TestObserverReasonSurfaceRefusesToCallAnUncapturedReasonHealthy` is the
|
|
1484
|
+
product-side half of the sweep: a reason distribution made entirely of
|
|
1485
|
+
uncaptured tokens is never a healthy row.
|
|
1486
|
+
- `TestConnectionContinuitySurfaceReportsCumulativeReconnects` reports the count
|
|
1487
|
+
as the total it is and derives no rate from a single sample.
|
|
1488
|
+
- `TestTurnAdmissionSurfaceReportsATornAuthoritySnapshotAsBroken`, with
|
|
1489
|
+
`TestSettledAuthorityObservationNamesATornTripleOnlyUnderTheFence` and
|
|
1490
|
+
`TestAuthorityFenceObservation*`, owns the C-5 read. Doctor samples each
|
|
1491
|
+
Pane's authority triple under a **shared, non-creating** hold of the writer's
|
|
1492
|
+
own fence, so a torn pairing is a producer fact rather than a transition
|
|
1493
|
+
caught in flight. A Pane with no fence file is reported `unfenced`, separately
|
|
1494
|
+
from torn: its writer predates the fence, which is a deployment answer.
|
|
1495
|
+
|
|
1496
|
+
- `TestHookDeliverySurfaceTreatsAnOpaqueFailureAsBroken` owns the layer after
|
|
1497
|
+
attribution. A hook that found its Pane has not yet changed it, and the write
|
|
1498
|
+
that follows can fail on its own. A failure naming its cause is a fault an
|
|
1499
|
+
operator acts on; one reporting only `exit status 1` says a process ended and
|
|
1500
|
+
nothing else. `TestOpaqueDeliveryReasonsNeverReachTheDiagnosis` pins both
|
|
1501
|
+
halves of that: the raw string is counted rather than repeated, and a reason
|
|
1502
|
+
carrying a path is opaque for the second reason that it is a leak.
|
|
1503
|
+
- `TestPaneOwnershipSurfaceReportsAForeignAttributionAsBroken` owns the failure
|
|
1504
|
+
that looks like success from every other angle — the event was attributed, the
|
|
1505
|
+
write landed, and the Pane it moved belongs to another provider.
|
|
1506
|
+
`TestOwnershipHealthCallsOnlyAPositivelyForeignPaneForeign` pins the predicate
|
|
1507
|
+
as one-sided and provider-neutral: an attribution is foreign only when the
|
|
1508
|
+
Registry positively records a *different* provider, so a Pane recording none
|
|
1509
|
+
is not a finding, and the judgment is a table rather than a branch.
|
|
1510
|
+
|
|
1511
|
+
Two things the attribution verdict counts, and one it does not.
|
|
1512
|
+
|
|
1513
|
+
The section carries the record span its hook rows were counted over. Every
|
|
1514
|
+
hook-layer number is cumulative across the whole reading window, and a
|
|
1515
|
+
deployment inside that window leaves records from both binaries in the same
|
|
1516
|
+
counts — so a repair that lands mid-window moves nothing until its predecessors
|
|
1517
|
+
age out, and a reader without the span reads the delay as the repair having
|
|
1518
|
+
failed and the eventual drop as time having healed it. Splitting the counts at
|
|
1519
|
+
a deployment boundary would need something this reader does not have: the hook
|
|
1520
|
+
layer is a short-lived process re-resolved from PATH on every firing, so the
|
|
1521
|
+
binary vintage of a long-running process says nothing about which image wrote a
|
|
1522
|
+
given record. That is also why the vintage line above qualifies the broker and
|
|
1523
|
+
observer rows and not these three.
|
|
1524
|
+
|
|
1525
|
+
It is reached **per source** rather than over the total. The defect it detects
|
|
1526
|
+
was one provider's, and a busy neighbour attributing everything would carry the
|
|
1527
|
+
aggregate and render the dead provider as a few stale panes — which is the
|
|
1528
|
+
reading that let it live for eight phases.
|
|
1529
|
+
|
|
1530
|
+
It counts only events the contract owed a Pane. `aiPaneMatchRefusals` names the
|
|
1531
|
+
answers that decline something the contract never promised — a conversation or
|
|
1532
|
+
an explicit Pane that no longer exists — and those are reported beside the
|
|
1533
|
+
failure count, never inside it.
|
|
1534
|
+
`TestAttributionHealthSeparatesAContractualRefusalFromAFailure` pins the split.
|
|
1535
|
+
A retired conversation whose thread keeps firing hooks is the mechanism working;
|
|
1536
|
+
reporting it as breakage made `doctor` call a machine behaving to spec broken,
|
|
1537
|
+
and a gate that cries wolf gets switched off — which is how the defects this
|
|
1538
|
+
section exists to catch survived next door. What stays a failure is the
|
|
1539
|
+
mechanism not answering: an unreadable inventory or Registry, or the ladder
|
|
1540
|
+
running over readable data and finding nothing, which is the shape a re-broken
|
|
1541
|
+
hook identity would take.
|
|
1542
|
+
|
|
1543
|
+
`TestEverySurfaceDeclaresWhatItsNumbersAreOver` and
|
|
1544
|
+
`TestEverySurfaceRendersTheScopeItDeclares` fold four separate corrections into
|
|
1545
|
+
one rule. Each of the four was the same mistake wearing a different face: a
|
|
1546
|
+
refusal the contract never promised counted among the failures it did; a judged
|
|
1547
|
+
count standing without its population; a lane that writes nothing sitting in a
|
|
1548
|
+
write rate's denominator; records from two binaries counted in one window. Every
|
|
1549
|
+
number was arithmetically right and none said what it ranged over, so a reader
|
|
1550
|
+
supplied the wrong range — and all four were found by a person, none by a test.
|
|
1551
|
+
A surface must now declare the phrase its rendering carries to answer "over
|
|
1552
|
+
what", and adding one without that answer fails the completeness check. The
|
|
1553
|
+
rule cannot judge whether a scope is *correct*; it makes stating one
|
|
1554
|
+
unavoidable, which is what none of the four had to do.
|
|
1555
|
+
|
|
1556
|
+
`TestHookReflectionWritesNeverDiscardTheirErrorSilently` covers what none of the
|
|
1557
|
+
log-derived verdicts can. A reflection path that runs a tmux write and throws
|
|
1558
|
+
the error away records `result:"state"` for an event whose Pane never moved, so
|
|
1559
|
+
the log itself reports success and every diagnosis built on it inherits the lie.
|
|
1560
|
+
It is the strongest form of the failure this whole section exists to catch:
|
|
1561
|
+
`disconnected` and `no matching pane` were at least wrong out loud.
|
|
1562
|
+
|
|
1563
|
+
The gate is zero within the hook reflection path, and it carries no baseline.
|
|
1564
|
+
Recording the current count as an allowance was considered and rejected:
|
|
1565
|
+
pinning today's state as the passing condition is the exact shape of the E2E
|
|
1566
|
+
assertion that made a silent control plane the condition for green, and
|
|
1567
|
+
building that trap inside the gate written to stop it is not a tradeoff worth
|
|
1568
|
+
making. An exception is admitted by a stated `best-effort-write: <reason>`
|
|
1569
|
+
comment, never by a count — the difference being that a reason is a judgment a
|
|
1570
|
+
reviewer can dispute, while a number just defers on volume. Three writes carry
|
|
1571
|
+
one today: two cosmetic (a pane title, a one-shot message on the operator's own
|
|
1572
|
+
screen) and one global cleanup marker whose failure simply causes a retry. None
|
|
1573
|
+
of the three can make a surface report a state a Pane does not hold.
|
|
1574
|
+
|
|
1575
|
+
Three things about the scan itself. It covers the whole package rather than a
|
|
1576
|
+
named list of files, so a discarded write appearing somewhere new is caught
|
|
1577
|
+
rather than skipped for being somewhere nobody thought to look. It counts a
|
|
1578
|
+
bare call statement as well as an assignment to blank identifiers — that second
|
|
1579
|
+
form appears nowhere in the tree today and is covered as prevention, so that
|
|
1580
|
+
deleting the assignment rather than checking the error does not quiet the gate.
|
|
1581
|
+
And it reads the syntax tree, not the text: a textual version counted the pattern inside the
|
|
1582
|
+
comment explaining why a checked helper replaced it, so the sentence describing
|
|
1583
|
+
the fix registered as the defect.
|
|
1584
|
+
`TestDiscardedWriteScanNeverCountsProseAboutCode` pins that, because a gate
|
|
1585
|
+
which cannot tell code from prose about code is one people route around.
|
|
1586
|
+
|
|
1587
|
+
The `pane-ownership` foreign count is broken down by direction for a related
|
|
1588
|
+
reason. A hook landing on the Pane that launched its host is the identity leak
|
|
1589
|
+
the contract closes; a provider's events arriving under another provider's
|
|
1590
|
+
source were misrouted before attribution ever ran, which a stale hook config on
|
|
1591
|
+
the machine can produce and no code change here would fix. One number would let
|
|
1592
|
+
either be mistaken for the other.
|
|
1593
|
+
|
|
1594
|
+
The section leads with a deployment-vintage line, and
|
|
1595
|
+
`TestControlPlaneVintage*` owns it. `make install` replaces the executable on
|
|
1596
|
+
disk and never replaces the image of a running process, so a green surface read
|
|
1597
|
+
from a replaced-image process describes code that process never loaded. This
|
|
1598
|
+
track lost two acceptance criteria to that twice. On Linux the diagnosis
|
|
1599
|
+
resolves `/proc/<pid>/exe` for the broker runtime and the lifecycle observers
|
|
1600
|
+
and reports how many run a replaced image; on a platform with no such table it
|
|
1601
|
+
reports the answer as unknown rather than claiming currency.
|
|
1602
|
+
|
|
1603
|
+
That line answers which image the Codex verdicts under it were read from, so it
|
|
1604
|
+
counts only those two roles. It is not the fleet answer, and reading it as one
|
|
1605
|
+
understated the fleet by a factor of five: on 2026-09-05 it named six processes
|
|
1606
|
+
while thirty-two children of the same executable were live, eighteen of them
|
|
1607
|
+
per-pane `internal supervise` supervisors that matched no named route and were
|
|
1608
|
+
dropped at a map lookup. `projmux doctor` therefore carries a second census
|
|
1609
|
+
under `Runtime`, rendered as `Projmux process vintage` and owned by
|
|
1610
|
+
`TestProjmuxProcessVintage*`, `TestProjmuxProcessRole*` and
|
|
1611
|
+
`TestDoctorSeparatesTheCodexDiagnosisVintageFromTheFleetCensus`.
|
|
1612
|
+
|
|
1613
|
+
- It is provider-neutral, which is why it is not on the Codex line. A
|
|
1614
|
+
supervisor supervises a Claude pane exactly as it supervises a Codex one, and
|
|
1615
|
+
`TestCodexControlPlaneVintageIgnoresProviderNeutralProcesses` keeps it off the
|
|
1616
|
+
line that qualifies Codex verdicts.
|
|
1617
|
+
- It has no unmatched case. A child whose route this reader cannot name is
|
|
1618
|
+
counted under `other` rather than dropped, so an unknown route can never make
|
|
1619
|
+
the fleet look smaller than it is.
|
|
1620
|
+
- Route words are argv without argv[0] and without anything behind a bare `--`.
|
|
1621
|
+
`projmux agent turn ... -- <text>` carries arbitrary prose, including the very
|
|
1622
|
+
route words this census matches on, and counting a message about the broker as
|
|
1623
|
+
a broker is the same class of error in the other direction.
|
|
1624
|
+
- Both censuses come from one process-table read, so the two lines can never
|
|
1625
|
+
disagree about the same process, and both answer `unknown` where the platform
|
|
1626
|
+
exposes no table.
|
|
1627
|
+
- Neither line names a process. `TestProcessVintageNamesNoProcess` audits both
|
|
1628
|
+
rendered strings for pids, paths and argv; the section is counts and nothing
|
|
1629
|
+
else, which is what makes a process census printable at all.
|
|
1630
|
+
|
|
1631
|
+
The gate does not change `projmux doctor`'s exit code. A broken surface is a
|
|
1632
|
+
readable verdict, not a failed command, and the CI half of the detection is the
|
|
1633
|
+
regression tests themselves.
|
|
1634
|
+
|
|
1635
|
+
### Shared AI pane option routing tests
|
|
1636
|
+
|
|
1637
|
+
`TestSharedPaneRoutingCodexIngest` drives the marker (`PreToolUse`) and ordinary
|
|
1638
|
+
state (`UserPromptSubmit`) caller paths through full Codex ingest. Both require
|
|
1639
|
+
writes on the verified app route and honest records; removing the shared argv
|
|
1640
|
+
prefix makes both fail. `TestSharedPaneRoutingSetClearRecord` pins exact inherited
|
|
1641
|
+
`-S` and detached app `-L` argv. `TestSharedPaneRoutingDenialIsBoundedAndWritesNothing`
|
|
1642
|
+
rejects unreachable servers, missing panes, foreign servers and absent resource
|
|
1643
|
+
identity without writes or success records. Existing Codex delivery authority,
|
|
1644
|
+
semantic, refusal-vocabulary and diagnostics gates remain in force.
|
|
1645
|
+
|
|
1646
|
+
`TestSharedPaneRoutingProviderConsumers` covers Codex, Claude, Antigravity and
|
|
1647
|
+
bell hook consumers, including state and resume markers.
|
|
1648
|
+
`TestSharedPaneRoutingLaunchBeforeFirstMarker` checks canonical detached
|
|
1649
|
+
materialization through its supplied exact runner before any managed marker or
|
|
1650
|
+
Pane UID exists. Existing topic/status semantic fixtures retain
|
|
1651
|
+
their option expectations while their mock server supplies explicit containment.
|
|
1652
|
+
`TestManagedProjectionRoutingCallers` enters managed interaction through Claude
|
|
1653
|
+
`UserPromptSubmit` hook ingest and managed topic through the internal
|
|
1654
|
+
`aiCommand.Run` compatibility set/clear path. Its internal status-idle companion
|
|
1655
|
+
covers badge/attention unset; hooks do not emit idle. Caller result, committed
|
|
1656
|
+
Registry state, exact route argv, target values and actual unset are asserted
|
|
1657
|
+
together. `TestManagedProjectionRoutingFailureKeepsCommit` refuses disappeared,
|
|
1658
|
+
denied and foreign routes after commit, with no fallback writes, and retains the
|
|
1659
|
+
existing committed-mirror error and first-write-failure behavior.
|
|
1660
|
+
|
|
1661
|
+
`TestManagedProjectionRoutingRealTmux` repeats these managed caller cases on two
|
|
1662
|
+
isolated real servers with identical pane IDs and opposite-server sentinels.
|
|
1663
|
+
Detached attribution reads use the fixture's exact route; projection writes run
|
|
1664
|
+
unaltered. Baseline detached raw projections and a shared-prefix-removal mutant
|
|
1665
|
+
both fail the target assertions. Inherited raw baseline writes can already work
|
|
1666
|
+
through the tmux environment; the argv fixture additionally pins explicit routing.
|
|
1667
|
+
An optional `PROJMUX_TEST_MANAGED_ROUTE_BINARY=<absolute binary>` executes
|
|
1668
|
+
the reachable hook case and a separate canonical public status/topic parity
|
|
1669
|
+
subtest through that binary. Internal topic and idle cases always
|
|
1670
|
+
compile this source; there is no installed target-topic CLI. Canonical public
|
|
1671
|
+
`agent status/topic` uses its separate already-routed mirror and is parity evidence,
|
|
1672
|
+
not proof of these compatibility projections.
|
|
1673
|
+
|
|
1674
|
+
`TestSharedPaneRoutingRealTmux` uses two real isolated servers with the same pane
|
|
1675
|
+
ID and checks set, clear, marker and full hook state against target readback and
|
|
1676
|
+
opposite-server sentinels for detached and inherited routes. It refuses a missing
|
|
1677
|
+
inherited route without fallback. Every server starts with a unique socket name
|
|
1678
|
+
inside a dedicated tmux root; only a verified private alias exposes the fixed app
|
|
1679
|
+
locator. Cleanup rechecks the observed exact socket before `-S` shutdown. The test
|
|
1680
|
+
runs with `make test` where tmux is available; an explicit installed smoke runs the
|
|
1681
|
+
same hook assertions against `PROJMUX_TEST_PANE_ROUTE_BINARY=<absolute binary>`.
|
|
1682
|
+
This is a test-only selector, not a product environment contract. The fixture
|
|
1683
|
+
seeds and strips the caller's internal activation Pane/generation envelope so a
|
|
1684
|
+
managed test process cannot suppress the isolated hook as a stale activation.
|
|
1685
|
+
|
|
1686
|
+
### Attributed Codex hook delivery route tests
|
|
1687
|
+
|
|
1688
|
+
Attribution and reflection are different layers. A Codex hook attributed to its
|
|
1689
|
+
Pane still has to write that Pane's options, and the hook the shared app-server
|
|
1690
|
+
launches inherits no tmux environment at all: its `tmux set-option` carried no
|
|
1691
|
+
socket argument, probed the operator's `default` server, found none, and handed
|
|
1692
|
+
back `exit status 1`. `ai-ingest.log` recorded that exit status where a cause
|
|
1693
|
+
belongs.
|
|
1694
|
+
|
|
1695
|
+
`applyCodexHookSemanticDelivery` now resolves one exact server before it writes
|
|
1696
|
+
anything, and refuses by name when it cannot.
|
|
1697
|
+
|
|
1698
|
+
- `TestCodexHookDeliveryWritesItsPaneWithoutAnyTmuxEnvironment` owns the case
|
|
1699
|
+
the failure came from: with no `TMUX` at all the delivery still resolves a
|
|
1700
|
+
route, and the argv it produces is pinned to the app-owned socket rather than
|
|
1701
|
+
left unprefixed.
|
|
1702
|
+
- `TestCodexHookDeliveryPrefersTheInheritedReceiptOverTheAppRoute` owns the
|
|
1703
|
+
precedence. A pane client's own `$TMUX` receipt names its server without a
|
|
1704
|
+
search, is taken as an explicit socket path, and needs no containment probe.
|
|
1705
|
+
- `TestCodexHookDeliveryRefusesARouteItCannotProve` owns the proof. The
|
|
1706
|
+
app-owned route is never trusted on its name: an unreachable runtime, an empty
|
|
1707
|
+
answer, a server that is not app-owned, a runtime holding a different pane,
|
|
1708
|
+
and a pane that is not a projmux Pane each produce their own reason and write
|
|
1709
|
+
nothing.
|
|
1710
|
+
- `TestCodexHookDeliveryFailureReasonsAreConcreteAndClosed` owns the vocabulary.
|
|
1711
|
+
Every token a refusal can render — the three reasons, the two route kinds, the
|
|
1712
|
+
four tmux causes — is non-empty and carries no socket path, no routing flag,
|
|
1713
|
+
and no process exit status.
|
|
1714
|
+
- `TestCodexHookDeliveryLeavesNoTransportDetailInTheIngestLog` owns the record
|
|
1715
|
+
rather than the error value. It injects a transport failure whose tmux output
|
|
1716
|
+
names its socket, drives the real ingest route, and reads the whole log file
|
|
1717
|
+
back: a reason that leaks is caught there even when every assertion above
|
|
1718
|
+
still passes.
|
|
1719
|
+
- `TestCodexHookDeliveryExplainsARejectedWriteByRereadingItsRoute` owns the
|
|
1720
|
+
residual failure. A `set-option` that fails after the route was proven is
|
|
1721
|
+
explained by re-reading the route — a pure read that repeats no write — so the
|
|
1722
|
+
record says whether the pane vanished or the runtime rejected the option.
|
|
1723
|
+
|
|
1724
|
+
tmux explains a refusal on stderr and that text carries the socket path it
|
|
1725
|
+
tried, so `codexHookDeliveryCause` is the only reader of tmux's own words here:
|
|
1726
|
+
it classifies them into a closed set and returns none of them. A reason names
|
|
1727
|
+
which lane answered — inherited or app-owned — and never which file backs it,
|
|
1728
|
+
because this track's change boundary keeps paths out of durable records.
|
|
1729
|
+
|
|
1730
|
+
The route decides where a reflection is written, not which Pane it belongs to;
|
|
1731
|
+
attribution stays with the matcher and the Registry step above. Two other hook
|
|
1732
|
+
writers on the same shared-host path, `applyAIStatusInternalWithActivationPolicy`
|
|
1733
|
+
and `markAIHookPane`, still issue unrouted tmux calls and discard their errors,
|
|
1734
|
+
so their failures are invisible rather than fixed.
|
|
1735
|
+
|
|
1736
|
+
### Install residue census tests
|
|
1737
|
+
|
|
1738
|
+
`make install` and `npm install` replace the executable and leave every
|
|
1739
|
+
long-lived projmux child on the image it started with. `projmux internal
|
|
1740
|
+
install-residue` measures how much of the fleet that is, appends one record to
|
|
1741
|
+
`install-residue.jsonl`, and prints at most one stderr notice; see
|
|
1742
|
+
[operational-diagnostics.md](operational-diagnostics.md#install-residue-ledger)
|
|
1743
|
+
for the ledger format and the mapping from its fields to the questions a
|
|
1744
|
+
bounded drain of those processes has to answer.
|
|
1745
|
+
|
|
1746
|
+
The deliverable is the measurement, not the message, so the tests are weighted
|
|
1747
|
+
toward the record rather than the wording.
|
|
1748
|
+
|
|
1749
|
+
- `TestProjmuxProcessVintageMeasuresTheResidualAgeDistribution` owns the age
|
|
1750
|
+
half of the census: only replaced-image processes contribute an age, the
|
|
1751
|
+
distribution is stored ascending and whole, and a clock that moved backwards
|
|
1752
|
+
floors at zero rather than reporting a process started after the census that
|
|
1753
|
+
observed it.
|
|
1754
|
+
- `TestProjmuxProcessVintageKeepsAnUnknownStartTimeOutOfTheDistribution` owns
|
|
1755
|
+
both halves of the unknown case: the process still counts as residue, and no
|
|
1756
|
+
age is invented for it.
|
|
1757
|
+
- `TestProjmuxProcessVintageCapsOneRoleAgeDistribution` owns the per-role sample
|
|
1758
|
+
bound and pins that a census which hit it says so, rather than reporting a
|
|
1759
|
+
prefix as a whole distribution.
|
|
1760
|
+
- `TestDoctorProcessVintageProjectionCarriesNoAges` keeps `projmux doctor`'s
|
|
1761
|
+
record exactly what it was: the age fields populate only when a caller
|
|
1762
|
+
supplies a reference instant.
|
|
1763
|
+
- `TestProcessImageListerReadsAPlausibleStartTime` (Linux) covers the one part
|
|
1764
|
+
a synthetic process table cannot. A reader that silently returned the zero
|
|
1765
|
+
time would leave every distribution empty, which is indistinguishable from a
|
|
1766
|
+
platform where start times are unknowable.
|
|
1767
|
+
- `TestInstallResidueReportsPerRoleResidualCountsAndAges` owns the record
|
|
1768
|
+
field-for-field, including the per-role distribution being as long as that
|
|
1769
|
+
role's residual count and ascending.
|
|
1770
|
+
- `TestInstallResidueZeroResidualPrintsNothing` owns the boundary that keeps the
|
|
1771
|
+
notice worth reading: an install that reached the whole fleet prints
|
|
1772
|
+
absolutely nothing, and still records that it did.
|
|
1773
|
+
- `TestInstallResidueUnsupportedPlatformPrintsNothingButRecordsTheGap` owns the
|
|
1774
|
+
macOS path: no terminal output where the census cannot be taken, and a
|
|
1775
|
+
`supported:false` record so the coverage gap is measured rather than absent.
|
|
1776
|
+
- `TestInstallResidueCarriesNoProcessIdentity` is the privacy gate, asserted
|
|
1777
|
+
negatively over the rendered notice and the ledger bytes at once: sentinel
|
|
1778
|
+
pids, executable path, and argv appear in neither.
|
|
1779
|
+
- `TestInstallResidueLedgerAppendsAndDerivesTheInstallGap` owns the frequency
|
|
1780
|
+
half — records accumulate and each carries the gap to the one before it.
|
|
1781
|
+
- `TestInstallResidueLedgerRotatesAtItsRecordBound` owns growth: the oldest rows
|
|
1782
|
+
go, the newest stay, and the file keeps mode `0600` across the rotation.
|
|
1783
|
+
- `TestInstallResidueNoticeOrdersRolesByResidualCount` and
|
|
1784
|
+
`TestInstallResidueNoticeTiesBreakOnTheCensusRoleOrder` own the render
|
|
1785
|
+
contract: biggest residual first, census role order on a tie, zero-residual
|
|
1786
|
+
roles omitted entirely.
|
|
1787
|
+
- `TestInstallResidueAgeTextRendersDurationsAnOperatorReads` owns the duration
|
|
1788
|
+
format, the median of both odd and even distributions, and the unknown case,
|
|
1789
|
+
which must not render as a zero age.
|
|
1790
|
+
- `TestInstallResidueNeverFailsAnInstall` owns the hard edge: an unresolvable
|
|
1791
|
+
state directory, an unwritable ledger, and the route entrypoint itself all end
|
|
1792
|
+
in a silent success.
|
|
1793
|
+
- `TestInstallResidueInstallerComesFromTheExistingEnvVar` pins that installer
|
|
1794
|
+
identity is read from `PROJMUX_INSTALLER` and that no new `PROJMUX_*` variable
|
|
1795
|
+
is introduced, since any such variable is a minor-release input to the hook
|
|
1796
|
+
contract.
|
|
1797
|
+
- `TestInstallResidueRouteIsRegisteredAsInternalPlumbing` pins the spelling the
|
|
1798
|
+
installer invokes and keeps the census out of the legacy hook migration path.
|
|
1799
|
+
|
|
1800
|
+
Nothing in this feature terminates, starts, signals, or restarts a process.
|
|
1801
|
+
The census is two file reads per process; replacing the residual processes is a
|
|
1802
|
+
separate decision this measurement exists to inform.
|
|
1803
|
+
|
|
578
1804
|
## Review Checklist
|
|
579
1805
|
- The branch stays within its stated scope.
|
|
580
1806
|
- The change preserves boundaries between portable `projmux` behavior and local machine policy.
|
|
581
1807
|
- The required `make` targets were run in order.
|
|
582
1808
|
- Test inventory updates are included when behavior changed.
|
|
583
1809
|
- Known parity gaps are explicit.
|
|
1810
|
+
|
|
1811
|
+
- `TestClaudePushSourceReplacementAfterDurableHandoffWritesZero` and
|
|
1812
|
+
`TestClaudeProviderPushFinalRouteCheckPrecedesSoleWrite` cover source/target
|
|
1813
|
+
authority loss across durable work and the final provider pre-write fence.
|
|
1814
|
+
- `TestClaudeExplicitMultipleRequestsAndHumanOverlapSelectOnlyNamedOriginal`
|
|
1815
|
+
requires an explicit choice of the original request; Stop text has no reply
|
|
1816
|
+
authority regardless of human activity or pending request count.
|
|
1817
|
+
- `TestClaudeReplyToolLiteralCommandAndOpaqueCarrier` and
|
|
1818
|
+
`TestClaudeReplyToolRejectsMalformedAndOtherOfficialTools` reject shell
|
|
1819
|
+
expansion, extra commands, nonallowlisted tools and malformed hook input.
|
|
1820
|
+
A shell-prefix carrier supplies only one canonical memory ticket; its other
|
|
1821
|
+
bytes are never executed or retained.
|
|
1822
|
+
- `TestClaudeReplyToolTicketsChooseExactActionAndConsumeOnce` and
|
|
1823
|
+
`TestClaudeReplyToolForeignStaleExpiredAndReplacedExecutableExecuteZero`
|
|
1824
|
+
bind parallel actions to separate tickets and one exact process birth,
|
|
1825
|
+
executable inode, current route and argv. Replays, expired tickets, foreign
|
|
1826
|
+
callers, replaced executables and missing hooks obtain no execution witness.
|
|
1827
|
+
- `TestClaudeReplyToolRequestedCaptureFailureRefusesAndSecretsAreAbsent` keeps
|
|
1828
|
+
requested guard capture failures closed and messaging credentials out of the
|
|
1829
|
+
fixed public-command environment.
|
|
1830
|
+
- `TestClaudeExplicitReplyUnsupportedTargetCannotCorruptStore` rejects a
|
|
1831
|
+
non-Codex reply target before a durable write. The process integration fixture
|
|
1832
|
+
now owns a real Codex broker binding for its original source.
|
|
1833
|
+
|
|
1834
|
+
- `TestAuthorityCheckUsesExistingExactLeaseWithoutProviderTraffic` proves the
|
|
1835
|
+
dialogue source's exact live Codex broker runtime, connection and binding
|
|
1836
|
+
fence with no new binding or provider request.
|
|
1837
|
+
- `TestNonblockingHookStoreRefusesContentionWithoutLateWrite` and
|
|
1838
|
+
`TestClaudeOfficialHookContentionInvalidatesReplyWithoutWaiting` keep compatibility hooks bounded. Explicit reply commits use broker original
|
|
1839
|
+
request validation independently of hook timing.
|
|
1840
|
+
|
|
1841
|
+
- `test/agent_dialogue_canary_test.py` covers exact public claim/schema mutations,
|
|
1842
|
+
runtime-first owner/generation negatives, prepare-without-provider-launch,
|
|
1843
|
+
environment isolation, and automatic partial-setup delayed-writer/early-error
|
|
1844
|
+
cleanup, plus registered Project session projection/foreign-scope refusals
|
|
1845
|
+
before runtime creation and automatic cleanup of a registered Project when
|
|
1846
|
+
setup fails before tmux creation. Audit tests preserve closed stage/exit and exact writer birth evidence
|
|
1847
|
+
outside the disposable root, reject unsafe candidate permissions before
|
|
1848
|
+
credential copying, and retain roots after uncertain cleanup without retry. Product `TestClaudeDialogueStream*` tests own public output parsing;
|
|
1849
|
+
the obsolete separate canary collector is removed.
|
|
1850
|
+
- `TestClaudeDialogueCanaryAcceptsProductionStoreAndPublicClaimReceipts` validates
|
|
1851
|
+
production Store.PutReply/Claim and public CLI JSON with the Python companion,
|
|
1852
|
+
including the actual `target-self-claim` reason and version-2 envelope.
|
|
1853
|
+
|
|
1854
|
+
- L20 owns a real authenticated Codex broker host and keeps its observed snapshot
|
|
1855
|
+
binding alive for the round trip; Registry-only synthetic authority is refused.
|
|
1856
|
+
The offline endpoint records and rejects every provider request/answer.
|
|
1857
|
+
First-failure diagnostics accept L20 and keep its replay attribution.
|
|
1858
|
+
|
|
1859
|
+
- `test/agent_dialogue_canary_test.py` also exercises Linux-only failed-canary
|
|
1860
|
+
cleanup with explicit delayed-writer barriers: the actual EXIT trap retains
|
|
1861
|
+
its root until the captured writer exits, preserves the original failure,
|
|
1862
|
+
tracks reparented descendants, and refuses stubborn or replaced births.
|
|
1863
|
+
These regressions use no live provider and skip when Linux pidfd is absent.
|
|
1864
|
+
|
|
1865
|
+
- `TestClaudeQualificationRequiresCurrentPinnedMemoryGuard` rejects asserted
|
|
1866
|
+
evidence with a missing, replaced or writable guard image before any push.
|
|
1867
|
+
Private coord v5 fences earlier unguarded qualification; the frozen provider
|
|
1868
|
+
auth/user frame is unchanged. L20 and the process fixture now execute the
|
|
1869
|
+
guarded public CLI, with real isolated tmux identity and Codex broker binding.
|
|
1870
|
+
|
|
1871
|
+
- `TestLifecycleDiscardedASCIIKeepsStringBoundariesAndValidation` covers the
|
|
1872
|
+
body-free projector's buffered ASCII scan across chunk, quote, escape,
|
|
1873
|
+
control-character and UTF-8 boundaries. The optimized path retains no body
|
|
1874
|
+
and leaves byte/depth/scalar limits and the 750 ms operation limit unchanged.
|
|
1875
|
+
`TestLifecycleDiscardedASCIIChecksCancellationWithinBoundedBufferedRun`
|
|
1876
|
+
requires cancellation within one 4 KiB buffered run and preserves the
|
|
1877
|
+
retained-state refusal before scanning.
|
|
1878
|
+
|
|
1879
|
+
- Offline L20 cleanup (`test/e2e/dialogue-cleanup.py`) reuses the failed-canary
|
|
1880
|
+
pidfd barrier and also captures exact owned cwd paths before canonical delete.
|
|
1881
|
+
It waits for captured pane/supervisor/helper births after reparenting; only
|
|
1882
|
+
role-validated fixture jobs and its exact broker receive pidfd TERM. The outer
|
|
1883
|
+
EXIT trap retains the root on missing exit proof. `OfflineDialogueCleanupTest`
|
|
1884
|
+
synchronizes a cwd-only late receipt writer with actual delete intent, checks
|
|
1885
|
+
original failure exit preservation, and rejects stubborn/foreign/replaced
|
|
1886
|
+
births without broad signals. Historical L20 late-writer attribution remains
|
|
1887
|
+
unknown; process exit proof does not depend on that attribution.
|
|
1888
|
+
- `TestStoreReplyIsAtomicCorrelatedAndReplayIdempotent` pins both store instances
|
|
1889
|
+
to the fixture clock so the replay test remains independent of wall-clock
|
|
1890
|
+
terminal retention while retaining the explicit one-hour replay timestamp.
|
|
1891
|
+
|
|
1892
|
+
- `TestClaudeDialogueCreateOptInRefusesUnsupportedOrUnconfiguredLaunchBeforeWrites`,
|
|
1893
|
+
`TestClaudeDialogueResumeOptInKeepsRunningAndNonClaudeRefusalsReadOnly`, and
|
|
1894
|
+
`TestClaudeDialogueOptInIsAnExplicitSupervisorEnvelopeNotAnInheritedDefault`
|
|
1895
|
+
cover the next-activation `--dialogue-reply-only` preflight and supervisor/gate
|
|
1896
|
+
envelope. No AgentSpec or default tool policy persists this opt-in. The
|
|
1897
|
+
production launcher creates a private generation profile and an owned
|
|
1898
|
+
stdout/stderr observer. Missing launcher adapters still fail before allocation.
|
|
1899
|
+
- `TestAgentMessageAndReplyOnlyPreflightDoNotMigrateGlobalOrProjectHooks` checks
|
|
1900
|
+
that public message authentication and explicit reply-only preflight never
|
|
1901
|
+
migrate unrelated global/project legacy hooks. Existing normal create/resume
|
|
1902
|
+
pre-dispatch behavior remains unchanged.
|
|
1903
|
+
|
|
1904
|
+
- `TestClaudeDialogueProfilePinsFilesAliasAndInvocationMode` and
|
|
1905
|
+
`TestClaudeDialogueOrdinaryEnvironmentKeepsOfficialPrefixWithoutInheritingOptIn`
|
|
1906
|
+
cover immutable profile inputs, pinned candidate alias, FIFO refusal, and
|
|
1907
|
+
ordinary environment compatibility. `TestAgentMessageAndReplyOnlyPreflightDoNotMigrateGlobalOrProjectHooks`
|
|
1908
|
+
also covers the profile's exact lifecycle/register/helper callbacks.
|
|
1909
|
+
- `TestClaudeDialogueCurrentHelperEvidenceAndObserverLossPrecludeEffects` uses
|
|
1910
|
+
the current helper UDS to obtain qualification evidence and complete a valid
|
|
1911
|
+
guarded reply baseline. Replacing only the observer descriptor then refuses
|
|
1912
|
+
otherwise-valid prepare, consume, commit and push requests without effects.
|
|
1913
|
+
- `TestClaudeDialogueStreamDiscardsTextThinkingAndPairsOnlyExactReplyTool` and
|
|
1914
|
+
`TestClaudeDialogueStreamRejectsUnknownEffectsAndBoundsState` cover transient
|
|
1915
|
+
public JSON validation, action/result pairing, zero pre-inbound tool effects,
|
|
1916
|
+
unknown metadata refusal and finite hook/tool state.
|
|
1917
|
+
- `TestClaudeDialoguePipeEOFClosesInputButWaitsCurrentTurn`,
|
|
1918
|
+
`TestClaudeDialoguePipeDiagnosticLossFailsClosedWithoutBusyLoop`,
|
|
1919
|
+
`TestClaudeDialogueCleanupWaitsExactWriterAndHandlesZombieWithoutSignals`, and
|
|
1920
|
+
`TestClaudeDialoguePartialSetupAndUnknownFilesRetainExactRoot` cover Linux
|
|
1921
|
+
terminal EOF, current-turn completion, stderr loss, exact-birth pidfd exit
|
|
1922
|
+
proof and retained evidence on uncertain cleanup. No provider runs in these tests.
|
|
1923
|
+
|
|
1924
|
+
- L20 now launches the synthetic Claude through public `--dialogue-reply-only`,
|
|
1925
|
+
exercises the real profile executor, observer and pinned prefix dispatch, and
|
|
1926
|
+
qualifies from current helper-memory evidence. It creates no hand-authored
|
|
1927
|
+
public-init evidence file. Qualification and ordinary replies are separately
|
|
1928
|
+
claimed by the original Codex route. This is deterministic integration
|
|
1929
|
+
evidence, not actual model execution. Wrapped pane output is joined for the
|
|
1930
|
+
readiness marker; cancellation closes the fixture's own first-frame listener.
|
|
1931
|
+
- `TestClaudeEndpointProcessIntegration` intentionally remains the private
|
|
1932
|
+
registration/bootstrap/process fixture. Its owned registration child opts in
|
|
1933
|
+
after activation; it does not depend on a supervisor-inherited guard flag,
|
|
1934
|
+
which production correctly scrubs. It does not substitute for public launch
|
|
1935
|
+
or actual provider qualification evidence.
|
|
1936
|
+
|
|
1937
|
+
- `TestClaudeDialogueToolEvidenceIsReadOnlyAndIndependentOfArrivalOrder` and
|
|
1938
|
+
`TestClaudeDialogueObservedToolCannotGrantOrForgeCommitEvidence` distinguish
|
|
1939
|
+
observed model tool ID/original selection and paired result from the pinned
|
|
1940
|
+
execution witness and successful broker commit. A failed commit, wrong result
|
|
1941
|
+
ref or forged observation cannot become correlated execution evidence; reading
|
|
1942
|
+
that bounded snapshot performs no delivery or reply action.
|
|
1943
|
+
- `TestAgentCapabilitiesClaudeMessageCellsReflectMissingRegistrationLease` and
|
|
1944
|
+
`TestAgentCapabilitiesClaudeUnqualifiedRecoveryUsesCurrentPublicProfile` keep
|
|
1945
|
+
recovery read-only and actionable: normal exit, same-UID explicit reply-only
|
|
1946
|
+
resume, then current Codex qualification from helper-memory evidence.
|
|
1947
|
+
|
|
1948
|
+
- `test/agent_dialogue_codex_observation_test.py` checks the frozen public Codex
|
|
1949
|
+
0.153.2 schema, explicit observed source, one thread/turn/item, paired command
|
|
1950
|
+
completion and closed result correlation, unknown/body privacy negatives, and
|
|
1951
|
+
bounded read-only connection EOF/deadline/peer replacement. It supplies no
|
|
1952
|
+
actual provider or model-action evidence.
|
|
1953
|
+
- `test/agent_dialogue_source_action_test.py` checks exact public qualification,
|
|
1954
|
+
original-source inbox claims and one idle send against production-shaped
|
|
1955
|
+
reply fixtures; changed composite routes fail before dispatch and unknown
|
|
1956
|
+
send outcomes are never retried. The source action returns closed facts and
|
|
1957
|
+
performs no fleet/root cleanup. Parent orchestrator wiring remains pending.
|
|
1958
|
+
|
|
1959
|
+
- `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.
|
|
1960
|
+
|
|
1961
|
+
- `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.
|
|
1962
|
+
|
|
1963
|
+
- 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.
|
|
1964
|
+
|
|
1965
|
+
- `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.
|
|
1966
|
+
- `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.
|