projmux 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/agent-workflow.md +93 -9
- package/docs/cli-guide.md +16 -4
- package/docs/cli.md +5 -4
- package/docs/codex-native-required-migration.md +175 -0
- package/docs/hooks.md +30 -5
- package/docs/npm-distribution.md +6 -0
- package/docs/operational-diagnostics.md +7 -8
- package/docs/testing.md +20 -1
- package/docs/troubleshooting.md +7 -0
- package/docs/upgrading.md +15 -0
- package/package.json +5 -5
package/docs/agent-workflow.md
CHANGED
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
- `make test`: fast unit coverage for app-layer AI split native agent launch/selective popup-toggle/settings/status/notification parity including AI pane option metadata, watcher metadata bootstrap for existing panes, capture-backed reply detection, missing-pane watcher shutdown, armed focus-only reply badge clearing, busy attention preservation on focus clear, manual AI topic preservation while watcher status still updates, agent-labeled desktop notification message context with pane-title-first body text, global/project-local lifecycle hook dispatch for post-create/pre-create/post-attach/send-noti with project hook and `.projmux/config.toml` trust-store hashing plus env/settings kill-switch gating, declarative startup command and hook run coverage, `send-noti` stdin JSON delivery plus `PROJMUX_NOTIFY_*` env payload, notify queue write success/failure/depth-guard dispatch rules, notify/statusbar/sidebar origin-client focus routing, project config generic env session environment application without Kube-specific projection, project hook/trust writes with startup/env compatibility, startup command send-keys orchestration plus startup pane replay markers, pre-create abort behavior, and shared projmux notification icon paths, and scoped even row/column resizing after shell and agent splits, status-bar git segment parity including muted git branch block styling, compact dirty/staged/ahead/behind state colors, and bounded git child execution, statusbar pwd display-only native-framed path popup with shared popup-wait-key/no-extra-payload-line command coverage, no clipboard or tmux buffer copy, and popup-wait-key cursor/raw-mode restore coverage, statusbar usage native HUD popup alignment/height-budget/threshold palette/muted sync-staleness/fallback coverage without raw CLI popup or popup-toggle stacking, statusbar settings click popup fallback and settings chip right-edge rendering without default trailing space, popup-toggle stale marker recovery, isolated `projmux shell` tmux app launch/config generation including home fallback plus project-context default session targeting from `PROJMUX_CWD` or nearest project marker, app-owned project-name statusbar layout, distinct project badge color, and quiet debounced session-state autosave command/app-config trigger, shell-entry welcome release prompt and inline update handling, shell update skip-by-latest-tag behavior plus best-effort stale-cache refresh, pane/window keybindings, keymap.toml tmux override rendering/stale unbinds including retired direct/UserKey cleanup, Settings Keybindings root/list/detail capture flows including parse-error rows, unsafe raw capture and timeout guards, disable/reset writes, app config regeneration, live tmux source-file reload, no-live-tmux save behavior, and stage-by-stage apply recovery copy, window rename bindings, pane rename helper/binding, pane-exit rebalance command/hooks, hook-pane and after-select-pane based attention focus hooks, attention badge toggle/clear/list/window rendering, attach/current/kill/pin/preview/prune/sessions/session-popup/settings commands, switch, tag, tmux helper commands, update status/check/apply cache and installer detection including GitHub Release binary asset selection/extraction/replacement, doctor versioned read-only report/section/verbosity contracts, AI notify integration diagnostics, and Session State resume metadata diagnostics, Settings AI notify integration diagnostics read-only status/conflict/CLI guidance, and Settings About update status/check action wiring, untitled standalone popup-toggle marker close/config install, direct popup minimum sizing, AI picker minimum width and height, sidebar minimum width and compact badge spacing, preview select writes, popup render output after cycling, switch picker pin action behavior without inline settings rows, nested settings hub sections for AI defaults, project picker filesystem scan/pin actions, Project Root settings source/shadowing/set/current/clear flows, app/keybinding info including Ctrl-M rename forwarding, and About version/source rendering, switch picker focused-session kill, switch picker launcher-key abort bindings, switch explicit project-root, unconfigured-root, and weak managed-root heuristic parity, switch popup hiding new-session candidates while sidebar keeps create-capable rows, switch row project-name display with `~` pinned to the top and live-session-first sorting, pretty-path, preview context without retired Kube metadata, switch settings subcommand flows including add-current-pin, interactive add-pin picker, and settings label/preview polish including Phase 2 Settings/hookmaker/project-startup/trust/quit destructive row color regression strings, native preview wiring, baseline picker surface parity including prompt/footer/header fallback without app-name filler and search-key scoped card matching, sidebar compact action-only key footer, sidebar preview-window/start-position behavior without focus-time session switching, sidebar row/window ANSI styling with pane-aggregated attention badge state and AI topic labels, Alt+2/Alt+3 legacy popup row, preview-window, pane metadata, and pane-snapshot parity, switch read0 card rows with active/inactive title styling, right-side status badges, combined directory/git metadata with muted inactive branch styling and bounded slow-repository branch enrichment, statusbar-matched block window tabs with window attention badges, read0 expect-key action parsing, restored pin/tag card badges, and restrained selected-row marker styling, switch preview metadata without duplicated directory/git rows, preview metadata rendering, popup pane display names for AI agents, AI topics, and shell commands, switch preview cycle bindings, sessions picker preview/cycle/open/kill wiring including attached-session fallback behavior, sessions picker launcher-key abort bindings, popup/switch preview summary formatting, popup sessions tmux entry helpers, switch/popup/session rendering, session identity, session-state Claude/Codex/Antigravity resume and declarative startup replay, session snapshot capture/autosave recipe classification including save/autosave pre-capture resume metadata refresh from live AI session ids including Antigravity conversation ids, Claude transcript paths, and Codex rollout log cwd matching/ambiguity skips, candidate discovery, config path derivation, popup preview read-models, and pure state rules including preview, tag, and lifecycle stores.
|
|
112
112
|
- `make test`: fast unit coverage for app-layer AI split native agent launch/selective popup-toggle/settings/status/notification parity including AI pane option metadata, watcher metadata bootstrap for existing panes, capture-backed reply detection, missing-pane watcher shutdown, armed focus-only reply badge clearing, busy attention preservation on focus clear, manual AI topic preservation while watcher status still updates, agent-labeled desktop notification message context with pane-title-first body text, global/project-local lifecycle hook dispatch for post-create/pre-create/post-attach/send-noti with project hook and `.projmux/config.toml` trust-store hashing plus env/settings kill-switch gating, declarative startup command and hook run coverage, `send-noti` stdin JSON delivery plus `PROJMUX_NOTIFY_*` env payload, notify queue write success/failure/depth-guard dispatch rules, notify/statusbar/sidebar origin-client focus routing, project config generic env session environment application without Kube-specific projection, project hook/trust writes with startup/env compatibility, startup command send-keys orchestration plus startup pane replay markers, pre-create abort behavior, and shared projmux notification icon paths, and scoped even row/column resizing after shell and agent splits, status-bar git segment parity including muted git branch block styling and compact dirty/staged/ahead/behind state colors, statusbar pwd display-only native-framed path popup with shared popup-wait-key/no-extra-payload-line command coverage, no clipboard or tmux buffer copy, and popup-wait-key cursor/raw-mode restore coverage, statusbar usage native HUD popup alignment/height-budget/threshold palette/muted sync-staleness/fallback coverage without raw CLI popup or popup-toggle stacking, statusbar settings click popup fallback and settings chip right-edge rendering without default trailing space, popup-toggle stale marker recovery, isolated `projmux shell` tmux app launch/config generation including home fallback plus project-context default session targeting from `PROJMUX_CWD` or nearest project marker, app-owned project-name statusbar layout, distinct project badge color, and quiet debounced session-state autosave command/app-config trigger, shell-entry welcome release prompt and inline update handling, shell update skip-by-latest-tag behavior plus best-effort stale-cache refresh, pane/window keybindings, keymap.toml tmux override rendering/stale unbinds including retired direct/UserKey cleanup, Settings Keybindings root/list/detail capture flows including parse-error rows, unsafe raw capture and timeout guards, disable/reset writes, app config regeneration, live tmux source-file reload, no-live-tmux save behavior, and stage-by-stage apply recovery copy, window rename bindings, pane rename helper/binding, pane-exit rebalance command/hooks, hook-pane and after-select-pane based attention focus hooks, attention badge toggle/clear/list/window rendering, attach/current/kill/pin/preview/prune/sessions/session-popup/settings commands, switch, tag, tmux helper commands, update status/check/apply cache and installer detection including GitHub Release binary asset selection/extraction/replacement, doctor versioned read-only report/section/verbosity contracts, AI notify integration diagnostics, and Session State resume metadata diagnostics, Settings AI notify integration diagnostics read-only status/conflict/CLI guidance, and Settings About update status/check action wiring, untitled standalone popup-toggle marker close/config install, direct popup minimum sizing, AI picker minimum width and height, sidebar minimum width and compact badge spacing, preview select writes, popup render output after cycling, switch picker pin action behavior without inline settings rows, nested settings hub sections for AI defaults, project picker filesystem scan/pin actions, Project Root settings source/shadowing/set/current/clear flows, app/keybinding info including Ctrl-M rename forwarding, and About version/source rendering, switch picker focused-session kill, switch picker launcher-key abort bindings, switch explicit project-root, unconfigured-root, and weak managed-root heuristic parity, switch popup hiding new-session candidates while sidebar keeps create-capable rows, switch row project-name display with `~` pinned to the top and live-session-first sorting, pretty-path, preview context without retired Kube metadata, switch settings subcommand flows including add-current-pin, interactive add-pin picker, and settings label/preview polish including Phase 2 Settings/hookmaker/project-startup/trust/quit destructive row color regression strings, native preview wiring, baseline picker surface parity including prompt/footer/header fallback without app-name filler and search-key scoped card matching, sidebar compact action-only key footer, sidebar preview-window/start-position behavior without focus-time session switching, sidebar row/window ANSI styling with pane-aggregated attention badge state and AI topic labels, Alt+2/Alt+3 legacy popup row, preview-window, pane metadata, and pane-snapshot parity, switch read0 card rows with active/inactive title styling, right-side status badges, combined directory/git metadata with muted inactive branch styling, statusbar-matched block window tabs with window attention badges, read0 expect-key action parsing, restored pin/tag card badges, and restrained selected-row marker styling, switch preview metadata without duplicated directory/git rows, preview metadata rendering, popup pane display names for AI agents, AI topics, and shell commands, switch preview cycle bindings, sessions picker preview/cycle/open/kill wiring including attached-session fallback behavior, sessions picker launcher-key abort bindings, popup/switch preview summary formatting, popup sessions tmux entry helpers, switch/popup/session rendering, session identity, session-state Claude/Codex/Antigravity resume and declarative startup replay, session snapshot capture/autosave recipe classification including save/autosave pre-capture resume metadata refresh from live AI session ids including Antigravity conversation ids, Claude transcript paths, and Codex rollout log cwd matching/ambiguity skips, candidate discovery, config path derivation, popup preview read-models, and pure state rules including preview, tag, and lifecycle stores.
|
|
113
113
|
- `make test` also covers GitHub Release updater resource and integrity boundaries: bounded compressed responses, decompressed tar streams, total and per-file regular bytes, entry counts, required SHA-256 asset digests, release-host/redirect allowlisting, scratch cleanup, and no atomic replacement after rejection.
|
|
114
|
-
- `make test` also freezes Doctor schema-versioned plain/JSON fixtures, exact text/JSON row and section projection parity, shared-inventory parity, default-versus-verbose detail ownership, projected exit semantics, Phase 2 ordered runtime/log typed findings (backend, bounded socket probe/output, bounded regular generated-config reads and digest drift, state/log/journal permissions, non-regular/symlink/oversize rejection,
|
|
114
|
+
- `make test` also freezes Doctor schema-versioned plain/JSON fixtures, exact text/JSON row and section projection parity, shared-inventory parity, default-versus-verbose detail ownership, projected exit semantics, Phase 2 ordered runtime/log typed findings (backend, bounded socket probe/output, bounded regular generated-config reads and digest drift, state/log/journal permissions, non-regular/symlink/oversize rejection, and bounded safe errors), exact removed-flag migration errors, typed display-only remediation, raw path/socket/routing/message/config/argv negative coverage, support-report scalar-array allowlisting and schema compatibility, and the strict no-installer/no-migration/no-operational-journal write or repair boundary across the full flag matrix.
|
|
115
115
|
- `make test` also covers resume-title cleaning at the shared Claude/Codex/Antigravity choke point: known Claude command, local-command, task-notification, and system-reminder scaffolding is discarded so discovery falls through to the first real prompt, while inline `<`, leading HTML, and custom-element prompts remain intact.
|
|
116
116
|
- `make test` also covers the hardcoded usage HUD manual refresh key, full enabled-adapter collection through the existing throttle/backoff policy, cooldown no-op, cached popup rerender, and unchanged click/`u` cache-only behavior.
|
|
117
117
|
- `make test` also covers usage collection-failure visibility: Claude/Codex/Antigravity adapter tables for one, several, and all rows failing field validation plus an empty array, asserting the surviving snapshot contents, the exact bounded warning string (count + row-index/field reasons, capped at five with `(+N more)`), and negative assertions that a skipped row is never fabricated as `0%`, a zero reset, or a synthesized identity; the two shape failures that still abort a whole Claude collect (non-array `limits`, the 64-row cap) versus a partial collect that refreshes the healthy rows; privacy guards that no raw upstream value, reset timestamp, opaque bucket ID, or decoder message reaches a warning; `formatStatusUsage` staleness goldens for fresh/stale/very-stale with the fresh golden byte-identical to the pre-change output and the age indicator driven by `staleAfter`/`veryStaleAfter` alone; and the `usage.collect.outcome` operations-journal family — closed provider/failure schema with shape rejection, one row per failing provider, zero rows on success, once-per-run suppression of identical tuples, best-effort appends that never change the command result, and `projmux diagnostics log --component usage --tail N` rendering.
|
|
@@ -136,6 +136,8 @@
|
|
|
136
136
|
- `make test` / `make test-integration` / `make test-e2e`: keymap multi-stroke Phase 0 pins v2 `sequences` parse/render/merge, marker-only v1 migration with pre-v2 backup and rollback, repeat no-op bytes, deterministic two-to-four-stroke trie/table generation, shared prefixes across standalone/app actions, duplicate/strict-prefix/single-first-stroke conflict rejection, rejection of the undeliverable `C-m`/`C-i`/`C-[` spellings a terminal reports as `Enter`/`Tab`/`Escape`, unsafe and picker-local/command/output/default-sequence negatives, apply-ordered retire of the recorded roots/tables strictly before `source-file` (the generated config records that state but never removes it, because a `run-shell` loop is not ordered against a sourced file), native modified-stroke transport allowlisting without independent dispatch, and zero-write config/live failure ordering. The isolated real-tmux paths prove single dispatch for shared-prefix `mouse.toggle`/`window.create`, Escape and unknown continuation cancellation to `root` with zero raw pane input, terminal and native `send-keys -K -c` logical-stream parity, repeated apply idempotence, sequence removal cleanup, and unchanged single-chord behavior.
|
|
137
137
|
- `make test` / `make test-integration` / `make test-e2e`: keymap multi-stroke Phase 1 Settings authoring pins one shared 1–4 stroke Add/replace recorder, picker-local sequence exclusion, en-US/ko-KR locale and navigation golden reachability, shared reserved-key policy with first-plain rejection and later-plain acceptance, typed/captured canonical v2 keymap and generated-config byte parity, length-driven single/sequence add and atomic cross-kind replace plus strict-prefix conflict recovery, saved/prepared/running-session reporting including no-live-tmux recovery, native-macOS-on/off versus Linux/WSL logical authoring parity with transport differences confined to Delivery diagnostics, and zero keymap/generated-config/live-server mutation from navigation, cancellation, Backspace editing, or delivery tests.
|
|
138
138
|
- `make test`: Settings IA re-cut Phase 3 versioned keymap action-ID migration. The action catalog carries a `CanonicalID` beside the runtime `ID`, and `keymapActionManifest` derives the exhaustive old→canonical table from it, so `TestKeymapManifestCoversEveryCatalogActionExactlyOnce` fails on any action without a v1 spelling or any manifest row that resolves to no action, `TestKeymapCanonicalIDsAreUniqueAndDistinctFromLegacyIDs` rejects a canonical id that duplicates another action's canonical or legacy id, `TestKeymapRetiredIDsAreDisjointFromTheManifest` keeps retired ids out of the rename table and requires a remediation for each, `TestKeymapCanonicalIDsUseTheDottedSchema` pins the dotted lower-case shape and that the writer quotes it, and `TestKeymapDispositionIsExactlyOnePerTable` proves every table gets exactly one of canonical/coalesce/preserve-unknown/retired-remediation. Parser coverage adds the root `schema_version` marker with duplicate, forward-version and unknown-root-key refusals, and rejects an unquoted dotted table because that is a nested TOML table rather than a dotted key (`TestKeymapV1FileParsesAndRoundTrips`, `TestKeymapRejectsUnquotedDottedTableAndFutureSchema`). `TestKeymapMigrationPreservesEffectiveBindings` migrates a fixture carrying a custom multi-alias, `keys = []`, a transport-dependent additive alias, a legacy `plain` and a legacy `prefix` and requires the merged chord table to be identical before and after. The apply half is covered by `TestKeymapMigrationCoalescesIdenticalDualTables`, `TestKeymapMigrationRefusesConflictingDualTablesWithZeroWrites` (no keymap write, no backup, no tmux call), `TestKeymapMigrationPreservesUnknownTables`, `TestKeymapMigrationRepeatRunIsAByteWriteFreeNoOp` (unchanged bytes *and* unchanged mtime), `TestKeymapMigrationOnAbsentFileIsANoOp`, `TestKeymapMigrationBackupIsDigestNamedExclusiveAndReused`, `TestKeymapMigrationRefusesToClobberAForeignBackup`, `TestKeymapMigrationFailsClosedWhenTheDirectoryIsReadOnly`, `TestKeymapMigrationPreservesFileModeAndSymlinkTarget` (writes through the link, keeps 0600), `TestKeymapRollbackRestoresV0` and `TestKeymapRollbackRefusesACorruptBackup`. Convergence and the write boundary are pinned by `TestSettingsKeySaveConvergesAV0KeymapToV2`, `TestSettingsKeySaveAbortsWhenMigrationConflicts` (Schema/Saved/Prepared/Running-session stages), `TestTmuxApplyRefusesToApplyWhenTheKeymapMigrationFails`, `TestTmuxApplyNoReloadMigratesWithoutTouchingTheServer`, `TestConfigRenderReportsMigrationOnStderrAndWritesNoKeymap` (the preflight never reaches the generated artifact on stdout) and `TestReadOnlyRoutesNeverRewriteTheKeymap`, which drives the real dispatcher against an isolated HOME/XDG for `help`, `version`, `get`, `describe`, `agent usage`, `doctor --help`, both `config render` artifacts and `update apply --dry-run`. `TestUpdateDryRunPreviewsTheMigrationStageWithoutPromisingADiff` requires the preview to name the stage and leak no canonical id, and `TestPostUpdateApplyArgsAlwaysReachTheNewBinary` pins that `--no-apply` becomes `config apply --no-reload` so an installer suppresses the reload without skipping the migration. `TestKeymapAndRegistrySchemaMarkersAreIndependent` and `TestKeymapMigrationFailureLeavesTheRegistryUntouched` keep the keymap `schema_version` domain separate from the resource registry `apiVersion`/`schemaVersion` envelope, with separate backups and no shared transaction.
|
|
139
|
+
- `make test`: update-availability parity Phase 0 post-apply version verification. `update apply` now reads the version of the executable PATH resolves for `projmux` (falling back to the running executable when PATH has none) before publication and again after every stage, and reports an explicit failure instead of success when that version did not rise. `TestUpdateApplyFailsWhenTheInstalledVersionDidNotChange` covers a same-version npm reinstall, the same for `go`, `--no-apply`, an install already at the expected version, and a version that went backwards, and requires the message to name the current version, the expected version, and the install channel. `TestUpdateApplyWithoutACachedCheckStillNamesTheMissingExpectedVersion` keeps the expected slot filled with an explicit unknown rather than dropped, `TestUpdateApplySucceedsWhenTheInstalledVersionRose` keeps a real upgrade passing and names the verified executable, `TestUpdateApplyRefusesToReportSuccessWhenTheVersionCannotBeRead` turns a failing or unparseable probe into an unverified failure with no `>> verified:` line, `TestUpdateApplyVerifiesThePathResolvedExecutable` pins that the comparison reads the PATH-resolved binary and never an installer report, `TestUpdateApplyFallsBackToTheRunningExecutableWhenPathHasNoProjmux` keeps an off-PATH install verifiable, `TestUpdateApplyStageOrderIsUnchangedByVersionVerification` proves the published pre-converge/publication/`config apply` sequence is byte-identical for npm, go, and both `--no-apply` forms, `TestUpdateApplyDryRunPreviewsVerificationWithoutProbing` keeps `--dry-run` a pure preview across all three installers, and `TestParseProjmuxVersionOutput` pins the `projmux <version>` line reader.
|
|
140
|
+
- `make test`: update-availability parity Phase 1 channel-scoped availability source. The judgment authority is now chosen from the detected install channel: npm asks `registry.npmjs.org/projmux` for `dist-tags.latest`, while go, github-release, and source builds keep the GitHub release API, and both requests go through the same update HTTP client so the shell gate's timeout and redirect ceiling are unchanged. The cache gains an additive `source` field; an answer recorded for a different source is discarded rather than aged out, and a cache written before the field existed still reads as a GitHub release answer so the unchanged channels neither refetch nor lose their offer. `TestUpdateCheckPicksTheAvailabilitySourceFromTheInstallChannel` pins the per-channel routing, the recorded source in the cache file, and the single `v`-prefixed version format both authorities produce. `TestShellGateWaitsForTheNPMRegistryToPublish` covers the reported symptom in both directions with the GitHub authority made unreachable: no `u` while only GitHub has the version, `u` once npm publishes it. `TestUpdateJudgmentIsUnchangedForNonNPMChannels` holds the change-freeze boundary for go, github-release, and source, including a pre-existing source-less cache that must not trigger a refetch. `TestUpdateCacheRecordsItsAvailabilitySourceAndDropsAForeignOne` proves the recorded source, the discard on a channel switch (status and `cachedLatestVersion` both fall back to unknown), the refetch that replaces it, and the unchanged cache file name. `TestShellGateStaysSilentWhenTheAvailabilitySourceFails` covers an unreachable registry, an error status, a response with no `dist-tags`, and a non-JSON body, requiring the failure to be reported and the status report to name the state and the source. `TestSettingsAboutRefreshRowNamesTheAvailabilitySource` pins that the Settings > About refresh row names the authority the check actually contacts.
|
|
139
141
|
- Darwin native-key focused coverage includes portable physical chord parsing for Alt/Option, Control, Control-Option, Shift, letters/digits/navigation/function keys, `darwin && cgo` synthetic event-to-canonical-chord mapping for Option, Control, shifted letters, function keys, and arrows with unmatched/plain event pass-through, shared `keymap.toml` custom-action loading (including `M-a`), focused tmux client discovery, canonical `send-keys -K -c` dispatch through the existing root key table, native Settings Add key capture winning over simultaneous terminal text/error results after ignoring the activation Enter while the non-Darwin path preserves immediate terminal capture, default-on shell startup, `PROJMUX_NATIVE_KEYS=0`/`false` and Settings `[ui].native_keys = false` broker opt-out gates, broker direct no-op behavior, one-time pre-permission consent guidance, broker-only process replacement after an Accessibility wait so fresh macOS permission state is observed without restarting tmux, preservation of the direct no-server shutdown boundary while permission is pending, and an explicit Linux/Windows/no-CGO shell startup no-op boundary.
|
|
140
142
|
- `make test` covers the default picker key ownership across catalog, generated tmux config, terminal adapters, setup probes, and native popup close hints: `Alt-4` opens AI Resume as a guaranteed launch default, while `Alt-7` opens the AI split picker as its editable default.
|
|
141
143
|
- `make test` also covers canonical `projmux setup terminal` terminal detection, preview no-write, apply/backup, Ghostty candidate conflicts, Windows Terminal merge, config overrides, and symlink refusal/allow; removed top-level command dispatch/help negatives, canonical rejection and help absence of the removed legacy-only `--dry-run` flag, and setup remediation hints are asserted separately.
|
|
@@ -173,7 +175,7 @@
|
|
|
173
175
|
- `make test` also covers `projmux agent integrate tmux-bell` dry-run/install/remove tmux command planning, managed `alert-bell` hook append/idempotence/removal, preservation of unmanaged bell hooks, and canonical `projmux internal agent-hook ingest bell --pane` queue push/metadata/dedupe behavior for non-AI-managed panes.
|
|
174
176
|
- `make test` also covers upgrade-safe managed ingest producer convergence: fresh Codex/Claude/Antigravity hooks, Antigravity Statusline, and tmux bell writers use `projmux internal agent-hook ingest`; marker-owned 0.10.1 fixtures migrate transactionally while missing and unmanaged providers remain untouched; dry-runs show every target and old→canonical command; repeats perform zero file/tmux mutations; later-provider conflicts preflight before any write; injected Antigravity second-file and tmux Nth-command failures restore original bytes, options, and unrelated hooks; `config apply --no-reload` migrates files without live calls; and normal apply targets the requested exact `-L` socket. The pinned v0.10.1 GitHub Release post-replace fixture additionally proves that normal update executes exact `tmux apply` against the candidate and converges all four managed files plus the exact bell hook through the hidden current-apply handoff, while old `--no-apply` executes the candidate zero times and requires the documented explicit candidate `config apply --no-reload` split stage; current updater `--no-apply` continues to invoke the new binary automatically.
|
|
175
177
|
- `make test` also covers `projmux diagnostics agent-hook` tail/path rendering and bounded JSONL log trimming for ingest diagnostics.
|
|
176
|
-
- `make test` also covers the Phase 0 operational diagnostics foundation: closed safe-event schema, control/length/home sanitization, lossy stable top-level error messages with dynamic pane/title/body/transcript/config-value exclusion, exhaustive allowlisted top-level/multi-mode command classification including direct-help and explicit dry-run success exclusion with boolean `=false` safeguards, exactly-once state-changing/error outcomes with explicit zero-success/error-one coverage for the six automatic `internal agent-hook ingest`, attention arm/clear/window, session-state autosave, and recent-window record paths plus an explicit-mutation success regression, private permission repair,
|
|
178
|
+
- `make test` also covers the Phase 0 operational diagnostics foundation: closed safe-event schema, control/length/home sanitization, lossy stable top-level error messages with dynamic pane/title/body/transcript/config-value exclusion, exhaustive allowlisted top-level/multi-mode command classification including direct-help and explicit dry-run success exclusion with boolean `=false` safeguards, exactly-once state-changing/error outcomes with explicit zero-success/error-one coverage for the six automatic `internal agent-hook ingest`, attention arm/clear/window, session-state autosave, and recent-window record paths plus an explicit-mutation success regression, private permission repair, replace-existing bounded complete-record retention, corrupt-tail recovery, OS-owned inter-process lock identity/crash recovery and explicit 200 ms budget, concurrent short-process rotation, common text/JSON/tail/filter reader fixtures, and unchanged top-level runtime/usage/exitCoder output and exit semantics.
|
|
177
179
|
- `make test` also covers Operational diagnostics Phase 2 lifecycle adoption: the maintained explicit-mutation inventory (attach/current/kill/sessions/switch plus session-popup open, window recent, runtime prune, focus session switching, shell open-app, snapshot replay creation, popup-toggle cancel restore, and config apply); session create/attach/switch/kill and `config apply` start/outcome success/error tables; composite create-then-attach and prune-kill-then-attach failure-stage attribution while preserving one outer pair; shared run IDs and no duplicate top-level outcome; best-effort store/write failure injection; closed operation/code pairing; apply typed no-server versus generic runner failure classification; focus success sealing at the actual switch-client boundary; unchanged read-only/hot-path zero-success recording; typed no-write runtime health projection with latest-state and bounded failure-code semantics; Doctor runtime/log typed consumer reuse; and support-report raw socket/path/session/window/pane/name negative fixtures. `make test-integration` additionally runs real create/attach/switch/kill success/failure routing against a run-unique `-L` socket, validates exact lifecycle pairs and apply classifications without raw routing, and guards cleanup with the queried actual socket path beneath the run-unique smoke root.
|
|
178
180
|
- `make test` also covers Operational diagnostics Phase 3 Session State adoption: the closed outcome/operation/code/source/six-count schema; direct and popup manual save/delete, Settings latest/named save and current/project delete, deduplicated prune delete, and manual snapshot projection ownership; exact snapshot and item aggregates; error count omission; Settings feedback error capture; append-failure best-effort ownership; autosave session/config/source/interval/debounce/store/save/set-option failure ownership including quiet error-one and project on/off precedence over a broken global config, plus repeated autosave success/no-op zero-volume behavior; same-run coexistence with the runtime lifecycle pair; and seeded path/command/session metadata negative fixtures. `make test-integration` exercises manual save, autosave success/error, save → desired-state mutation → exact closed-Project restore projection → ordinary materialization, and aggregate delete against the guarded run-unique tmux socket and isolated XDG root, including exact snapshot cleanup and raw metadata negative checks.
|
|
179
181
|
- `make test` also covers Operational diagnostics Phase 4 notify/focus adoption: the closed transition/disposition/provider/category/route schema and cross-family rejection; explicit notify enqueue ownership with queued/deduplicated/failed outcomes; secondary attention and nested prune-reconcile enqueue ownership isolation with no-change zero volume; external-hook, Linux, WSL Toast/fallback, disabled, unavailable, desktop-dedupe, and visible-pane delivery outcomes; one shared process recorder with identical-tuple hot-path coalescing; append-failure best-effort semantics; focus direct/queue/toast request classification with unresolved, notify-only, fallback, and focused outcomes; same-run coexistence with the existing `session.switch` lifecycle pair without a generic duplicate; unchanged `PROJMUX_FOCUS_DEBUG` output; and seeded summary/body/tag/group/title/path/UUID plus support-report negative fixtures.
|
|
@@ -361,6 +363,17 @@
|
|
|
361
363
|
|
|
362
364
|
- `make test` / `make test-e2e`: interactive tmux `run-shell` producers converge on one output channel. tmux paints a foreground `run-shell` job's stdout, its stderr, and its `'<command>' returned <n>` line as a view-mode screen over the pane the key was pressed in, which is how a successful pane-menu split and a successful Window create/rename came to hide the Codex process the operator was reading behind `[0/1452]`. `internal/app/run_shell_output_ledger.go` declares the closed set: every producer -- catalog binding, context-menu item, status-bar binding, generated hook, startup command, runtime command, and the one allowlisted control sentinel -- carries exactly one of `intentional-ui`, `stdout-stderr-zero`, `redirect`, and `exact-client-message`, and `forbidden-overlay` exists only so the test has a name for what it refuses. Two sweeps keep the set closed: the generated standalone and app configs are rendered with every catalog action bound (Window create and rename ship with no default chord, so a stock-config sweep would never see them), each `run-shell` occurrence must resolve to exactly one row, and each row's declared background/redirect/exit-guard must match the bytes; a source sweep then requires every non-comment `run-shell` line in `internal/app` to be a registered site, so a new producer cannot be added without classifying it. Enforcement is structural rather than per-route: `App.Run` dispatches the ledgered interactive argv through a guard that buffers stdout and stderr, converges a failure to one bounded `display-message` on the exact client, and exits zero -- with the single exception of an undeliverable message, where the original error is returned rather than claiming success. Coverage pins the guarded/unguarded matrix (public `switch open`, `create`, `rename`, `describe`, popup payload routes, the picker, the supervisor, and every help spelling stay unguarded and byte-identical), a loud success reaching tmux as zero bytes and zero messages, the bounded one-line failure copy, and the confirmed producers' new copy: `Created Pane`, `Created Window`, `Renamed Window: <name>`, each delivered to the exact client with the canonical projection consumed in-process and left to the public CLI. The direct split bridge stops reporting a refusal twice (client message plus non-zero exit), the status-bar keyboard table now carries `#{client_tty}` so a second attached client never receives another operator's result, and the foreground runtime-created hook is exit-guarded so a refused convergence cannot paint the pane that just appeared. The generated-config theme goldens were rebaselined once for those two config changes.
|
|
363
365
|
|
|
366
|
+
- `make test`: Codex broker-facing app-server contract Phase 0 splits endpoint attach authority from daemon lifecycle authority and closes the inbound server-request, pre-turn, and experimental-capability seams the endpoint broker consumes. `TestEndpointAttachAndDaemonLifecycleAuthorityMatrix` closes the readiness x ownership x version table: a ready exact-current endpoint attaches whether or not the official daemon manager owns it, a skew, a protocol mismatch, an unknown running version, and unknown ownership each refuse with their own typed code, and lifecycle authority stays `managed` for a manager-owned endpoint, `cold-start` for a dead one, and `none` everywhere else including a ready unmanaged endpoint this process may talk to. `TestEndpointAttachAuthorityLeavesNativeActionReadinessUnchanged` keeps the existing native-action refusal and the rendered `Health` projection byte-identical, so the new axis adds no diagnostics surface. `TestAttachRefusesBeforeDialAndOpensExactlyOnceWhenAllowed`, `TestAttachToUnmanagedCurrentEndpointMutatesNoDaemonLifecycle`, and `TestAttachRefusesVersionSkewBeforeTheAttachDial` run the real path against a fake Codex whose every argv is recorded: an unmanaged exact-current endpoint initializes, resumes, and reads with a daemon start/stop/restart/kill/bootstrap/remote-control/login/config argv count of zero, while a skewed endpoint is refused after the read-only probe's single proxy open and before the attach dial, with zero thread requests. `TestServerRequestResponseIsOncePerConnectionAcrossRawScalarKinds`, `TestServerRequestRawIdentityIsPreservedFromFrameToResponse`, and `TestServerRequestResponseAuthorityDiesWithTheConnection` join the existing `TestServerRequestResponsePreservesRawScalarIDAndRejectsNonInt64` shape matrix to pin byte-faithful raw server-request identity from frame to response, keep a string id and a numeric id that render the same text as two distinct requests, refuse every duplicate answer with `ErrResponseAlreadySent` and zero wire bytes, and give a disconnected or replaced connection no pending response authority while a fresh connection starts a fresh ledger. `TestPreTurnThreadBootstrapSubscribesThenReadsWithoutTurns` and `TestPreTurnThreadBootstrapRefusesASubstitutedThread` pin the pre-turn bootstrap as exactly one explicit `thread/resume` subscription followed by one `thread/read(includeTurns=false)` snapshot against a no-turn fixture, with a substituted thread refused, a failed subscription stopping before the snapshot, and an empty id sending nothing. `TestNativeResumeUsesStoredThreadAndCreatesZeroThreads` and `TestNativeResumeRefusesAnUnnegotiatedConnectionBeforeTheWire` extend the same capability gate to `thread/resume`, whose always-on `excludeTurns` upstream also refuses without `experimentalApi`: a negotiated connection sends exactly one resume with the exact stored thread and zero `thread/start`, and an unnegotiated one is refused before any byte reaches the wire. `TestExperimentalInitializeGatesAdditionalRootsOnTheWire` is the initialize/request ordering ledger: additional writable roots reach `thread/start` and `thread/resume` only on a connection whose initialize negotiated `experimentalApi`, a plain initialize sends no capabilities at all and refuses roots with a typed `ErrExperimentalRequired`/`ErrUnsupported` before any request, and an empty root list stays off the wire. `TestStartDefaultThreadEmptyAndPromptedRequestCounts` gains the matching product-connection row: `--add-dir` roots on the current non-negotiated create connection refuse as a safe fallback with zero `thread/start` and `turn/start`. `TestBrokerFacingContractRetainsNoProviderContent` audits the added surface as closed field sets, drops the provider conversation title from the pre-turn snapshot, and keeps refusal codes and sentinels free of paths. `TestInstalledIsolatedPreTurnBootstrapSmoke` is opt-in through `PROJMUX_CODEX_BROKER_SMOKE_ROOT` with a matching contained `CODEX_HOME` and inherited `TMUX`/`TMUX_PANE` stripped; it attaches to a real unmanaged exact-current endpoint, creates a turnless thread, closes that connection, and reads the same thread's `includeTurns=false` snapshot from a second attach, recording only content-free identity and status. The explicit `thread/resume` subscription leg is recorded as typed evidence rather than asserted, because installed Codex 0.150.1 answers `thread/resume` only for a thread whose rollout already exists.
|
|
367
|
+
- `make test`: Codex hook trust-state preservation makes the managed marker block a definition-only ownership boundary rather than a byte range projmux may rewrite. `TestAIIntegrateCodexPreservesTrustStateInsideManagedBlock`, `TestAIIntegrateCodexPreservesStateInEveryMarkerPlacement`, and `TestAIIntegrateCodexDryRunOnConvergedTrustedConfigIsEmptyDiff` pin exact `[hooks.state]` and unknown-table preservation before, inside, and after the markers plus a byte no-op repeat; `TestManagedIngestConvergenceRepeatsWithoutTouchingCodexTrustState` extends the same contract to the automatic `config apply` / `make install` path with the config mode intact. `TestAIIntegrateCodexChangedHookIdentityDoesNotTransferTrust` and `TestAIIntegrateCodexRemovedHookKeepsUnknownAndUnrelatedTrustState` keep a changed or removed hook identity from inheriting or minting a hash while the untouched hooks keep theirs, and `TestProductionSourceNeverProducesCodexTrustState` audits that no production file names `trusted_hash` or `hooks.state` at all. `TestAIIntegrateCodexRecoversUnmarkedProjmuxHookWiring` and `TestManagedIngestConvergenceRecoversUnmarkedCodexHookWiring` adopt wiring whose markers a provider re-serialization dropped instead of failing `config apply`, while `TestAIIntegrateCodexStillRefusesHandWrittenUnmanagedHooks` and `TestAIIntegrateCodexRefusesUnmarkedRecoveryThatWouldRenumberHooks` keep the pre-existing refusal for hooks projmux did not author and for any adoption that would renumber a foreign hook entry.
|
|
368
|
+
|
|
369
|
+
- `make test`: Codex endpoint broker core Phase 1 adds the dark `internal/integrations/agents/codexbroker` package: one shared app-server connection, many fenced per-thread bindings, and a reconnect supervisor that reaches the endpoint only through an injected opener whose production default is the Phase 0 attach seam. `TestConcurrentBindingsRouteOutOfOrderResponsesAndInterleavedEventsToTheExactThread` runs two bindings over one in-memory endpoint whose control responses come back in the reverse order to the requests and whose notifications interleave: each response reaches exactly the caller that issued it, each notification reaches exactly the thread it names, and the shared connection ends with one connect and zero disconnects. `TestSlowBindingBacklogOverflowRevokesOnlyThatBindingAndKeepsTheSharedConnection` overruns one binding's bounded queue by exactly one event and requires `resync-required` on that binding alone, its stream closed after the events that fit rather than silently gapped, and the fast binding, the shared connection, and the open-attempt count untouched. `TestLongDisconnectReconnectsWhileABindingRemainsAndFencesTheOldEpochOut` injects a 3.75s outage through the fake clock (250ms + 500ms + 1s + 2s of capped jittered backoff across four refused attaches), reconnects while the binding remains, and then proves the retired epoch inert: its event is dropped as stale with zero fan-out and a fence minted on it refuses with `stale-connection-epoch` and zero requests on either connection, while the current fence still writes; unbinding the last binding freezes the open-attempt count and leaves no scheduled wait. `TestReconnectSnapshotAndLiveEventsMergeIntoOneOrderBehindTheBarrier` closes the snapshot/live race matrix - live events entirely before the snapshot, straddling it, and entirely after it - as one continuous snapshot-then-buffered-then-live order, and holds control authority shut for the whole barrier: `ControlAuthority` refuses `control-not-open` and a mutation attempted there writes zero bytes and enters no ledger row. `TestBarrierSnapshotFailureRevokesOnlyTheRefusedThread` splits the barrier's fault surface, so a live endpoint refusing one thread revokes only that binding with `snapshot-unavailable` while a disconnect during the snapshot leaves the binding intact for the next epoch's barrier. `TestMutationAtADisconnectBoundaryEndsIndeterminateWithZeroResends` pins the write ledger: an in-flight mutation cut by a disconnect ends `indeterminate` with one recorded attempt, the replacement connection receives zero requests, the resend counter stays at zero, and an endpoint that answers with an error stays classified `refused` rather than unknown. `TestThreadlessNotificationFansOutToNoBindingAndAttributesToNoAgent` runs the threadless fixtures - no params, a payload with no thread id, a blank thread id, and malformed params - through both the pure `AttributeNotification` seam and the live broker for zero binding fan-out and zero Agent attribution, with a following attributed event proving the stream was reachable throughout. `TestApprovalLeaseIsAnsweredOncePerConnectionAndDiesWithIt` binds the raw JSON-RPC request id, the thread, and both epochs into one lease, spends it once, refuses the duplicate, a forged thread, and an id-less lease, and shows connection replacement revoking it with zero answers on the new wire. `TestBrokerLifecycleRefusalsAndEpochsAreClosed` closes the construction and lifecycle table - unknown endpoint key, missing opener, blank thread id, duplicate bind, bind after close - and requires a rebound thread to take a fresh binding epoch that refuses the retired fence. `TestReconnectBackoffStaysInsideItsCappedJitterBounds` holds every attempt and jitter pair inside the half-base floor and the five-second cap, monotonically. `TestConcurrentBindUnbindReconnectAndSlowConsumerAreRaceAndLeakFree` runs binds, unbinds, connection replacement, delivery, and deliberate slow consumers together under `-race` and requires an idempotent concurrent `Close` with no goroutine left behind. `TestBrokerRetainsNoProviderContent` audits the retained surface as closed field sets, keeps every refusal, outcome, and origin code a bare content-free token, renders a typed refusal as its code alone over a path-bearing cause, and requires a prompt-bearing mutation and a payload-bearing event to leave no byte in the diagnostics or the ledger. `TestBrokerImportsNoRegistryTmuxOrCLIPackage` is the reverse-dependency audit: the package's only in-repo import may be `codexappserver`, so no Registry, tmux, or CLI package can reach back into the endpoint layer.
|
|
370
|
+
|
|
371
|
+
- `make test`: Codex endpoint broker runtime Phase 2 gives the dark `internal/integrations/agents/codexbroker` package an OS-level runtime: a per-state-domain singleton host, an owner-private discovery contract, and an authenticated content-free local IPC protocol, plus the minimal hidden `internal codex-broker serve|probe` entrypoint in `internal/app` that hosts one and reaches one. `TestConcurrentClientsShareOneRuntimeProcessAndOneUpstreamConnection` starts four separate client processes, holds them at one barrier, and releases them together against one empty state domain: the ledger the runtime shares with the parent must show exactly one published runtime, exactly one upstream connection, and one snapshot per client, with every client reporting the same runtime identity, because the socket bind is the singleton mutex and the exclusive startup lock keeps a losing starter from reclaiming the winner's socket. `TestDiscoveryCredentialAndPermissionFailuresBindNothingAndWriteNothing` runs the adversarial matrix - a wrong credential, an absent credential, a foreign endpoint key, a malformed first frame, a record readable beyond its owner, a record replaced by a symlink, a record naming another endpoint, no record at all, and a relative or empty state domain - and requires zero bindings, zero upstream requests, and zero upstream snapshots after all of them; an unshared protocol window gets a runtime of its own because that refusal also starts a drain. `TestStaleRuntimeArtifactsAreReclaimedOnlyByExactOwnerProof` is the ownership fixture: a live runtime's socket is reused rather than replaced with a typed `host-live`, a socket left behind by a killed runtime is removed, a regular file or a symlink at the socket path is refused as `discovery-untrusted` and left byte-identical, and the last row varies the record's pid across live, absent, and foreign values to require an identical decision every time, because a reusable pid may not be ownership authority. `TestRuntimeCrashRevokesTheOldEpochBeforeANewRuntimeRestoresAuthority` kills a real runtime process out from under a bound client: the binding revokes with `host-unavailable` before anything can present its fence, its stream closes, `ControlAuthority` and `Submit` both refuse with zero added upstream writes, and `Ensure` then proves the abandoned socket stale, starts a replacement with a different runtime identity, and restores authority only through a fresh snapshot, where a foreign epoch still refuses and writes nothing. `TestRestoredAuthorityOpensOnlyBehindTheSnapshotBarrier` holds the runtime's snapshot open and requires a bound-but-unopened client to refuse `control-not-open` with zero endpoint calls until the snapshot crosses the socket. `TestLastBindingRemovalIdlesTheRuntimeOutAndLeavesNoArtifact` keeps a held binding alive well past the idle bound, then requires the runtime to stop after the last binding is removed, remove exactly the socket and record it published, and leave no goroutine behind. `TestIncompatibleClientDrainsTheRuntimeWithoutSeveringLiveBindings` is the rolling binary replacement matrix: overlapping version windows negotiate down and reuse the running runtime in both directions, while a disjoint window is refused by name with `drain-required` without replacing the runtime or its socket, the live binding keeps delivering and keeps control authority through the drain, a new bind is refused so the drain stays finite, and only after the last binding drains does a replacement start under exact owner proof with a new runtime identity. `TestRuntimeStateAndDiagnosticsRetainNoProviderContent` drives a prompt-bearing mutation and a payload-bearing event across the socket and then searches every file the runtime persisted in its state domain for them, and pins `HostStats` as a closed set of tokens and counters. `TestRuntimeProtocolNegotiationAndFrameBoundsAreClosed` closes the negotiation table and requires an oversized frame to be refused on write with zero bytes emitted and on read. `TestInternalCodexBrokerServePublishesAndIdlesOutWithoutTouchingAnyDaemon` runs the real entrypoint against an isolated state domain: it publishes an owner-only record, reports readiness, and idles out without reaching any Codex daemon, because the broker opens its upstream connection only once a binding exists. `TestInternalCodexBrokerRouteStaysHiddenPlumbingWithNoPublicSurface` is the scope audit - no public route gains a broker spelling, the runtime stays inside the hidden `internal` namespace and out of the canonical command projection, every malformed invocation is a usage error, and `probe --no-start` refuses with `host-unavailable` while creating nothing outside the broker artifact directory. `TestCodexBrokerStateDomainFollowsTheProjmuxStatePaths` pins the singleton to the installation's state directory rather than to a working directory or a pane.
|
|
372
|
+
- `make test`: Codex native lifecycle and control cutover Phase 3 makes the endpoint broker the product's endpoint producer. `TestBrokerBoundAgentsShareOneUpstreamConnectionWithIndependentBindingEpochs` binds a prompted-create Agent and a stored-resume Agent through the real runtime host and requires one upstream connection between them, one shared connection epoch, two different binding epochs, and interleaved events that reach only the thread that named them. `TestBrokerThreadlessAndForeignEventsReachNoExactAgentBinding` runs the ambiguous fixtures - no thread id, a blank one, a foreign thread, and a foreign thread sharing the bound Agent's working directory - and requires the exact binding to receive none of them while a following attributed event proves the stream stayed live. `TestBrokerEpochRotationFencesTheOldEpochOutWithZeroEndpointWrites` replaces the connection under a live binding: the retired epoch's stream closes, its steer and its approval answer are both refused, the replacement connection records zero steer requests and zero answers from it, the binding epoch survives the reconnect unchanged, and the replacement epoch starts a turn normally. `TestBrokerRuntimeLossEndsTheEpochAndRefusesEveryLateMutation` takes the runtime away from a bound client and requires the epoch to end before any caller can present its fence, every late mutation and answer to be refused with zero endpoint writes, and a reopen against an unpublished runtime to refuse rather than start one. `TestBrokerDisconnectEndsTheEpochBeforeAnyReplacementBarrier` covers the promptness half of the disconnect contract: a binding survives an outage so the broker can keep reconnecting, so the binding's out-of-band suspension - not the ordered stream, which has nothing to say until the next barrier - retires the live epoch immediately, refuses its late mutations with zero endpoint writes, and keeps the stale barrier from being re-served while the endpoint is gone. `TestBrokerEpochBacklogOverflowEndsOnlyThatEpochStream` overruns one Agent's bounded stream and requires that Agent alone to be cut to a resync while its sibling on the same shared connection keeps receiving its own events. `TestBrokerApprovalLeaseAnswersTheExactRawRequestExactlyOnce` carries an inbound server request through the broker to the app layer with its byte-faithful raw id and its normalized rendering intact, decodes it with the existing envelope decoder, answers it once, and requires the repeat to be refused with exactly one entry in the endpoint's answer ledger. `TestBrokerEpochReadsTheExactLifecycleSnapshotThroughItsOwnFence` pins the control epoch's state input to the same fence its mutations use. `TestNativeLifecycleProducerIsExactlyOnePerActivationGeneration` and `TestBrokerProducerKeepsRecoveringPastTheLegacyAttemptCeiling` are the single-producer and persistent-reconnect audits: the producer decision is total and never yields both, and a producer that owns its own reconnect keeps recovering well past the legacy six-attempt budget without ever publishing the terminal `reconnect-exhausted` fallback, while that budget stays byte-identical for the producer that still needs it. `TestBrokerCutoverPreservesTheHookFallbackVocabulary` is the compatibility audit for the authority sources, the hook-suppression rule, and the pre-mutation convergence to `provider-hook`. On the endpoint side, `TestNativeCreateAndResumeAttachToTheExactCurrentUnmanagedEndpoint` runs the real create and resume paths against a fake Codex whose every argv is recorded and requires an exact-current endpoint the official manager does not own to accept one `thread/start`, one `turn/start`, and one `thread/resume` with a daemon start/stop/restart/kill/bootstrap/remote-control/login/config argv count of zero, while `TestNativeCreateAndResumeKeepSafeFallbackOnEveryRefusedAttachRow` keeps a skewed and an ownership-unknown endpoint refusing before the connection opens, as a safe fallback carrying its typed reason and with zero thread or turn requests. `TestEndpointAttachAuthorityIsTheOnlyWidenedProductRow` walks the availability x ownership x version table behind the product's own source and availability preconditions and requires exactly one row to differ from the retired native-action gate: the ready, exact-current, unmanaged one. `TestInstalledIsolatedBrokerNativeBindingSmoke` is opt-in through `PROJMUX_CODEX_CUTOVER_SMOKE_ROOT` with a matching contained `CODEX_HOME`, an isolated state domain, and inherited `TMUX`/`TMUX_PANE` stripped, and it is the only check that runs the cutover against a real Codex app-server: a direct `codex app-server --listen unix://` under that contained `CODEX_HOME`, which upstream reports as running with no daemon backend. It requires that endpoint to be unmanaged, exact-current, attach-allowed, and to carry no daemon lifecycle authority; runs the product's own prompted create there so the thread's rollout materializes; binds that exact thread through the product session over a runtime it publishes itself; starts one turn through the resulting broker epoch and requires the epoch's own fenced snapshot to show that exact turn in progress before steering it through the same wire; interrupts it; and then requires the runtime to leave no artifact behind. Every `codex` argv the whole path ran is recorded by a PATH shim the test installs, and the run fails unless all of them are `app-server proxy` or `app-server daemon version`, which is the zero-daemon-lifecycle-mutation half of the acceptance against a real endpoint rather than a fake one.
|
|
373
|
+
- `make test`: Codex interactive-only mode and native-required prompted create makes an unproven native authority a typed refusal instead of a silent plain-CLI Agent. `TestPromptedNativeCodexCreateIssuesOneTurnAndNeverRepeatsThePromptInPaneArgv` and `TestStartDefaultThreadEmptyAndPromptedRequestCounts` pin one `thread/start` plus one `turn/start` per payload with the prompt absent from Pane argv and from the Registry. `TestUnavailableNativeCreateRefusesInsteadOfSilentlyCreatingAPlainAgent` and `TestUnavailableNativePickerResumeRefusesInsteadOfRebindingOntoTheRolloutLane` close the pre-mutation refusal matrix for the two Agent-creating routes at zero Registry and zero tmux mutation, `TestIndeterminateNativeCreateRefusesASecondLaneAndWritesZero` keeps the post-mutation row refusing without offering any second lane, and `TestUnavailableNativeResumeKeepsTheStoredConversationOnTheProviderResumeLane` holds the boundary at `agent resume`, which creates no Agent and keeps its safe fallback to one provider resume of the stored conversation. `TestInteractiveOnlyIsTheOnlyPlainCodexLaneAndBothSpellingsAreEquivalent` pins flag acceptance, manifest and rendered help parity, and byte-equal output across `create agent --provider codex --interactive-only` and `create codex --interactive-only`, plus the exit-2 non-Codex-provider refusal at zero transactions. `TestStartDefaultThreadDeliversAdditionalRootsOrFailsClosed` is the roots capability matrix: the exact cleaned list on a negotiated connection, a fail-closed typed refusal when the endpoint cannot carry it, and nothing on the wire for an empty list. `TestDefaultNativeCodexFanOutRefusesWithZeroMutationsAndInteractiveOnlyKeepsCardinality` refuses a prompted multi-Window native create with zero threads, Panes, Registry writes, and tmux objects while keeping the `--interactive-only` and empty-prompt fan-out cardinality. `TestEmptyPromptCodexCreateIsByteForByteUnchangedByTheNativeRequiredGate` and `TestClaudeAndAntigravityLifecycleAndHookContractAreUnchangedByTheNativeGate` guard the deferred empty-prompt boundary and the unchanged Claude/Antigravity hook activation and fan-out contract, and `TestCodexNativeLaunchOutcomeTableIsClosed` keeps the closed outcome table describing exactly these rows.
|
|
374
|
+
- `make test`: Codex per-Agent native observer retirement makes the endpoint broker the only native lifecycle, control, and approval producer, and projects its content-free telemetry onto the diagnostics surfaces. `TestNativeLifecycleProducerIsExactlyOnePerActivationGeneration` is the source audit that replaced the old producer selection: `internal/app` opens no app-server proxy of its own anywhere, and the native lifecycle observer is built with exactly one connection opener, so there is no second branch a dual-write could take. `TestRetiredCodexAppServerWatchRouteIsRefused` proves the retired producer cannot be revived by argv, hook, or script: the `codex-appserver-watch` ingest route is gone and the surviving route names the broker binding it consumes. `TestBrokerProducerKeepsRecoveringPastTheLegacyAttemptCeiling` and `TestRetiredObserverRecoveryBackoffIsCappedAndNeverExhausts` replace the fixed six-attempt exhaustion budget with the whole reconnect contract: recovery is bounded only by the capped backoff and by the exact binding, the terminal `reconnect-exhausted` fallback is never published again, the reconnect gap writes zero bytes upstream, and the only exits are a replaced binding and a cancelled context. `TestCodexNativeObserversSharingOneEndpointRecoverIndependently` keeps that containment per activation: one activation reconnecting onto its own replacement projects nothing onto its sibling and cannot terminate it. `TestRuntimeTelemetryCountsOneConnectionForConcurrentBindings` is the number the retirement is measured by - three bindings, one upstream connection, one live client session, and a telemetry frame that reaches no endpoint - and `TestRuntimeTelemetryNamesEachBindingScopedFault` keeps queue eviction, a refused reconnect snapshot, and a fenced-out epoch separable instead of collapsed into one revocation count, with a voluntary unbind never counted as a fault. `TestBrokerDiagnosticsReadCreatesNothing` and `TestBrokerDiagnosticsReportTheRestingAbsentRuntime` fix the read-only contract of that projection: asking about the broker publishes no discovery artifact, reclaims nothing, starts no runtime, and reports a machine with no live native Agent as the resting `absent` state under its closed refusal rather than as a fault. `TestBrokerDiagnosticsProjectOneConnectionAndTypedFaults` and `TestCodexBrokerDiagnosticIsContentFree` pin the rendered value: one connection per effective endpoint regardless of binding count, a down connection distinguished from one that never existed, and a closed field set carrying no provider content and no location, with an unclassified failure never leaking its message. `TestDeclaredPlainCodexLaneNamesOnlyTheByDesignLanes`, `TestDeclaredLaneVocabularyIsClosedAgainstUnknownPaneValues`, and `TestDeclaredLaneReadsBackFromTheExactPaneOption` give the two by-design plain-CLI lanes - an empty-prompt default create and an explicit `--interactive-only` - a typed declaration that survives the round trip through the exact pane option, while a forged or unknown value is refused so a declaration cannot hide a real fallback. `TestManagedCodexAuthorityCensusSeparatesDeclaredFromUnexplainedFallback` is the acceptance surface: the declared lanes are counted apart from the unexplained native fallback that the native-authority contract requires to be zero, sibling providers never enter the census, and the classes partition the managed Codex Agents exactly. `TestPlainCodexCreatesWriteTheirDeclarationOntoTheExactPane` closes the write half through the real create route and the real pane binder, so a create route that stopped passing the declaration - or a binder that stopped writing it - would fail rather than silently return every by-design plain Agent to the unexplained count.
|
|
375
|
+
- `make test-e2e-shards`: the Linux e2e suite is scheduled one shard per runner. `scripts/test-e2e-docker.sh` accepts `PROJMUX_E2E_LINUX_SHARD=<shard>` and `PROJMUX_E2E_SUITE=codex-lifecycle|npm-staging` as mutually exclusive single-suite selectors that reuse the existing `E2E_SCENARIO` replay plumbing and derive their exact terminal inventory from the same `test/e2e/linux-shards.tsv` row the replay router reads; setting none leaves the default four-shard parallel run plus both suites unchanged. `test/e2e/shard-contract.sh` pins the CI topology to that manifest: `e2e-linux` matrixes exactly the manifest shards and `e2e-suite` the codex/npm pair, both `fail-fast: false` on their own `ubuntu-latest` runner with their own `timeout-minutes` and four uniquely named evidence artifacts, and both are `needs` and `--required` children of the aggregate `Test` gate so no shard result can be hidden. It also pins the thin `E2E Tests` job that keeps reporting the status-check name the branch ruleset requires: it is `if: always()` and evaluates both matrix results through the same `required-gate.py`, because a required context that is never reported stays pending forever instead of failing, so a skipped or missing job there would deadlock merges rather than turn a check red.
|
|
376
|
+
|
|
364
377
|
## When To Update This List
|
|
365
378
|
|
|
366
379
|
- A feature moves between unit, integration, and e2e coverage levels.
|
|
@@ -380,6 +393,8 @@
|
|
|
380
393
|
|
|
381
394
|
- `make test` / `make test-integration` / `make test-e2e`: Save and Quit policy Phase 0 freezes one complete exact-route Registry/resource-graph observation and captures every `StatusLive` managed Project latest snapshot in Project UID/session order before the existing guarded shutdown. C-1 enforcement is `TestPlanQuitSnapshotBatchSelectsOnlyLiveManagedProjectsInStableOrder`, `TestQuitSavePreflightUnavailableOrConflictPerformsZeroCaptureAndKill`, `TestQuitSaveBarrierContinuesAfterMiddleFailureAndDoesNotKill`, `TestQuitSaveBarrierRetryRecapturesEveryTargetThenRunsOneGuardedShutdown`, `TestQuitSaveAndQuitIntegrationCapturesRegistryGraphBeforeGuardedShutdown`, and `TestQuitSaveBarrierKeepsNamedRegistryAndSiblingBytesInvariant`; missing-root, incomplete identity, marker/route/socket drift, 0/1/N, atomic validity, retry, named-snapshot, Registry, and sibling invariants remain fail-closed. C-2 enforcement is `TestQuitUnsavedActionsAndFlagsAreSnapshotFreeAndByteEquivalent`, `TestQuitCommandPickerActionsHaveEnglishKoreanMeaningParity`, and `TestQuitCommandCancelAndCloseDoNotShutdown`: explicit unsaved quit and existing `--yes`/`--force` retain zero snapshot I/O and exact guarded-shutdown compatibility while Cancel/Esc write nothing. The installed smoke strips inherited `TMUX`/`TMUX_PANE`, uses one unique `TMUX_TMPDIR` with exact app and sibling sockets, materializes two managed Projects plus Home/control and ephemeral sessions with auto-save off, requires two refreshed latest snapshots before app death, excludes every non-Project session, preserves named/Registry/sibling bytes, and cleans only queried socket paths proven below the smoke root.
|
|
382
395
|
|
|
396
|
+
- `make test` / `make test-e2e`: Sidebar popup runtime-stop anchor authority gives the `sessionizer-sidebar` popup one anchor transport end to end. The generated Alt-1 launcher already passes the origin Pane as a `switch --ui=sidebar --anchor %N` operand and supplies no `__PROJMUX_RUNTIME_ANCHOR_PANE`, so both stop consumers the sidebar reaches now read that operand: `stopManagedProjectSession` resolves its route through `resolveInvocationRuntimeMutationRouteWithAnchor`, and `executeUnmanagedRuntimeStop` takes the anchor as an explicit argument that non-popup callers leave blank. `TestSwitchSidebarKillStopsManagedProjectFromAPopupWithoutTMUXPANE` and `TestSwitchSidebarKillStopsUnmanagedSiblingFromAPopupWithoutTMUXPANE` pin one exact `kill-session -t $N` plus the fallback client handoff in the real popup shape -- a valid inherited `TMUX` receipt with `TMUX_PANE` and the private anchor env both blank -- for the managed Project row and the discovered unmanaged sibling; `TestSwitchSidebarKillWithoutFallbackRefreshesWithoutWriting` keeps a sidebar with no other live session on a plain refresh with zero writes; `TestSwitchSidebarKillWithoutAnchorOperandRefusesWithZeroWrites` and `TestSwitchSidebarKillRefusesAMalformedAnchorOperand` keep a missing or non-`%N` operand a typed route refusal rather than a silent no-op, with tmux writes at zero and the managed route refusing before the fallback handoff; and `TestSidebarPopupAnchorSupplierAndStopConsumerShareOneTransport` fails if the launcher stops emitting the operand or a consumer stops reading it. `TMUX_PANE` is never promoted to anchor authority, and the route authority rules, `session-popup`'s private env transport, and every other popup mode are unchanged. L11's two sidebar Ctrl-X drivers now run with `TMUX_PANE` unset so they exercise the operand instead of a shell export.
|
|
397
|
+
|
|
383
398
|
## E2E Required-Gate Evidence
|
|
384
399
|
|
|
385
400
|
- `make test-e2e-coverage` is the maintained AGS-OEDR inventory gate. It
|
|
@@ -424,6 +439,15 @@
|
|
|
424
439
|
Codex, and npm consumers. Local Security is the exact three-way
|
|
425
440
|
`make security` topology; CI's required branch-protection context is the
|
|
426
441
|
fail-closed aggregate named `Test`.
|
|
442
|
+
- `test/lib/smoke.sh` owns every scenario wait through `smoke_wait_until
|
|
443
|
+
<seconds> <description> <command>`; `test/e2e/` holds no raw `for _ in
|
|
444
|
+
{1..N}` polling loop. A wait that expires fails with the description it was
|
|
445
|
+
given plus the budget applied, elapsed time, attempt count, and last predicate
|
|
446
|
+
status, so no scenario can enter its next assertion with the condition still
|
|
447
|
+
unmet. `E2E_WAIT_SCALE` multiplies every budget for a slow runner.
|
|
448
|
+
`test/e2e/reliability-contract.sh` F08/F09 pin both halves: a deliberately slow
|
|
449
|
+
fixture times out with its own description and state dump and then passes once
|
|
450
|
+
the scale buys it time, and the timeout instant itself moves with the scale.
|
|
427
451
|
- `test/e2e/evidence-contract.sh` (`make test-e2e-contract`) and
|
|
428
452
|
`test/e2e/reliability-contract.sh` (`make test-e2e-reliability`) keep the persisted
|
|
429
453
|
`projmux.e2e-attempt/v1` evidence and success result hash stable while adding
|
|
@@ -435,14 +459,74 @@
|
|
|
435
459
|
exact tmux state plus sanitized hashed tails; denylist checks reject token,
|
|
436
460
|
raw HOME/path, full Registry, and raw command leakage. Normal scenario meaning,
|
|
437
461
|
one-build/four-shard topology, and `.bin/e2e-evidence` retention are unchanged.
|
|
438
|
-
- `
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
462
|
+
- `test/e2e/evidence-contract.sh` also pins failure-evidence preservation. A
|
|
463
|
+
failing attempt folds the emitted terminal record and the L06/L08/L16
|
|
464
|
+
diagnostic into the persisted `projmux.e2e-attempt/v1` artifact as the additive
|
|
465
|
+
optional `terminal_line`, `terminal_status`, `terminal_source`, and
|
|
466
|
+
`diagnostic` fields; a begin/pass record keeps its exact prior field set and
|
|
467
|
+
bytes. Degraded input no longer discards the whole attribution: a zero
|
|
468
|
+
`BASH_LINENO[0]`, an out-of-range wait status, and a non-allowlisted source
|
|
469
|
+
path each drop only their own field and mark the terminal record
|
|
470
|
+
`attribution=partial` while scenario, phase, owner, shard, and replay survive.
|
|
471
|
+
The `PROJMUX_E2E_INTENTIONAL_ZERO_LINE` fixture drives that path through the
|
|
472
|
+
real harness, and CI preserves the attempt evidence subtree — excluding
|
|
473
|
+
`binary/` — as a downloadable artifact on both failing and passing e2e runs.
|
|
474
|
+
- `test/e2e/evidence-contract.sh` also pins observed failure classification. The
|
|
475
|
+
`class` field is derived, not declared: the recorder compares the attempts
|
|
476
|
+
already recorded for the same `binary_sha256` under `--evidence-root` and only
|
|
477
|
+
falls back to a caller's `--class` when they say nothing. A build seen both
|
|
478
|
+
failing and passing is nondeterministic and splits by whether the source of
|
|
479
|
+
that nondeterminism is ours — `flake` for a scenario we own, and
|
|
480
|
+
`unowned-nondeterminism` for one like `C01` that observes the real Codex model
|
|
481
|
+
— so a later quarantine policy never sets a deadline nobody here can meet. A
|
|
482
|
+
failure reproduced on the same build is `deterministic-regression`; a failure
|
|
483
|
+
nobody has retried is `unrepeated-failure` and no longer borrows the
|
|
484
|
+
reproduction claim. Where a derivation happened the record carries the
|
|
485
|
+
additive optional `class_basis` and `prior_outcomes`; where it did not, the
|
|
486
|
+
record keeps its exact prior field set and bytes. `e2e-evidence.py classify`
|
|
487
|
+
exposes the predicate as a truth table, and `e2e-evidence.py flake-rate <dir>`
|
|
488
|
+
reports per-scenario flake rate over downloaded attempt evidence plus an
|
|
489
|
+
`E2E_FLAKE_FLIP` line for every stored class the other attempts contradict.
|
|
490
|
+
L06 diagnostics are derived the same way: `attribution` names what the racers
|
|
491
|
+
were observed to do instead of always claiming lock exhaustion, `acquire` is
|
|
492
|
+
no longer unconditionally `contended`, and `lock_age_ms` is paired with
|
|
493
|
+
`lock_observed` so an unread lock file cannot report a scenario timer as lock
|
|
494
|
+
age. That renamed payload travels as `projmux.e2e-diagnostic/v2` while the
|
|
495
|
+
unchanged L08/L16 payloads stay `v1`, and evidence retained from before the
|
|
496
|
+
rename still validates and still aggregates. `PROJMUX_E2E_ATTEMPT` now defaults to `GITHUB_RUN_ATTEMPT`, so a rerun's
|
|
497
|
+
evidence is no longer written as attempt 1.
|
|
498
|
+
- `make test` / `go test -race` / `make test-e2e`: the Registry mutation lock
|
|
499
|
+
succeeds on a deadline, not on an attempt budget. `acquireLockWithDeadline`
|
|
500
|
+
blocks on `flock(LOCK_EX)` against a persistent `registry.json.flock`
|
|
501
|
+
descriptor so the kernel owns the wait queue, keeps writing the legacy
|
|
502
|
+
`registry.json.lock` O_EXCL marker for the compatibility window an older
|
|
503
|
+
install still observes, and treats a marker as stale only when its recorded
|
|
504
|
+
pid is gone rather than after 30s of wall clock. The separate recovery lock
|
|
505
|
+
(`acquireRecoveryLock`) keeps its 200-attempt budget and wall-clock stale
|
|
506
|
+
window. `TestRegistryLockFailsOnItsDeadlineNotOnAnAttemptBudget` pins
|
|
507
|
+
`ErrLockTimeout` and the absence of any attempt count in the failure,
|
|
508
|
+
`TestRegistryLockDeadlineIsMeasuredOnTheInjectedClock` spends a whole budget
|
|
509
|
+
in simulated time, `TestRegistryLockReclaimsAMarkerOnlyWhenItsOwnerIsGone`
|
|
510
|
+
reclaims a reaped owner at once while a live, empty, malformed, or unreadable
|
|
511
|
+
owner is never reclaimed, `TestRegistryLockKeepsMutualExclusionWithAMarkerOnlyInstall`
|
|
512
|
+
covers the compatibility window in both directions, and
|
|
513
|
+
`TestRegistryLockGrantsEveryWriterUnderDeliberateContention` plus the fixed-clock
|
|
514
|
+
`TestConcurrentUpdatesSerializeThroughTheRegistryLock` require every contending
|
|
515
|
+
writer to be granted with no overlapping critical sections. The degraded-mode
|
|
516
|
+
gate in front of an ordinary mutation is now a suspicion confirmed under the
|
|
517
|
+
lock rather than an immediate refusal: a commit publishes the initialized
|
|
518
|
+
marker before it renames the staged registry into place, so an unlocked
|
|
519
|
+
observer could read a healthy neighbour's in-flight commit as state loss and
|
|
520
|
+
refuse a writer that never reached the lock.
|
|
521
|
+
`TestAConcurrentWriterIsNotRefusedInsideAnotherCommitsMarkerWindow` builds that
|
|
522
|
+
window directly and requires the second writer to wait through it. The
|
|
523
|
+
`Race Tests` CI job runs the metadata, notify, and recent-window packages
|
|
524
|
+
under `-race`.
|
|
525
|
+
The real-tmux L06 burst still requires all eight exact-socket creates to
|
|
526
|
+
converge on one Window with nine unique mirrored Panes and no lock or staged
|
|
527
|
+
residue; it is a regression guard, not the evidence for the lock change.
|
|
445
528
|
- `make test` / `make test-integration`: attention omitted-target convergence
|
|
529
|
+
- `make test` / `make test-e2e`: Closed-Project startup mode selection is one adjudication shared by both entry points. `switch sidebar-open` re-execs across a process boundary, and before this change the picker-off sidebar always emitted `continue` and the re-exec trusted that token verbatim, so an unregistered root on a fresh install failed in `ContinueProject` with `continue project unavailable: no usable snapshot`. `defaultProjectStartupMode` now owns the picker-off decision -- the Settings/runtime sentinels, the `$HOME` guard, and the single `ProjectRegistered` read -- and all three closed-Project entry points call it: `openProjectTarget`, the sidebar emit point `openProjectTargetPathFromSidebar` that builds the `--mode` token, and `runSidebarOpen`, which re-adjudicates a `continue` that crossed the re-exec boundary. `TestProjectStartupModeSelectionIsOneDecisionAcrossEntryPoints` drives the (registered root) x (picker on/off) x (explicit picker choice) table through the in-process open, the sidebar emit point, and the sidebar continuation, requiring the emitted `--mode` token and the mode the continuation acts on to agree with the in-process choice. `TestSidebarOpenPromotesUnregisteredRootToFreshWhenPickerIsOff` is the regression guard, `TestSidebarOpenKeepsRegisteredRootOnContinue` keeps a registered root and its retained topology on `continue`, `TestSidebarOpenHonorsExplicitPickerChoice` proves an explicit picker-on choice is never promoted, `TestSidebarOpenNeverDemotesAnArrivingFreshMode` keeps the re-adjudication one-directional, `TestSidebarOpenSurfacesRegistrationReadFailure` refuses to answer an unreadable Registry with a mode, and `TestSidebarOpenContinueOnUnregisteredRootKeepsTheSnapshotRefusal` pins the no-usable-snapshot message for the continue that is still reachable by explicit choice. The L11 closed-startup e2e keeps its direct `--mode fresh` / `--mode continue` execution paths and adds step 9, `Closed Project startup mode selection`, which covers the arrival half of the decision: the picker-off sidebar now emits `fresh`, so the step deliberately forwards the legacy neutral `continue` token an older client can still send, against an unregistered root in an empty Registry on its own isolated socket/HOME/XDG/TMUX_TMPDIR, and requires the continuation to re-decide it so the Project is minted and its session opened rather than the snapshot refusal. The emitted half is covered by the unit tests through `sidebarEmittedStartupMode`.
|
|
446
530
|
is enforced by `TestAttentionMutationOmittedTargetMatchesExplicitPaneLedger`,
|
|
447
531
|
`TestAttentionMutationOmittedTargetRefusesWithoutExactInvocationPane`, and
|
|
448
532
|
`TestAttentionMutationHelpUsesOptionalPaneCatalogUsage`; generated/reference/
|
package/docs/cli-guide.md
CHANGED
|
@@ -924,10 +924,11 @@ lifetime total; `logs.recent-errors.bounded` means older errors were omitted.
|
|
|
924
924
|
The probe captures at most 4 KiB, generated config inspection reads at most
|
|
925
925
|
1 MiB, and the journal seam reads at most 5 MiB.
|
|
926
926
|
Symlinks and non-regular inputs are rejected without following or blocking on
|
|
927
|
-
them.
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
927
|
+
them. The closed `privacy-unverified` warning stays in the finding schema for
|
|
928
|
+
a path whose privacy cannot be established from its mode bits, but no supported
|
|
929
|
+
platform reports it: Linux and macOS are the only build targets and POSIX mode
|
|
930
|
+
bits are authoritative on both, so a readable path always resolves to a
|
|
931
|
+
private or insecure classification. Doctor never changes permissions.
|
|
931
932
|
|
|
932
933
|
JSON reports have integer `schema_version: 2`. An unfiltered report retains the
|
|
933
934
|
existing typed `dependencies`, `ai_notify_integrations`,
|
|
@@ -1366,6 +1367,17 @@ the resolved `claude`, `codex`, or `agy` executable inside the managed wrapper;
|
|
|
1366
1367
|
projmux still sets the context directory, tmux title, AI pane metadata, and
|
|
1367
1368
|
split layout.
|
|
1368
1369
|
|
|
1370
|
+
A prompted Codex create — provider `codex` with exactly one non-empty payload
|
|
1371
|
+
operand — requires native authority. If the app-server endpoint is not ready or
|
|
1372
|
+
attachable, if `--add-dir` roots cannot be negotiated, or if the selector
|
|
1373
|
+
resolves several Windows, the create refuses with zero Registry and zero tmux
|
|
1374
|
+
mutations instead of silently producing a plain-CLI Agent with no native turn
|
|
1375
|
+
control. `--interactive-only` is the one public spelling that asks for that
|
|
1376
|
+
plain Agent on purpose; it is Codex-only and equivalent on `create agent
|
|
1377
|
+
--provider codex` and the `create codex` shortcut. Empty-prompt creates,
|
|
1378
|
+
multi-operand payloads, `agent resume`, Claude, and Antigravity are unaffected.
|
|
1379
|
+
See [Codex Native-Required Create Migration](codex-native-required-migration.md).
|
|
1380
|
+
|
|
1369
1381
|
Automation callers get the new pane's handle from `-o pane-id` on the canonical
|
|
1370
1382
|
create routes: `projmux create agent --provider <p> --placement right -o pane-id`
|
|
1371
1383
|
and `projmux create pane --placement right -o pane-id` each print exactly the
|
package/docs/cli.md
CHANGED
|
@@ -443,8 +443,9 @@ Selectorless authority: `refusal` — there is no safe selectorless action; refu
|
|
|
443
443
|
projmux create project --root <absolute-path> [--name <name>] [--label key=value]... [-o <mode>]
|
|
444
444
|
projmux create window [--project <ref> | -p <ref>] [--name <name>] [--label key=value]... [-o <mode>] [-- <payload>]
|
|
445
445
|
projmux create pane [--project <ref> | -p <ref>] [--window <ref> | -w <ref>]... [--pane <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
|
|
446
|
-
projmux create agent --provider <provider> [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
|
|
447
|
-
projmux create codex
|
|
446
|
+
projmux create agent --provider <provider> [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--pane <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
|
|
447
|
+
projmux create codex [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
|
|
448
|
+
projmux create claude|antigravity [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--create-window] [--placement right|down] [-o <mode>] [-- <payload>]
|
|
448
449
|
projmux create notification --text <s> --target <SESSION[:WINDOW[.PANE]]> [--socket <s>]
|
|
449
450
|
projmux create snapshot
|
|
450
451
|
```
|
|
@@ -513,7 +514,7 @@ Create an Agent detached on an explicit Pane or the Window's exact shell or Agen
|
|
|
513
514
|
Selectorless authority: `natural-omitted` — omission resolves one predictable current resource or documented contextual read/scope; any selector replaces it.
|
|
514
515
|
|
|
515
516
|
```
|
|
516
|
-
projmux create agent --provider <provider> [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
|
|
517
|
+
projmux create agent --provider <provider> [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
|
|
517
518
|
```
|
|
518
519
|
|
|
519
520
|
Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`
|
|
@@ -545,7 +546,7 @@ Provider shortcut for create agent --provider codex
|
|
|
545
546
|
Selectorless authority: `natural-omitted` — omission resolves one predictable current resource or documented contextual read/scope; any selector replaces it.
|
|
546
547
|
|
|
547
548
|
```
|
|
548
|
-
projmux create codex [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
|
|
549
|
+
projmux create codex [--project <ref> | -p <ref>] [--cwd <path>] [--add-dir <path>]... [--interactive-only] [--window <ref> | -w <ref>]... [--pane <ref>]... [--selector key=value]... [--create-window] [--name <name>] [--label key=value]... [--placement right|down] [-o <mode>] [-- <payload>]
|
|
549
550
|
```
|
|
550
551
|
|
|
551
552
|
Output modes (`-o`): `uid`, `name`, `ref`, `metadata`, `json`, `pane-id`, `none`
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Codex Native-Required Create Migration (0.14.0)
|
|
2
|
+
|
|
3
|
+
0.14.0 makes native authority a requirement for a *prompted* managed Codex
|
|
4
|
+
create instead of something projmux attempts and silently gives up on. Where an
|
|
5
|
+
earlier release would quietly hand back a plain-CLI Codex Agent — one that looks
|
|
6
|
+
managed, carries no app-server thread binding, and answers no native turn
|
|
7
|
+
control — the create now refuses at the provider-mutation boundary and names the
|
|
8
|
+
one explicit way to ask for that plain Agent.
|
|
9
|
+
|
|
10
|
+
This note covers only that change. For update mechanics by installer type see
|
|
11
|
+
[Upgrading](upgrading.md); for endpoint readiness diagnosis see
|
|
12
|
+
[Troubleshooting](troubleshooting.md#codex-app-server-install-topology).
|
|
13
|
+
|
|
14
|
+
## There is nothing to migrate
|
|
15
|
+
|
|
16
|
+
Upgrading is the whole migration. Every existing resource keeps working as it
|
|
17
|
+
is:
|
|
18
|
+
|
|
19
|
+
| Surface | Change in 0.14.0 |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| Registry schema | none — `internal/core/metadata` is byte-identical across the release range |
|
|
22
|
+
| Existing Agents and Panes | none — no stored field is added, read differently, or backfilled |
|
|
23
|
+
| Configuration files | none — no key added, renamed, or removed |
|
|
24
|
+
| Post-create and Agent hook contract | none — no `PROJMUX_*` env var added, renamed, or removed, and no payload schema change |
|
|
25
|
+
|
|
26
|
+
No command has to be run before or after the upgrade for this change. What
|
|
27
|
+
changes is the answer a *new* prompted Codex create gives when native authority
|
|
28
|
+
cannot be proven.
|
|
29
|
+
|
|
30
|
+
## The shape that is now gated
|
|
31
|
+
|
|
32
|
+
Exactly one create shape is native-required. All five conditions must hold:
|
|
33
|
+
|
|
34
|
+
- provider is `codex`, and
|
|
35
|
+
- the payload is exactly one operand, and
|
|
36
|
+
- that operand is non-empty, and
|
|
37
|
+
- `--interactive-only` is not passed, and
|
|
38
|
+
- the create is not carrying a capability selection from the split-UI picker.
|
|
39
|
+
|
|
40
|
+
Anything outside that shape keeps its previous behavior unchanged. The closed
|
|
41
|
+
outcome table lives in `internal/app/codex_native_thread.go`
|
|
42
|
+
(`codexNativeLaunchOutcomeTable`) and is pinned by
|
|
43
|
+
`TestCodexNativeLaunchOutcomeTableIsClosed`.
|
|
44
|
+
|
|
45
|
+
## Calls that now refuse
|
|
46
|
+
|
|
47
|
+
### 1. Prompted create against an endpoint that is not ready or not attachable
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
projmux create codex -- "review the diff"
|
|
51
|
+
projmux create agent --provider codex -- "review the diff"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Before: native thread preparation failed as a safe fallback and the create
|
|
55
|
+
silently continued on the plain CLI lane, producing a managed Agent with no
|
|
56
|
+
native binding.
|
|
57
|
+
|
|
58
|
+
Now: the create refuses before the split, before the hook probe, and before the
|
|
59
|
+
Registry commit. Zero threads, zero Panes, zero Registry writes, zero tmux
|
|
60
|
+
objects. The refusal carries the typed reason from the endpoint (for example
|
|
61
|
+
`daemon-not-running`) and names `--interactive-only`. Exit code 1.
|
|
62
|
+
|
|
63
|
+
Fix it by making the app-server endpoint available — start with
|
|
64
|
+
`projmux doctor --section integrations --verbose` — or ask for the plain lane on
|
|
65
|
+
purpose with `--interactive-only`.
|
|
66
|
+
|
|
67
|
+
### 2. Prompted create with `--add-dir` against an endpoint that cannot negotiate roots
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
projmux create codex --add-dir /path/to/other-root -- "review the diff"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Additional writable roots travel on the upstream experimental API. Before, the
|
|
74
|
+
create connection never negotiated that capability, so roots always failed the
|
|
75
|
+
request; that failure was classified as a safe fallback and the create silently
|
|
76
|
+
dropped to the plain CLI lane.
|
|
77
|
+
|
|
78
|
+
Now a rooted create negotiates the capability on its own connection and delivers
|
|
79
|
+
the exact cleaned list. An endpoint that cannot answer the negotiated form
|
|
80
|
+
fails closed with reason `additional-writable-roots-unsupported` rather than
|
|
81
|
+
creating an Agent whose writable workspace is narrower than what was asked for.
|
|
82
|
+
Exit code 1.
|
|
83
|
+
|
|
84
|
+
A create with no `--add-dir` keeps the plain, non-negotiated connection exactly
|
|
85
|
+
as before.
|
|
86
|
+
|
|
87
|
+
### 3. Prompted create whose selector resolves several Windows
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
projmux create codex --window main --window side -- "review the diff"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
One create owns exactly one native thread, and a Registry rollback cannot delete
|
|
94
|
+
an app-server thread, so a prompted native fan-out has no atomic shape. Before,
|
|
95
|
+
every target dropped to the plain CLI lane. Now the fan-out is refused before
|
|
96
|
+
the first allocation, as a usage error (exit code 2) naming how many Windows the
|
|
97
|
+
selector resolved.
|
|
98
|
+
|
|
99
|
+
Narrow the selector to one Window, or pass `--interactive-only` to keep the
|
|
100
|
+
previous one-Agent-per-Window cardinality.
|
|
101
|
+
|
|
102
|
+
### 4. Resume picker: selecting an app-server-sourced Codex row
|
|
103
|
+
|
|
104
|
+
This is the split-UI resume picker (`Alt-7` / the resume selection surface), not
|
|
105
|
+
the `projmux agent resume` command.
|
|
106
|
+
|
|
107
|
+
When the picker's Codex rows come from a healthy app-server, selecting one of
|
|
108
|
+
those native rows resumes that exact thread through the native lane. Before, a
|
|
109
|
+
failed native resume preparation silently rebound the selection onto the rollout
|
|
110
|
+
CLI lane — it reported a resume while answering no native turn control. Now it
|
|
111
|
+
refuses. Exit code 1.
|
|
112
|
+
|
|
113
|
+
There is no `--interactive-only` escape hatch here: the operator picked an
|
|
114
|
+
existing conversation, not a launch mode. Rows that came from the rollout scan
|
|
115
|
+
rather than the app-server are unaffected and keep the current CLI lane.
|
|
116
|
+
|
|
117
|
+
## The escape hatch: `--interactive-only`
|
|
118
|
+
|
|
119
|
+
`--interactive-only` is the only public spelling that asks for a plain
|
|
120
|
+
interactive Codex Agent with no native thread binding, and it is the only way to
|
|
121
|
+
reach the plain CLI lane on purpose.
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
projmux create codex --interactive-only -- "interactive task"
|
|
125
|
+
projmux create agent --provider codex --interactive-only -- "interactive task"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- Both spellings are equivalent: identical flag acceptance, identical manifest
|
|
129
|
+
and rendered help, byte-equal stdout.
|
|
130
|
+
- The native controller is not consulted at all. No thread is created, no native
|
|
131
|
+
Pane state is bound, and the Agent keeps the existing hook activation contract
|
|
132
|
+
(`provider-hook`).
|
|
133
|
+
- The payload stays the provider's initial task on the CLI argv, exactly as
|
|
134
|
+
before.
|
|
135
|
+
- It gives up native turn control for that Agent — start, steer, interrupt, and
|
|
136
|
+
approval routing through the app-server. That is a deliberate reduced
|
|
137
|
+
capability, not a defect.
|
|
138
|
+
- It is Codex-only. Passing it to `--provider claude` or `--provider antigravity`,
|
|
139
|
+
or to the `create claude` / `create antigravity` shortcuts, is a usage error
|
|
140
|
+
(exit code 2) raised before any transaction opens, so nothing is created.
|
|
141
|
+
- It is a create-time flag and is not stored on the Agent. `projmux agent resume`
|
|
142
|
+
has no way to tell an interactive-only Agent apart later and does not treat one
|
|
143
|
+
specially.
|
|
144
|
+
|
|
145
|
+
## What did not change
|
|
146
|
+
|
|
147
|
+
| Surface | Behavior |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| `projmux agent resume` | unchanged. A stored Agent whose native resume cannot be proven keeps its existing safe fallback to one provider resume of the stored conversation. `internal/app/agent_resume.go` has a net diff of zero lines in this release. |
|
|
150
|
+
| Empty-prompt Codex create (`projmux create codex` with no payload) | unchanged byte-for-byte, including argv, hook acknowledgement, output, and late refinement. An empty prompt is not attachable native input, so it was never in the gated shape. |
|
|
151
|
+
| Multi-operand payload (`projmux create codex -- a b`) | unchanged. The legacy CLI owns provider parsing for a multi-operand payload, so it is not a native create candidate and keeps the plain lane. |
|
|
152
|
+
| Claude and Antigravity | unchanged lifecycle, fan-out, and hook activation contract. |
|
|
153
|
+
| Public hook env and payload schema | unchanged. |
|
|
154
|
+
| Post-thread-creation failures | unchanged. A native failure *after* `thread/start` returned still refuses without offering any second lane — including `--interactive-only` — because starting another Codex process could submit the same prompt twice. |
|
|
155
|
+
|
|
156
|
+
## Verifying this yourself
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
go test ./internal/app/ -run 'TestInteractiveOnlyIsTheOnlyPlainCodexLaneAndBothSpellingsAreEquivalent|TestDefaultNativeCodexFanOutRefusesWithZeroMutationsAndInteractiveOnlyKeepsCardinality|TestEmptyPromptCodexCreateIsByteForByteUnchangedByTheNativeRequiredGate|TestClaudeAndAntigravityLifecycleAndHookContractAreUnchangedByTheNativeGate|TestUnavailableNative|TestCodexNativeLaunchOutcomeTableIsClosed'
|
|
160
|
+
go test ./internal/integrations/agents/codexappserver/ -run TestStartDefaultThread
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
| Claim in this note | Test |
|
|
164
|
+
| --- | --- |
|
|
165
|
+
| Prompted create refuses instead of silently creating a plain Agent, for both the unavailable-endpoint and unsupported-roots rows | `TestUnavailableNativeCreateRefusesInsteadOfSilentlyCreatingAPlainAgent` |
|
|
166
|
+
| Roots are delivered exactly on a negotiated connection, fail closed otherwise, and stay off the wire when empty | `TestStartDefaultThreadDeliversAdditionalRootsOrFailsClosed` |
|
|
167
|
+
| Prompted fan-out refuses with zero mutations; `--interactive-only` keeps the old cardinality | `TestDefaultNativeCodexFanOutRefusesWithZeroMutationsAndInteractiveOnlyKeepsCardinality` |
|
|
168
|
+
| Picker resume refuses instead of rebinding onto the rollout lane, and offers no launch-mode escape hatch | `TestUnavailableNativePickerResumeRefusesInsteadOfRebindingOntoTheRolloutLane` |
|
|
169
|
+
| `agent resume` keeps its safe fallback to one provider resume | `TestUnavailableNativeResumeKeepsTheStoredConversationOnTheProviderResumeLane` |
|
|
170
|
+
| Both `--interactive-only` spellings are equivalent, and non-Codex providers refuse it at zero transactions | `TestInteractiveOnlyIsTheOnlyPlainCodexLaneAndBothSpellingsAreEquivalent` |
|
|
171
|
+
| Empty-prompt create is unchanged | `TestEmptyPromptCodexCreateIsByteForByteUnchangedByTheNativeRequiredGate` |
|
|
172
|
+
| Claude and Antigravity are unchanged | `TestClaudeAndAntigravityLifecycleAndHookContractAreUnchangedByTheNativeGate` |
|
|
173
|
+
| One payload sends exactly one `turn/start` and never repeats the prompt in Pane argv | `TestPromptedNativeCodexCreateIssuesOneTurnAndNeverRepeatsThePromptInPaneArgv` |
|
|
174
|
+
| The post-mutation row still refuses a second lane | `TestIndeterminateNativeCreateRefusesASecondLaneAndWritesZero` |
|
|
175
|
+
| The outcome table describes exactly these rows and no others | `TestCodexNativeLaunchOutcomeTableIsClosed` |
|
package/docs/hooks.md
CHANGED
|
@@ -328,10 +328,31 @@ command = "projmux internal agent-hook ingest codex-hook >/dev/null 2>&1 || true
|
|
|
328
328
|
```
|
|
329
329
|
|
|
330
330
|
Repeated installs are idempotent and preserve unrelated Codex config, including
|
|
331
|
-
unmanaged hook entries for the same events. If projmux sees
|
|
332
|
-
`projmux internal agent-hook ingest codex-hook` command
|
|
333
|
-
|
|
334
|
-
|
|
331
|
+
unmanaged hook entries for the same events. If projmux sees a
|
|
332
|
+
`projmux internal agent-hook ingest codex-hook` command it did not author —
|
|
333
|
+
a different matcher, a wrapped command, or an extra key — it refuses to install
|
|
334
|
+
over it rather than guessing ownership. `--dry-run` previews the TOML update.
|
|
335
|
+
`--remove` removes projmux-managed Codex hooks wiring.
|
|
336
|
+
|
|
337
|
+
The marker block is not a projmux-owned byte range. Projmux owns only the hook
|
|
338
|
+
definitions it wrote and, inside the block, the `[features] hooks = true`
|
|
339
|
+
toggle; everything else in the file belongs to Codex or to you and is handed
|
|
340
|
+
back verbatim. That split is what keeps hook trust alive across convergence:
|
|
341
|
+
Codex records the hooks you approved as `[hooks.state]` subtables holding a
|
|
342
|
+
`trusted_hash`, and it can write them anywhere in the same file, including
|
|
343
|
+
between the projmux markers. Convergence lifts that state back out of the block
|
|
344
|
+
unchanged instead of rewriting the block wholesale, so `projmux agent integrate
|
|
345
|
+
codex`, `projmux config apply`, and the `make install` convergence leave every
|
|
346
|
+
approval you already made in place.
|
|
347
|
+
|
|
348
|
+
A Codex TOML re-serialization can also drop the marker comments while leaving
|
|
349
|
+
the hook wiring projmux wrote behind. Projmux recognizes that wiring as its own
|
|
350
|
+
and restores the markers around the byte-identical definitions rather than
|
|
351
|
+
refusing, so a marker-less config still converges and keeps its trust. Recovery
|
|
352
|
+
stops where ownership does: hook entries you hand-wrote keep the refusal above,
|
|
353
|
+
and so does any layout where re-adopting the projmux entries would renumber a
|
|
354
|
+
hook entry projmux does not own, because Codex keys trust state by array
|
|
355
|
+
position.
|
|
335
356
|
|
|
336
357
|
The Codex hooks install list is catalog-driven. Projmux ships an embedded
|
|
337
358
|
default catalog at `internal/app/ai_hook_catalogs/codex.json`, and merges an
|
|
@@ -392,7 +413,11 @@ not stored.
|
|
|
392
413
|
|
|
393
414
|
Codex may require reviewing or trusting hooks through its `/hooks` flow before
|
|
394
415
|
commands run. Projmux only writes the managed config block; it does not attempt
|
|
395
|
-
to auto-trust hooks.
|
|
416
|
+
to auto-trust hooks. It never computes, copies, moves, or deletes a
|
|
417
|
+
`trusted_hash`, and it never carries an approval from one hook identity to
|
|
418
|
+
another: if a hook's matcher, type, command, or event changes, Codex asks you to
|
|
419
|
+
review that hook again while the other hooks keep the trust you already gave
|
|
420
|
+
them.
|
|
396
421
|
|
|
397
422
|
## Tmux Bell Fallback
|
|
398
423
|
|
package/docs/npm-distribution.md
CHANGED
|
@@ -96,6 +96,12 @@ scripts/package-npm.sh \
|
|
|
96
96
|
This keeps the npm platform binaries byte-for-byte aligned with the GitHub
|
|
97
97
|
Release binaries, including the `darwin && cgo` native key adapter, then
|
|
98
98
|
publishes each staged package with `npm publish --access public`.
|
|
99
|
+
|
|
100
|
+
The GitHub release itself stays a draft until that npm job succeeds. release-please
|
|
101
|
+
creates the release with `draft` set, `release.yml` uploads archives to the drafted
|
|
102
|
+
release, and only the final `publish-release` job flips it visible. So by the time a
|
|
103
|
+
user can see release `vX.Y.Z`, npm `dist-tags.latest` already resolves to `X.Y.Z`;
|
|
104
|
+
a failed npm publish keeps the release hidden and the workflow red.
|
|
99
105
|
The npm publish job uses GitHub Actions OIDC (`id-token: write`) instead of a
|
|
100
106
|
long-lived `NPM_TOKEN` secret. PR CI runs `make npm-pack` so package staging and
|
|
101
107
|
dry-run packing fail before release.
|
|
@@ -202,9 +202,10 @@ Its captured output is capped at 4 KiB. Doctor reads only a pre-existing
|
|
|
202
202
|
regular generated config (at most 1 MiB) without following symlinks, and the
|
|
203
203
|
shared read-only journal seam rejects non-regular inputs and files above 5 MiB.
|
|
204
204
|
These conditions degrade to typed findings rather than blocking or repairing
|
|
205
|
-
the source.
|
|
206
|
-
cannot prove
|
|
207
|
-
|
|
205
|
+
the source. The `privacy-unverified` finding remains in the schema for a path
|
|
206
|
+
whose privacy `os.FileMode` cannot prove, but no supported platform emits it:
|
|
207
|
+
Linux and macOS are the only build targets and POSIX mode bits are
|
|
208
|
+
authoritative on both. Doctor does not modify permissions.
|
|
208
209
|
|
|
209
210
|
### Registry materialization invariant audit
|
|
210
211
|
|
|
@@ -266,11 +267,9 @@ releases ownership when a process exits, so an orphaned lock path needs no
|
|
|
266
267
|
path deletion or stale-owner reclamation and cannot race a successor owner.
|
|
267
268
|
Lock acquisition has an explicit 200 ms total budget so this side channel
|
|
268
269
|
cannot materially delay the original command result. When the file exceeds
|
|
269
|
-
5 MiB,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
discarded before the next append, and the reader skips malformed or truncated
|
|
273
|
-
records.
|
|
270
|
+
5 MiB, an atomic `rename` replacement retains approximately the newest 2 MiB,
|
|
271
|
+
beginning at a complete valid record. A trailing partial record is discarded
|
|
272
|
+
before the next append, and the reader skips malformed or truncated records.
|
|
274
273
|
|
|
275
274
|
Classification is intentionally conservative for mutation-capable interactive
|
|
276
275
|
commands: opening session/project/settings/popup flows is treated as changing
|
package/docs/testing.md
CHANGED
|
@@ -25,10 +25,29 @@ and humans run the same entrypoints.
|
|
|
25
25
|
every fixture has its own HOME/XDG/tmux/socket/evidence roots and every
|
|
26
26
|
consumer records the same binary SHA. `E2E_SCENARIO=<ID>` selects one exact
|
|
27
27
|
stable scenario for replay.
|
|
28
|
+
- Three mutually exclusive selectors narrow one invocation. `E2E_SCENARIO=<ID>`
|
|
29
|
+
replays one scenario, `PROJMUX_E2E_LINUX_SHARD=<shard>` runs exactly one
|
|
30
|
+
Linux fixture with the terminal inventory its `linux-shards.tsv` row owns,
|
|
31
|
+
and `PROJMUX_E2E_SUITE=codex-lifecycle|npm-staging` runs one non-Linux suite.
|
|
32
|
+
Setting none keeps the default: four Linux shards in parallel plus both
|
|
33
|
+
suites. CI uses the shard/suite selectors to give every suite its own runner,
|
|
34
|
+
so container isolation and schedule isolation are the same unit there; local
|
|
35
|
+
`make test-e2e` still runs the whole matrix on one machine.
|
|
36
|
+
- Every scenario wait states a budget in seconds rather than a loop count, and
|
|
37
|
+
`E2E_WAIT_SCALE=<factor>` multiplies all of them at once. Raise it when a
|
|
38
|
+
runner is slow or loaded; a wait that expires still fails with the description
|
|
39
|
+
of what it was waiting for, so a slow machine reports a timeout rather than
|
|
40
|
+
the regression message of the assertion that would have run next.
|
|
28
41
|
- `make test-e2e-contract`, `make test-e2e-reliability`, and
|
|
29
42
|
`make test-e2e-shards` validate typed attempt evidence, bounded semantic
|
|
30
43
|
waits/owned cleanup, and exhaustive four-shard isolation without rerunning
|
|
31
|
-
the full product matrix.
|
|
44
|
+
the full product matrix. The shard target also pins the CI job list to the
|
|
45
|
+
manifest: one non-fail-fast job per shard and per suite, each with its own
|
|
46
|
+
runner, its own timeout and its own uniquely named evidence artifacts, all of
|
|
47
|
+
them required children of the aggregate `Test` gate. It also pins the thin
|
|
48
|
+
`E2E Tests` job, which exists because the branch ruleset requires a status
|
|
49
|
+
check under that exact name; a required context that is never reported stays
|
|
50
|
+
pending rather than failing, so dropping that job would deadlock merges.
|
|
32
51
|
- `make test-e2e-coverage` validates
|
|
33
52
|
`test/e2e/ags-oedr-manifest.json`: executable scenario markers and shard
|
|
34
53
|
assignments must match all 21 rows with orphan count zero. A matrix may move
|
package/docs/troubleshooting.md
CHANGED
|
@@ -124,6 +124,13 @@ endpoint, close every sharing client, stop the process through the operator
|
|
|
124
124
|
that owns it, then run `codex app-server daemon start`. Projmux never performs
|
|
125
125
|
those stop/restart steps or invents an ownership-specific kill command.
|
|
126
126
|
|
|
127
|
+
A prompted managed Codex create also requires that endpoint. When it is not
|
|
128
|
+
ready or not attachable, `projmux create codex -- "<prompt>"` refuses instead of
|
|
129
|
+
silently creating a plain-CLI Agent, and the refusal carries the same typed
|
|
130
|
+
reason Doctor reports. `--interactive-only` creates that plain Agent on purpose,
|
|
131
|
+
without native turn control. See
|
|
132
|
+
[Codex Native-Required Create Migration](codex-native-required-migration.md).
|
|
133
|
+
|
|
127
134
|
If native app-server features are needed, review the
|
|
128
135
|
[official Codex CLI installation options](https://learn.chatgpt.com/docs/codex/cli)
|
|
129
136
|
and install or repair the managed standalone payload. Then rerun Doctor. Do not
|
package/docs/upgrading.md
CHANGED
|
@@ -52,6 +52,21 @@ still requires an explicit `PROJMUX_INSTALLER=github-release`.
|
|
|
52
52
|
|
|
53
53
|
## Behavior Changes
|
|
54
54
|
|
|
55
|
+
### Prompted Codex create requires native authority
|
|
56
|
+
|
|
57
|
+
A prompted managed Codex create — provider `codex` with exactly one non-empty
|
|
58
|
+
payload operand — no longer degrades to the plain CLI lane when native authority
|
|
59
|
+
cannot be proven. It refuses at the provider-mutation boundary with zero
|
|
60
|
+
threads, zero Panes, and zero Registry writes, and names `--interactive-only` as
|
|
61
|
+
the explicit way to ask for a plain interactive Codex Agent instead.
|
|
62
|
+
|
|
63
|
+
There is nothing to migrate: no Registry schema, configuration, hook contract,
|
|
64
|
+
or stored Agent changes, and every existing resource keeps working after the
|
|
65
|
+
upgrade. `projmux agent resume`, empty-prompt creates, and the Claude and
|
|
66
|
+
Antigravity providers are unchanged. For the affected calls, the escape hatch,
|
|
67
|
+
and how to verify each claim, see
|
|
68
|
+
[Codex Native-Required Create Migration](codex-native-required-migration.md).
|
|
69
|
+
|
|
55
70
|
### Registry schema v2 final Window anchors
|
|
56
71
|
|
|
57
72
|
The unreleased intermediate schema-v2 Window field `primaryPaneRef` has been
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projmux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "tmux project session manager",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/crevissepartners/projmux#readme",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"package:npm:pack": "scripts/package-npm.sh --pack"
|
|
29
29
|
},
|
|
30
30
|
"optionalDependencies": {
|
|
31
|
-
"@projmux/linux-x64": "0.
|
|
32
|
-
"@projmux/linux-arm64": "0.
|
|
33
|
-
"@projmux/darwin-x64": "0.
|
|
34
|
-
"@projmux/darwin-arm64": "0.
|
|
31
|
+
"@projmux/linux-x64": "0.14.0",
|
|
32
|
+
"@projmux/linux-arm64": "0.14.0",
|
|
33
|
+
"@projmux/darwin-x64": "0.14.0",
|
|
34
|
+
"@projmux/darwin-arm64": "0.14.0"
|
|
35
35
|
}
|
|
36
36
|
}
|