projmux 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/docs/agent-workflow.md +182 -4
- package/docs/cli-guide.md +94 -21
- package/docs/cli.md +267 -7
- package/docs/codex-native-required-migration.md +175 -0
- package/docs/configuration.md +36 -13
- package/docs/hooks.md +30 -5
- package/docs/install.md +23 -7
- package/docs/keybindings.md +3 -1
- package/docs/npm-distribution.md +6 -0
- package/docs/operational-diagnostics.md +7 -8
- package/docs/testing.md +33 -7
- package/docs/troubleshooting.md +164 -0
- package/docs/upgrading.md +38 -6
- package/package.json +5 -5
|
@@ -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/configuration.md
CHANGED
|
@@ -422,26 +422,40 @@ user/global preference in this release.
|
|
|
422
422
|
## Codex app-server health
|
|
423
423
|
|
|
424
424
|
Settings > AI includes a read-only `Codex control plane` row. It reports one of
|
|
425
|
-
`App Server`, `Hook fallback`, or `Unavailable`, together with
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
425
|
+
`App Server`, `Hook fallback`, or `Unavailable`, together with the existing
|
|
426
|
+
effective reason. Endpoint readiness, running executable/version, official
|
|
427
|
+
daemon-manager ownership, and remote-control capability are separate closed
|
|
428
|
+
axes. Separate `probe_reason` and `install_capability` fields preserve the
|
|
429
|
+
app-server root cause and bounded PATH/managed-payload topology. Lifecycle
|
|
430
|
+
outcome/reason and sanitized CLI, managed, and running versions remain
|
|
431
|
+
independent. Read-only surfaces report `not-attempted/read-only`.
|
|
432
|
+
`projmux doctor --section integrations` and explicit support reports expose the
|
|
433
|
+
same bounded fields without executable/socket paths, prompts, tokens, process
|
|
434
|
+
output, or response payloads.
|
|
431
435
|
|
|
432
436
|
There is no app-server source setting or environment override. Authority is a
|
|
433
437
|
capability result, not a preference: Projmux probes the existing local control
|
|
434
|
-
socket through `codex app-server proxy
|
|
435
|
-
|
|
436
|
-
|
|
438
|
+
socket through `codex app-server proxy`, reads official manager evidence through
|
|
439
|
+
`codex app-server daemon version`, and reads remote-control state through
|
|
440
|
+
`remoteControl/status/read`, all with short timeouts. An older endpoint that
|
|
441
|
+
does not expose the last method reports `unsupported` on that axis without
|
|
442
|
+
hiding endpoint readiness. Doctor, Settings, and support reports never start or
|
|
443
|
+
otherwise mutate the daemon, configuration, login state, or control socket.
|
|
444
|
+
|
|
445
|
+
An `external-cli-only` install capability acknowledges that the ordinary CLI
|
|
446
|
+
exists while the canonical managed daemon payload was not observed. It does
|
|
447
|
+
not identify a package manager. Install topology is not manager ownership:
|
|
448
|
+
only the official daemon response's backend field proves a managed process.
|
|
437
449
|
|
|
438
450
|
The Codex integration has a lifecycle seam for later native features. Only an
|
|
439
|
-
actual native user action may use it
|
|
440
|
-
|
|
451
|
+
actual native user action may use it. A ready unmanaged, version-skewed, or
|
|
452
|
+
ownership/version-unknown endpoint is refused without mutation and reports the
|
|
453
|
+
shared-client interruption risk plus bounded operator recovery. Only an exact
|
|
454
|
+
missing or connection-refused default control socket is start-eligible. That path invokes the
|
|
441
455
|
official idempotent `codex app-server daemon start` command at most once per
|
|
442
456
|
in-flight process decision, then retries proxy initialization with a bounded
|
|
443
|
-
backoff.
|
|
444
|
-
|
|
457
|
+
backoff. Projmux never automatically stops, kills, restarts, adopts, or enables
|
|
458
|
+
remote control on the shared app server.
|
|
445
459
|
|
|
446
460
|
The default `Codex` row in the provider picker launches immediately through the
|
|
447
461
|
canonical create route. It does not start or probe the app-server, call
|
|
@@ -922,6 +936,15 @@ that target subtree atomically, runs the ordinary materializer, and performs an
|
|
|
922
936
|
explicit client handoff last when `--client` is present. Restore never modifies
|
|
923
937
|
or deletes the source snapshot.
|
|
924
938
|
|
|
939
|
+
Interactive `projmux quit` also offers `Save Project snapshots and quit`. It
|
|
940
|
+
recaptures the latest snapshot for every live Registry-bound Project on the
|
|
941
|
+
exact app server, regardless of the global or Project auto-save toggle, and
|
|
942
|
+
stops the server only after all captures succeed. A partial failure keeps the
|
|
943
|
+
server running and keeps each successful atomic snapshot for inspection or
|
|
944
|
+
retry. Control/Home, ephemeral, unmanaged, conflicted, and sibling-server
|
|
945
|
+
sessions are never promoted into Project snapshots. `Quit without saving`,
|
|
946
|
+
`quit --yes`, and `quit --force` perform no snapshot inventory or store I/O.
|
|
947
|
+
|
|
925
948
|
## Decoration Mode
|
|
926
949
|
|
|
927
950
|
Settings > Appearance controls optional icon decoration per surface:
|
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/install.md
CHANGED
|
@@ -21,6 +21,11 @@ projmux doctor
|
|
|
21
21
|
`doctor` performs read-only diagnostics for runtime tools such as `tmux`,
|
|
22
22
|
`git`, and `stty`.
|
|
23
23
|
|
|
24
|
+
If the npm shim reports `unsupported or incomplete npm install`, or a live
|
|
25
|
+
pre-0.13 app server is missing its logical socket marker, use the canonical
|
|
26
|
+
[Troubleshooting](troubleshooting.md) diagnosis and recovery steps. Doctor is
|
|
27
|
+
read-only; its displayed remediation is never executed automatically.
|
|
28
|
+
|
|
24
29
|
Provider integrations are opt-in and use the canonical installer spelling:
|
|
25
30
|
|
|
26
31
|
```sh
|
|
@@ -100,10 +105,15 @@ cd projmux
|
|
|
100
105
|
make install
|
|
101
106
|
```
|
|
102
107
|
|
|
103
|
-
`make install` builds the
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
`make install` builds the candidate, uses that candidate to run
|
|
109
|
+
`config apply --bin <install-target>` before publication, atomically replaces
|
|
110
|
+
`$(go env GOPATH)/bin/projmux`, and runs the installed binary's `config apply`
|
|
111
|
+
again as post-publication verification. This ordering migrates a pre-0.13 live
|
|
112
|
+
server's exact logical socket marker before a consumer that requires it becomes
|
|
113
|
+
reachable. A convergence or publication failure is non-zero and prints the
|
|
114
|
+
exact `projmux config apply --socket <name>` recovery. Successful installs then
|
|
115
|
+
reconcile the notify queue through `projmux notification reconcile`. Override
|
|
116
|
+
the destination with `INSTALL_DIR=/usr/local/bin`.
|
|
107
117
|
|
|
108
118
|
Update source checkouts with the repository workflow:
|
|
109
119
|
|
|
@@ -121,11 +131,14 @@ marked as release-managed:
|
|
|
121
131
|
export PROJMUX_INSTALLER=github-release
|
|
122
132
|
```
|
|
123
133
|
|
|
124
|
-
With that set, `projmux update apply` downloads the latest matching
|
|
125
|
-
asset,
|
|
134
|
+
With that set, `projmux update apply` downloads and verifies the latest matching
|
|
135
|
+
release asset, converges the exact live route before replacement, atomically
|
|
136
|
+
replaces the current executable, and reapplies the live tmux config as
|
|
137
|
+
post-publication verification.
|
|
126
138
|
`--no-apply` skips the live reload only — the new binary still migrates the
|
|
127
139
|
keymap schema and marker-owned provider files, then writes the generated
|
|
128
|
-
config. It
|
|
140
|
+
config. It prints the exact explicit apply required before ordinary mutation
|
|
141
|
+
and does not touch a live tmux bell hook. See
|
|
129
142
|
[Upgrading](upgrading.md#managed-agent-hook-producer-migration).
|
|
130
143
|
|
|
131
144
|
## npm Packaging Details
|
|
@@ -133,3 +146,6 @@ config. It does not touch a live tmux bell hook. See
|
|
|
133
146
|
Repository packaging and publish details are maintained in
|
|
134
147
|
[npm Distribution](npm-distribution.md). That document is for maintainers; end
|
|
135
148
|
users should not need it for installation.
|
|
149
|
+
|
|
150
|
+
For incomplete optional dependencies, Doctor findings, and live app socket
|
|
151
|
+
marker recovery, see [Troubleshooting](troubleshooting.md).
|
package/docs/keybindings.md
CHANGED
|
@@ -406,7 +406,9 @@ installed yet, and only it knows its own canonical ids.
|
|
|
406
406
|
|
|
407
407
|
`--no-apply` suppresses the live tmux reload, not the migration. Installer paths
|
|
408
408
|
still invoke the new binary as `config apply --no-reload` so the schema does not
|
|
409
|
-
fall behind the binary that writes it.
|
|
409
|
+
fall behind the binary that writes it. They omit pre-publication live
|
|
410
|
+
convergence entirely and print the exact explicit `config apply --socket`
|
|
411
|
+
still required before ordinary mutation.
|
|
410
412
|
|
|
411
413
|
### Downgrading or rolling back
|
|
412
414
|
|
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
|
@@ -15,18 +15,39 @@ and humans run the same entrypoints.
|
|
|
15
15
|
`tmux` server, and notify queue CRUD.
|
|
16
16
|
- `make test-install-smoke` builds the same Docker image and runs
|
|
17
17
|
`test/install/smoke.sh`. It validates `make install`, atomic binary
|
|
18
|
-
replacement into an isolated install dir,
|
|
19
|
-
|
|
18
|
+
replacement into an isolated install dir, pre-publication marker convergence,
|
|
19
|
+
concurrent legacy/candidate/installed shell and attach consumers, exact
|
|
20
|
+
server-generation/session preservation, `tmux apply`, and post-install notify
|
|
21
|
+
reconcile initialization with a fresh HOME/XDG state tree.
|
|
20
22
|
- `make test-e2e` prepares one attempt-local immutable product binary, then
|
|
21
23
|
runs four isolated Linux real-tmux fixtures plus the Codex lifecycle and npm
|
|
22
24
|
staging fixtures. The required inventory is `L01`-`L19`, `C01`, and `N01`;
|
|
23
25
|
every fixture has its own HOME/XDG/tmux/socket/evidence roots and every
|
|
24
26
|
consumer records the same binary SHA. `E2E_SCENARIO=<ID>` selects one exact
|
|
25
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.
|
|
26
41
|
- `make test-e2e-contract`, `make test-e2e-reliability`, and
|
|
27
42
|
`make test-e2e-shards` validate typed attempt evidence, bounded semantic
|
|
28
43
|
waits/owned cleanup, and exhaustive four-shard isolation without rerunning
|
|
29
|
-
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.
|
|
30
51
|
- `make test-e2e-coverage` validates
|
|
31
52
|
`test/e2e/ags-oedr-manifest.json`: executable scenario markers and shard
|
|
32
53
|
assignments must match all 21 rows with orphan count zero. A matrix may move
|
|
@@ -41,10 +62,15 @@ and humans run the same entrypoints.
|
|
|
41
62
|
checks scanner/rule/baseline identity, PR-range/full-history secret scans,
|
|
42
63
|
cache miss-to-hit convergence, privacy-safe artifacts, and the fail-closed
|
|
43
64
|
aggregate. CI exposes their stable aggregate as `Test`.
|
|
44
|
-
- `make deadcode` runs
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
65
|
+
- `make deadcode` runs the focused baseline-contract fixture, then runs
|
|
66
|
+
`go tool deadcode` (pinned via the go.mod tool directive) over the module.
|
|
67
|
+
`.deadcode-allowlist.txt` is exact to current findings: duplicate and stale
|
|
68
|
+
rows fail. `.deadcode-must-keep.txt` separately records proactive migration,
|
|
69
|
+
compatibility, and proof APIs as `symbol<TAB>non-empty reason`; duplicates
|
|
70
|
+
within either file, overlap across files, malformed reasons, and findings
|
|
71
|
+
outside the two-file union fail deterministically. `make test` also runs the
|
|
72
|
+
focused fixture, and `make fix` runs the complete deadcode gate after
|
|
73
|
+
`go fix`.
|
|
48
74
|
|
|
49
75
|
## Docker-Covered Checks
|
|
50
76
|
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
Start with read-only diagnostics:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
projmux doctor
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
`doctor` never installs packages, rewrites configuration, or changes a tmux
|
|
10
|
+
server. Run a remediation command only after reviewing the finding that
|
|
11
|
+
recommended it. For the operational journal's privacy and retention contract,
|
|
12
|
+
see [Operational Diagnostics](operational-diagnostics.md).
|
|
13
|
+
|
|
14
|
+
## App socket marker migration
|
|
15
|
+
|
|
16
|
+
Projmux 0.13 and newer require two server-global markers before an ordinary
|
|
17
|
+
command may mutate the app tmux server:
|
|
18
|
+
|
|
19
|
+
- `@projmux_app=1` declares app ownership.
|
|
20
|
+
- `@projmux_socket_name=<name>` declares the logical `-L <name>` route.
|
|
21
|
+
|
|
22
|
+
An app server started by a pre-0.13 release can still be live with the first
|
|
23
|
+
marker and no logical marker. In that partial state, `shell`, attach, and
|
|
24
|
+
materialization commands fail closed and print the exact recovery command.
|
|
25
|
+
For the default app socket, run:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
projmux config apply --socket projmux
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This explicit apply keeps the live server and its sessions, binds `-L projmux`
|
|
32
|
+
to one absolute socket path and server PID, sources the generated config,
|
|
33
|
+
writes the missing logical marker, and verifies both markers against the same
|
|
34
|
+
server generation. Ordinary commands do not write the marker. Apply also
|
|
35
|
+
refuses a foreign server, a different existing logical marker, an alias/path
|
|
36
|
+
mismatch, or PID drift; do not replace the refusal with a raw `tmux set-option`
|
|
37
|
+
command.
|
|
38
|
+
|
|
39
|
+
For a non-default socket, use the exact name printed by the failing command:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
projmux config apply --socket <name>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then retry the original command.
|
|
46
|
+
|
|
47
|
+
## Diagnostic sequence
|
|
48
|
+
|
|
49
|
+
Use this order so each step remains read-only until you deliberately run the
|
|
50
|
+
recovery command:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
projmux doctor --section runtime --verbose
|
|
54
|
+
projmux diagnostics log
|
|
55
|
+
tmux -L projmux show-options -gqv @projmux_app
|
|
56
|
+
tmux -L projmux show-options -gqv @projmux_socket_name
|
|
57
|
+
tmux -L projmux display-message -p -F '#{socket_path} #{pid}'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Replace `projmux` in all three tmux commands with the exact logical socket name
|
|
61
|
+
you are diagnosing. Expected healthy marker output is `1` and that same socket
|
|
62
|
+
name. The final read records the physical socket/PID pair for comparison; it
|
|
63
|
+
does not grant authority or repair anything.
|
|
64
|
+
|
|
65
|
+
The marker-specific Doctor codes map to these actions:
|
|
66
|
+
|
|
67
|
+
| Code | Meaning | Remediation |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| `runtime.route-marker.missing` | App-owned live server has no logical marker, normally after a pre-0.13 live-server upgrade. | Run the exact `projmux config apply --socket <name>` printed by the failing ordinary command, then retry it. |
|
|
70
|
+
| `runtime.route-marker.mismatch` | The app-owned server declares a different logical route. | Do not overwrite it. Inspect the diagnostic sequence and confirm which `-L` route owns the server. |
|
|
71
|
+
| `runtime.route-marker.unreadable` | Doctor could reach the socket but could not read one or both ownership markers. | Inspect `projmux diagnostics log`, tmux/socket permissions, and the exact marker reads. Do not apply until the read failure is understood. |
|
|
72
|
+
|
|
73
|
+
Other runtime Doctor codes use bounded remediation identifiers in text and
|
|
74
|
+
JSON:
|
|
75
|
+
|
|
76
|
+
| Code family | Remediation |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `runtime.socket.unreachable` | Start the app with `projmux shell`; if a server should already exist, inspect the exact socket first. |
|
|
79
|
+
| `runtime.socket.probe-failed`, `runtime.backend.unknown` | Inspect `projmux diagnostics log` and the operational journal. |
|
|
80
|
+
| `runtime.config.generated-missing`, `runtime.config.generated-invalid` | Run `projmux config apply --socket <name>` after confirming the target server. |
|
|
81
|
+
| `runtime.config.generated-unreadable` | Inspect config and directory permissions before applying. |
|
|
82
|
+
| `runtime.config.applied-stale` | Run the exact config apply command to reload the generated config. |
|
|
83
|
+
| `runtime.config.applied-unknown` | Start or identify the app runtime before attempting a reload. |
|
|
84
|
+
| `logs.*` | Follow the finding's `inspect-state-permissions`, `inspect-log-permissions`, or `inspect-operational-journal` remediation. |
|
|
85
|
+
| `registry.materialize.*` | Inspect the reported Registry topology. Doctor is read-only and does not repair it. |
|
|
86
|
+
|
|
87
|
+
Informational `*.ready`, `*.current`, `*.reachable`, `*.none`, `*.clean`, and
|
|
88
|
+
`*.audited` codes need no remediation. JSON exposes the same `code` and
|
|
89
|
+
`remediation` values as verbose text.
|
|
90
|
+
|
|
91
|
+
## Codex app-server install topology
|
|
92
|
+
|
|
93
|
+
Start with the read-only integration report:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
projmux doctor --section integrations --verbose
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The `Codex app-server` result keeps four readiness axes separate:
|
|
100
|
+
|
|
101
|
+
- `Endpoint readiness` says whether the existing endpoint is ready, dead, or
|
|
102
|
+
failed with a bounded reason.
|
|
103
|
+
- `running executable` plus the sanitized version fields distinguish a proven
|
|
104
|
+
managed executable from unknown identity and current from skewed versions.
|
|
105
|
+
- `manager ownership` comes only from the official daemon backend result; an
|
|
106
|
+
absent or unclear result is never guessed from endpoint health.
|
|
107
|
+
- `remote control` independently reports disabled, connecting, connected,
|
|
108
|
+
errored, unsupported, unavailable, or unknown.
|
|
109
|
+
|
|
110
|
+
`Source`/`reason`, `App-server probe`, `install capability`, and `lifecycle`
|
|
111
|
+
remain separate supporting fields. A ready endpoint therefore does not hide an
|
|
112
|
+
unmanaged process or version skew.
|
|
113
|
+
|
|
114
|
+
`external-cli-only` means the ordinary Codex CLI executable is present, but
|
|
115
|
+
the canonical managed payload needed by `codex app-server daemon start` was not
|
|
116
|
+
observed. It does not mean the ordinary CLI is unsupported and does not prove
|
|
117
|
+
who owns a running process.
|
|
118
|
+
|
|
119
|
+
An explicit native action refuses a ready unmanaged or version-skewed endpoint.
|
|
120
|
+
The refusal reports `shared-clients-disconnect`: replacing this shared process
|
|
121
|
+
can interrupt every attached Codex client. For a managed skew, confirm the
|
|
122
|
+
interruption and run `codex app-server daemon restart`. For an unmanaged
|
|
123
|
+
endpoint, close every sharing client, stop the process through the operator
|
|
124
|
+
that owns it, then run `codex app-server daemon start`. Projmux never performs
|
|
125
|
+
those stop/restart steps or invents an ownership-specific kill command.
|
|
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
|
+
|
|
134
|
+
If native app-server features are needed, review the
|
|
135
|
+
[official Codex CLI installation options](https://learn.chatgpt.com/docs/codex/cli)
|
|
136
|
+
and install or repair the managed standalone payload. Then rerun Doctor. Do not
|
|
137
|
+
copy binaries, create symlinks in the Codex home, or edit the control socket as
|
|
138
|
+
a diagnostic workaround. Doctor, Settings, and support-report collection never
|
|
139
|
+
start the daemon or modify the installation.
|
|
140
|
+
|
|
141
|
+
## Incomplete npm install
|
|
142
|
+
|
|
143
|
+
If the npm shim exits before Projmux starts with:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
projmux: unsupported or incomplete npm install for <platform>/<arch>.
|
|
147
|
+
Expected optional dependency @projmux/<platform>-<arch> to provide bin/projmux.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
the platform-specific optional package is missing. This can happen when npm
|
|
151
|
+
reuses stale package metadata or optional dependencies were disabled. Because
|
|
152
|
+
the Go binary is absent, `projmux doctor` cannot run yet. Re-resolve the current
|
|
153
|
+
release and its optional dependency:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
npm cache verify
|
|
157
|
+
npm install -g projmux@latest --include=optional
|
|
158
|
+
projmux version
|
|
159
|
+
projmux doctor
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
If the same error remains, remove the incomplete global package, refresh npm's
|
|
163
|
+
package metadata, and reinstall. Do not copy a binary from another platform.
|
|
164
|
+
GitHub Release and source alternatives are documented in [Install](install.md).
|