projmux 0.12.2 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/docs/agent-workflow.md +277 -20
- package/docs/ai-agent-shortcuts.md +22 -3
- package/docs/architecture.md +1291 -90
- package/docs/cli-guide.md +572 -69
- package/docs/cli.md +498 -67
- package/docs/configuration.md +220 -35
- package/docs/hooks.md +17 -3
- package/docs/install.md +23 -7
- package/docs/keybindings.md +5 -3
- package/docs/legacy-cli-retirement.md +37 -8
- package/docs/operational-diagnostics.md +58 -2
- package/docs/session-restore.md +73 -157
- package/docs/settings-ia.md +48 -24
- package/docs/statusbar.md +10 -2
- package/docs/testing.md +93 -10
- package/docs/troubleshooting.md +157 -0
- package/docs/upgrading.md +266 -11
- package/docs/usage-tracking.md +56 -10
- package/package.json +5 -5
|
@@ -0,0 +1,157 @@
|
|
|
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
|
+
If native app-server features are needed, review the
|
|
128
|
+
[official Codex CLI installation options](https://learn.chatgpt.com/docs/codex/cli)
|
|
129
|
+
and install or repair the managed standalone payload. Then rerun Doctor. Do not
|
|
130
|
+
copy binaries, create symlinks in the Codex home, or edit the control socket as
|
|
131
|
+
a diagnostic workaround. Doctor, Settings, and support-report collection never
|
|
132
|
+
start the daemon or modify the installation.
|
|
133
|
+
|
|
134
|
+
## Incomplete npm install
|
|
135
|
+
|
|
136
|
+
If the npm shim exits before Projmux starts with:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
projmux: unsupported or incomplete npm install for <platform>/<arch>.
|
|
140
|
+
Expected optional dependency @projmux/<platform>-<arch> to provide bin/projmux.
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
the platform-specific optional package is missing. This can happen when npm
|
|
144
|
+
reuses stale package metadata or optional dependencies were disabled. Because
|
|
145
|
+
the Go binary is absent, `projmux doctor` cannot run yet. Re-resolve the current
|
|
146
|
+
release and its optional dependency:
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
npm cache verify
|
|
150
|
+
npm install -g projmux@latest --include=optional
|
|
151
|
+
projmux version
|
|
152
|
+
projmux doctor
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
If the same error remains, remove the incomplete global package, refresh npm's
|
|
156
|
+
package metadata, and reinstall. Do not copy a binary from another platform.
|
|
157
|
+
GitHub Release and source alternatives are documented in [Install](install.md).
|
package/docs/upgrading.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Upgrading
|
|
2
2
|
|
|
3
|
+
If an older live app server refuses ordinary commands after upgrading, or npm
|
|
4
|
+
cannot resolve the platform optional dependency, follow
|
|
5
|
+
[Troubleshooting](troubleshooting.md). The canonical marker recovery is the
|
|
6
|
+
exact `projmux config apply --socket <name>` printed by the refusal; ordinary
|
|
7
|
+
runtime commands never backfill missing markers.
|
|
8
|
+
|
|
3
9
|
projmux has two update surfaces:
|
|
4
10
|
|
|
5
11
|
- `projmux shell` reads the cached release status before opening the app. When
|
|
@@ -25,7 +31,9 @@ projmux update apply
|
|
|
25
31
|
Use `--dry-run` to see the planned action and `--no-apply` to skip reloading
|
|
26
32
|
the live tmux config after the binary changes. `--no-apply` suppresses live
|
|
27
33
|
tmux access only; the new binary still migrates the keymap schema and
|
|
28
|
-
marker-owned provider files, then writes the generated config.
|
|
34
|
+
marker-owned provider files, then writes the generated config. It finishes by
|
|
35
|
+
printing `projmux config apply --socket projmux` as the explicit live
|
|
36
|
+
convergence still required before ordinary mutation. See
|
|
29
37
|
[Keymap schema migration](#keymap-schema-migration) and
|
|
30
38
|
[Managed Agent hook producer migration](#managed-agent-hook-producer-migration).
|
|
31
39
|
|
|
@@ -44,15 +52,253 @@ still requires an explicit `PROJMUX_INSTALLER=github-release`.
|
|
|
44
52
|
|
|
45
53
|
## Behavior Changes
|
|
46
54
|
|
|
55
|
+
### Registry schema v2 final Window anchors
|
|
56
|
+
|
|
57
|
+
The unreleased intermediate schema-v2 Window field `primaryPaneRef` has been
|
|
58
|
+
replaced by the final v2 shape:
|
|
59
|
+
|
|
60
|
+
- `anchorPaneRef` is required and names a same-Window shell or managed Agent
|
|
61
|
+
Pane.
|
|
62
|
+
- `defaultShellPaneRef` is optional; when present it names a direct
|
|
63
|
+
Window-owned shell Pane.
|
|
64
|
+
|
|
65
|
+
The on-disk `schemaVersion` remains `2` because the intermediate shape was not
|
|
66
|
+
published. A schema-v1 Registry migrates directly to these fields. An
|
|
67
|
+
intermediate-v2 Registry is identified by raw field presence and normalized
|
|
68
|
+
under the Registry lock. The migration publishes an exact mode-0600 backup and
|
|
69
|
+
a checksum/repair report before staging and atomically replacing the Registry.
|
|
70
|
+
Mixed legacy/final fields, mixed Window authorities, dangling or cross-Window
|
|
71
|
+
anchors, and invalid default shells fail closed without changing the source.
|
|
72
|
+
Repeating migration on final-v2 is a byte-level no-op.
|
|
73
|
+
|
|
74
|
+
The final writer never emits `primaryPaneRef`. A matching intermediate
|
|
75
|
+
pre-release validator therefore sees final-v2 as invalid because its required
|
|
76
|
+
legacy authority is absent. Not every old read surface necessarily runs that
|
|
77
|
+
validator: an old command may ignore the new fields and return a partial view.
|
|
78
|
+
That is evidence of incompatibility, not permission to proceed. Operators must
|
|
79
|
+
refuse a binary-only downgrade before installation and must not permit the old
|
|
80
|
+
binary to write final-v2 bytes. To roll back during the prerelease window, stop
|
|
81
|
+
the final writer, restore the exact pre-normalization Registry backup, and
|
|
82
|
+
restore the matching intermediate binary as a pair. Snapshots are not migration
|
|
83
|
+
or rollback inputs and are never rewritten by this normalization.
|
|
84
|
+
|
|
85
|
+
Rollback rehearsal is byte-oriented: record the backup path, mode, SHA-256,
|
|
86
|
+
and matching pre-release binary revision; stop every final-v2 writer; atomically
|
|
87
|
+
restore those exact intermediate-v2 bytes; verify their checksum; install the
|
|
88
|
+
recorded matching binary; then run its read-only validation before permitting a
|
|
89
|
+
write. Installing only the intermediate binary while leaving final-v2 Registry
|
|
90
|
+
bytes in place is deliberately rejected and is not a rollback procedure.
|
|
91
|
+
|
|
92
|
+
### `create` is resource-backed on every spelling
|
|
93
|
+
|
|
94
|
+
**Breaking.** `create pane`, `create agent`, and the `create
|
|
95
|
+
codex|claude|antigravity` shortcuts no longer have two product models behind the
|
|
96
|
+
presence of `--project`. Previously an invocation *with* `--project` created
|
|
97
|
+
Registry resources and materialized them detached, while an invocation *without*
|
|
98
|
+
it ran a runtime-only split of the current tmux window that created no Projmux
|
|
99
|
+
resource and moved the client. That second model is gone.
|
|
100
|
+
|
|
101
|
+
What changes for an existing invocation:
|
|
102
|
+
|
|
103
|
+
| Invocation | Before | Now |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| `create codex --placement right` inside a managed Project | Runtime-only split of the current tmux window; no Registry resource; the client followed the new pane | Creates an Agent and its managed Pane below the **active managed Project's active Window**, anchored on the active Pane, materialized detached; the client does not move |
|
|
106
|
+
| `create codex -w hi --create-window` | `flag provided but not defined: -w` | Creates Window `hi` under the active managed Project, then the Agent and its managed Pane inside it |
|
|
107
|
+
| `create pane --placement down` inside a managed Project | Runtime-only shell split | Creates a Pane resource below the active Window and splits it detached |
|
|
108
|
+
| Any `create` inside Home, a control session, an unattributed or foreign pane | Runtime-only split | Exit `2` naming `--project`, with zero Registry writes and zero tmux mutations |
|
|
109
|
+
| Any `create` from outside tmux with no `--project` | Runtime-only split against the default server | Exit `2` naming `--project`; no server is probed |
|
|
110
|
+
| Any `create` with an explicit `--project` | Resource-backed | Unchanged |
|
|
111
|
+
| `create pane -o uid\|name\|ref\|metadata\|json` with no `--project` | Exit `2` saying the compatibility split creates no resource | Works: the projections resolve the created resource |
|
|
112
|
+
|
|
113
|
+
Two consequences are worth calling out:
|
|
114
|
+
|
|
115
|
+
- **The client no longer follows the new pane.** Every create is detached. Use
|
|
116
|
+
`projmux focus pane uid:<uid>` — or `-o pane-id` and your own `select-pane` —
|
|
117
|
+
when you want to move there.
|
|
118
|
+
- **Panes created this way are now managed.** They appear in `get panes`,
|
|
119
|
+
in the primary navigation surface, and in `reconcile resources`. Nothing
|
|
120
|
+
adopts the runtime-only panes created by older releases; they stay visible in
|
|
121
|
+
the Runtime diagnostics surface and are never imported automatically.
|
|
122
|
+
|
|
123
|
+
The default keybindings whose bodies are `create codex|claude|pane --placement
|
|
124
|
+
right|down` keep their spelling and their "split where I am" meaning, and now
|
|
125
|
+
produce Registry resources. `internal agent-pane launch-default` — the saved
|
|
126
|
+
default split mode, bound to Ctrl-Shift-R/L in the terminal adapters — kept its
|
|
127
|
+
spelling and its meaning, and now produces the same Registry resources: see
|
|
128
|
+
[Every split surface is Registry-first](#every-split-surface-is-registry-first).
|
|
129
|
+
|
|
130
|
+
The generated tmux config changed shape to support that. Every projmux
|
|
131
|
+
`run-shell` binding is now rendered as `run-shell "TMUX_PANE=#{pane_id} <bin>
|
|
132
|
+
..."`, because tmux exports `$TMUX` to a `run-shell` child but never
|
|
133
|
+
`$TMUX_PANE`, and without the exact pane a binding cannot tell where it was
|
|
134
|
+
pressed. `projmux config apply` — which `make install` and `projmux update
|
|
135
|
+
apply` run for you — rewrites the generated file. If you hand-copied a projmux
|
|
136
|
+
`run-shell` line into your own `~/.tmux.conf`, add the same prefix.
|
|
137
|
+
|
|
138
|
+
If you need a raw, unmanaged tmux split, use tmux itself (`split-window`).
|
|
139
|
+
projmux does not spell that as a resource verb.
|
|
140
|
+
|
|
141
|
+
### Every split surface is Registry-first
|
|
142
|
+
|
|
143
|
+
The saved-default split binding, the `Alt-7` provider picker, and the resume
|
|
144
|
+
picker used to open a pane by calling tmux's `split-window` directly. Those panes
|
|
145
|
+
were runtime-only: no uid, no owner Window, no Agent row, and no row in `get
|
|
146
|
+
panes` or the primary navigation surface. They now go through the same canonical
|
|
147
|
+
`create` route the `create codex|claude|pane` bindings and typed commands use, so
|
|
148
|
+
every pane a Projmux surface opens is a managed resource.
|
|
149
|
+
|
|
150
|
+
What changes for you:
|
|
151
|
+
|
|
152
|
+
- **Panes from the picker and the saved default are managed.** They appear in
|
|
153
|
+
`get panes`, `get agents`, the primary navigation surface, and `reconcile
|
|
154
|
+
resources`, and they are deleted with `projmux delete pane|agent`. Panes those
|
|
155
|
+
surfaces created in older releases are not adopted; they stay visible in the
|
|
156
|
+
Runtime diagnostics surface.
|
|
157
|
+
- **A launch needs a resolvable Project.** These surfaces create resources, so
|
|
158
|
+
they follow [Create scope](cli-guide.md#create-scope) like any other create. A
|
|
159
|
+
UI action taken outside a managed Project fails with the same
|
|
160
|
+
`pass --project <ref>` guidance instead of opening an unmanaged pane.
|
|
161
|
+
- **The legacy `ai split` route is gone**, along with its `--agent`,
|
|
162
|
+
`--force-agent`, and `--print-pane-id` flags. The `ai` root itself was retired
|
|
163
|
+
earlier; nothing reaches that handler now. Automation that wants the new pane's
|
|
164
|
+
handle uses `-o pane-id` on a canonical create — see
|
|
165
|
+
[AI Agent Shortcuts](ai-agent-shortcuts.md).
|
|
166
|
+
- **The AI title watcher no longer starts automatically.** The canonical create
|
|
167
|
+
route never started it, and these surfaces now behave the same way. Pane
|
|
168
|
+
titles, topics, and status come from provider hooks and from `projmux agent
|
|
169
|
+
topic`. `projmux internal agent-hook watch-title` still exists for the hook
|
|
170
|
+
contract.
|
|
171
|
+
|
|
172
|
+
If you need a raw, unmanaged tmux split, use tmux itself (`split-window`).
|
|
173
|
+
|
|
174
|
+
### One lifecycle trigger route
|
|
175
|
+
|
|
176
|
+
The generated tmux config used to install two lifecycle routes: `internal tmux
|
|
177
|
+
reconcile-bindings` on `after-new-window`/`after-split-window`, and `internal tmux
|
|
178
|
+
release-dead-agent-panes` on `pane-exited`/`after-kill-pane`. Both answered the
|
|
179
|
+
same question with a different subset of one reconciliation, so a pane exit
|
|
180
|
+
inside a session that had also just gained a window paid for two registry
|
|
181
|
+
transactions to reach the state one pass reaches.
|
|
182
|
+
|
|
183
|
+
All four hooks now invoke one hidden route, `internal tmux converge
|
|
184
|
+
--socket-path <path> --session <id> --reason <config-apply|runtime-created|runtime-exited>`,
|
|
185
|
+
and so does `projmux config apply`. At most one convergence worker runs per exact
|
|
186
|
+
tmux server, so a burst of hooks costs one pass instead of one per event. Which
|
|
187
|
+
hooks each config installs is unchanged: the app config carries all four, and the
|
|
188
|
+
standalone snippet carries the two pane-exit hooks only, so a raw `new-window` in
|
|
189
|
+
a session projmux does not own still stays unmanaged.
|
|
190
|
+
|
|
191
|
+
`projmux config apply` — which `make install` and `projmux update apply` run for
|
|
192
|
+
you — rewrites the generated file. A tmux server still holding config from an
|
|
193
|
+
older binary keeps invoking the two retired routes, which now fail; the hooks
|
|
194
|
+
guard every invocation with `>/dev/null 2>&1`, so nothing surfaces, and the next
|
|
195
|
+
`config apply` replaces them. If you hand-copied a projmux `set-hook` line into
|
|
196
|
+
your own `~/.tmux.conf`, re-copy it from `projmux config render standalone`.
|
|
197
|
+
|
|
198
|
+
### Discovery no longer registers Projects
|
|
199
|
+
|
|
200
|
+
**Breaking.** A directory found under a discovery root used to become a Registry
|
|
201
|
+
Project on its own. Any mutation route ran a reconcile prelude that walked the
|
|
202
|
+
configured workdirs and registered every child it did not recognize, so a single
|
|
203
|
+
`projmux create pane` in one repository could add a Project for every sibling
|
|
204
|
+
directory beside it. Scanning a directory is now a scan and nothing else.
|
|
205
|
+
|
|
206
|
+
Three collections, three authorities:
|
|
207
|
+
|
|
208
|
+
| Thing | What it is | What it is not |
|
|
209
|
+
| --- | --- | --- |
|
|
210
|
+
| Workdirs / `PROJMUX_MANAGED_ROOTS` / `PROJMUX_PROJDIR` | Scan roots to look inside | Not a statement that anything inside them is a Project |
|
|
211
|
+
| A discovered child | An unregistered candidate | Not managed identity, however often it is scanned |
|
|
212
|
+
| A Registry Project | Managed identity, with a stable uid | Not derived from a path, a name, or a scan |
|
|
213
|
+
|
|
214
|
+
What registers a Project now:
|
|
215
|
+
|
|
216
|
+
```sh
|
|
217
|
+
projmux create project --root /abs/path/to/repo
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
…or opening that one candidate from the Projects sidebar, which performs the same
|
|
221
|
+
registration for that exact path. Both are idempotent: a root an existing Project
|
|
222
|
+
already claims writes nothing at all. Sibling candidates under the same discovery
|
|
223
|
+
root stay unregistered.
|
|
224
|
+
|
|
225
|
+
What changes for an existing invocation:
|
|
226
|
+
|
|
227
|
+
| Invocation | Before | Now |
|
|
228
|
+
| --- | --- | --- |
|
|
229
|
+
| `create window --project <name>` where `<name>` is a discovered-but-unregistered directory | The reconcile prelude registered it (and every other discovered child) first, then the create succeeded | Exits non-zero naming the exact `--root` to register, or open it once from the sidebar |
|
|
230
|
+
| Any mutation route, with N children under a scan root | Up to N Projects appeared | Zero Projects appear |
|
|
231
|
+
| `switch open <unregistered directory>` | Opened a session; the Project appeared later, as a side effect of some unrelated mutation | Registers that one path, then opens it exactly as before |
|
|
232
|
+
|
|
233
|
+
If you relied on the old behavior to populate the Registry, register the roots
|
|
234
|
+
you actually want once:
|
|
235
|
+
|
|
236
|
+
```sh
|
|
237
|
+
for root in ~/src/*; do projmux create project --root "$root"; done
|
|
238
|
+
projmux get projects
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Pins are typed, and migrate on request
|
|
242
|
+
|
|
243
|
+
**Breaking.** `~/.config/projmux/pins` held one absolute path per line, which
|
|
244
|
+
could not say whether the path was a Project. It is now a typed envelope:
|
|
245
|
+
|
|
246
|
+
```text
|
|
247
|
+
projmux-pins v2
|
|
248
|
+
project proj-kwo4qozry2sr2ycij2g45zyvam
|
|
249
|
+
candidate /home/dev/src/scratch
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
A `project` pin references a Registry Project uid; its displayed root and name
|
|
253
|
+
are projected from the Registry on every read, so the pin survives a rebind, a
|
|
254
|
+
rename, and a missing root. A `candidate` pin references a path that no Project
|
|
255
|
+
claims and stays a path.
|
|
256
|
+
|
|
257
|
+
Nothing migrates behind your back. Reads project a pre-v2 file in memory, so the
|
|
258
|
+
sidebar looks the same before and after; the file is rewritten only when you ask:
|
|
259
|
+
|
|
260
|
+
```sh
|
|
261
|
+
projmux pin project migrate --dry-run # report only
|
|
262
|
+
projmux pin project migrate # store the typed form
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Per line, migration has exactly three outcomes:
|
|
266
|
+
|
|
267
|
+
| Registry Projects claiming the path | Result |
|
|
268
|
+
| --- | --- |
|
|
269
|
+
| exactly one | becomes that Project's `project <uid>` pin |
|
|
270
|
+
| none | stays a `candidate <path>` pin |
|
|
271
|
+
| two or more | the **entire** migration is refused; the pin file and the Registry keep their bytes, and the message names the repair |
|
|
272
|
+
|
|
273
|
+
Repair an ambiguous pin with `projmux rebind project` so one Project claims the
|
|
274
|
+
path, or pin the Project you meant directly with `projmux pin project add
|
|
275
|
+
uid:<uid>`, then re-run the migration. A corrupt file, or one written by a newer
|
|
276
|
+
projmux, is refused rather than partially read.
|
|
277
|
+
|
|
278
|
+
`pin project add|remove|toggle <dir>` is unchanged in argv and now resolves to a
|
|
279
|
+
typed pin under the same rule (one Project → managed, none → candidate, more than
|
|
280
|
+
one → refused). `pin project list` gained a kind column and a `--kind
|
|
281
|
+
project|candidate` filter; the first mutation after an upgrade migrates the file
|
|
282
|
+
first, so it can refuse for the reasons above.
|
|
283
|
+
|
|
284
|
+
Settings > Projects shows the three collections separately: **Additional
|
|
285
|
+
discovery roots** (scan roots), **Pinned Projects** (managed, by uid, with rebind
|
|
286
|
+
and unpin), and **Candidate Pins** (unregistered paths, with register and unpin).
|
|
287
|
+
On Windows the discovery roots stay OS-native paths; drive-letter, case, and
|
|
288
|
+
separator differences are folded only when matching a candidate or migrating a
|
|
289
|
+
legacy pin, never to mint or merge a Project uid.
|
|
290
|
+
|
|
47
291
|
### Legacy compatibility routes removed
|
|
48
292
|
|
|
49
293
|
This release removes the human-facing compatibility argv and old
|
|
50
294
|
pre-namespace internal aliases listed in the [retirement ledger](legacy-cli-retirement.md).
|
|
51
|
-
|
|
52
|
-
and no side effect.
|
|
53
|
-
`
|
|
54
|
-
|
|
55
|
-
`
|
|
295
|
+
Rejected compatibility argv below a surviving mixed root exits 2 with its exact
|
|
296
|
+
canonical replacement, no stdout, and no side effect. Fully removed human roots
|
|
297
|
+
(`current`, `kill`, `notify`, `sessions`, `session-state`, `tag`, `upgrade`, and
|
|
298
|
+
`usage`) and removed internal aliases (`tmux`, `status`, `statusbar`, `preview`,
|
|
299
|
+
`session-popup`, `key-broker`, and `popup-wait-key`) are unknown top-level
|
|
300
|
+
commands and exit 1. Use `internal ...` for generated plumbing and `config
|
|
301
|
+
render|apply` for public configuration work.
|
|
56
302
|
|
|
57
303
|
The mixed roots retain only `attach project`, `focus project|window|pane`, `pin
|
|
58
304
|
project`, and `prune project|snapshot`. Shortcuts and singular/plural resource
|
|
@@ -300,10 +546,17 @@ npm install -g projmux
|
|
|
300
546
|
For npm-managed installs, `projmux update apply` runs:
|
|
301
547
|
|
|
302
548
|
```sh
|
|
549
|
+
<current-projmux> config apply --bin <published-projmux> --socket projmux
|
|
303
550
|
npm install -g projmux@latest
|
|
304
551
|
projmux config apply
|
|
305
552
|
```
|
|
306
553
|
|
|
554
|
+
The first apply must succeed before npm publication begins. The final apply is
|
|
555
|
+
post-publication verification by the new binary. Go, GitHub Release, and source
|
|
556
|
+
`make install` use the same pre-converge → publish → verify contract. A failed
|
|
557
|
+
pre-apply prevents publication; a publication or verification failure returns
|
|
558
|
+
non-zero and prints the exact `projmux config apply --socket projmux` recovery.
|
|
559
|
+
|
|
307
560
|
`npm install -g projmux@latest` is used instead of `npm update -g projmux`
|
|
308
561
|
because `npm update -g` honors the installed semver range and frequently
|
|
309
562
|
refuses to move a global install across a newer minor/major release, leaving it
|
|
@@ -326,9 +579,10 @@ projmux update apply --no-apply # migrate + write config, skip the live reload
|
|
|
326
579
|
projmux update apply --dry-run # print the steps only
|
|
327
580
|
```
|
|
328
581
|
|
|
329
|
-
`projmux update apply`
|
|
330
|
-
|
|
331
|
-
|
|
582
|
+
`projmux update apply` first converges the live route with the current binary
|
|
583
|
+
and exact eventual executable path, reinstalls via `go install`, then reapplies
|
|
584
|
+
with the published binary so a running `-L projmux` server picks up new bindings
|
|
585
|
+
without a restart.
|
|
332
586
|
|
|
333
587
|
The command reads `PROJMUX_PROJDIR` from the calling shell and memoizes the
|
|
334
588
|
primary path to `~/.config/projmux/projdir`, so the new binary keeps the same
|
|
@@ -347,8 +601,9 @@ PROJMUX_PROJDIR="/main/repos:/secondary/repos" projmux update apply
|
|
|
347
601
|
|
|
348
602
|
When `PROJMUX_INSTALLER=github-release`, `projmux update apply` downloads the
|
|
349
603
|
latest matching `projmux_<version>_<goos>_<goarch>.tar.gz` asset from GitHub
|
|
350
|
-
Releases, extracts the binary,
|
|
351
|
-
then runs the new binary's
|
|
604
|
+
Releases, extracts the binary, converges the exact live route, atomically
|
|
605
|
+
replaces the current executable, and then runs the new binary's
|
|
606
|
+
`projmux config apply` — or `config apply --no-reload`
|
|
352
607
|
when `--no-apply` is set, so the keymap schema migration still happens.
|
|
353
608
|
|
|
354
609
|
Set the installer explicitly if you manage a release binary outside npm or Go:
|
package/docs/usage-tracking.md
CHANGED
|
@@ -75,7 +75,31 @@ floor.
|
|
|
75
75
|
|
|
76
76
|
### Codex (`internal/core/usage/adapters/codex`)
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
The native Codex app-server account API is primary:
|
|
79
|
+
|
|
80
|
+
- An explicit `projmux agent usage` request reuses the app-server
|
|
81
|
+
ensure-ready lifecycle, then opens one initialized official stdio-proxy
|
|
82
|
+
connection and calls `account/rateLimits/read` with a bounded deadline.
|
|
83
|
+
Automatic HUD refreshes probe/read an already-running daemon but do not gain
|
|
84
|
+
daemon-start authority.
|
|
85
|
+
- When `rateLimitsByLimitId` is present it is the authoritative multi-bucket
|
|
86
|
+
view; the backward-compatible `rateLimits` mirror is not duplicated. Each
|
|
87
|
+
bucket preserves its map key, nullable `limitId`, nullable `limitName`,
|
|
88
|
+
primary/secondary slot, percentage, nullable reset, and nullable cadence.
|
|
89
|
+
`300` and `10080` minutes project to `5h` and `weekly`. Missing or unknown
|
|
90
|
+
positive cadences remain lossless `quota` rows instead of disappearing.
|
|
91
|
+
- `account/rateLimits/updated` is merged sparsely into the just-read native
|
|
92
|
+
snapshot during one bounded event-settle window. Null account metadata does
|
|
93
|
+
not clear a prior label or identity. The event-refreshed rows then use the
|
|
94
|
+
same Manager throttle, replace, store, and last-known-good path as a read.
|
|
95
|
+
- Validation is row-level. A malformed bucket/window is dropped with a bounded,
|
|
96
|
+
field-only warning while valid siblings remain native. Native and rollout
|
|
97
|
+
rows are never combined in one invocation.
|
|
98
|
+
|
|
99
|
+
When app-server is unavailable, unsupported, disconnected, timed out, or the
|
|
100
|
+
account exposes no supported rate-limit bucket (including API-key-style
|
|
101
|
+
accounts), the adapter invokes the existing newest-rollout collector exactly
|
|
102
|
+
once without attempting login, logout, token refresh, or config writes:
|
|
79
103
|
|
|
80
104
|
- Walks `${HOME}/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-*.jsonl`
|
|
81
105
|
newest-first by mtime (NOT filename — Dropbox-synced rollouts can
|
|
@@ -99,8 +123,10 @@ Local rollout JSONL parser. No network calls.
|
|
|
99
123
|
dropped and reported rather than projected as a genuine `0%`.
|
|
100
124
|
|
|
101
125
|
Codex shares the manager's default `30s` throttle (no
|
|
102
|
-
`ThrottleHinter`)
|
|
103
|
-
|
|
126
|
+
`ThrottleHinter`) and does not implement `BackoffStater`. A successful
|
|
127
|
+
rollout fallback row records `source=rollout` plus the closed fallback reason.
|
|
128
|
+
If neither lane produces rows, Manager preserves the previous source/value and
|
|
129
|
+
adds a closed `stale_reason` to the last-known-good row.
|
|
104
130
|
|
|
105
131
|
### Antigravity (`internal/core/usage/adapters/antigravity`)
|
|
106
132
|
|
|
@@ -163,13 +189,17 @@ For `--model all`, calls `Manager.Collect` (or `ForceCollect` with
|
|
|
163
189
|
Enabled agents, filters by window, and renders the tab-aligned table:
|
|
164
190
|
|
|
165
191
|
```
|
|
166
|
-
MODEL WINDOW PCT RESETS_AT RESET_IN STALE
|
|
192
|
+
MODEL WINDOW PCT RESETS_AT RESET_IN STALE SOURCE REASON
|
|
193
|
+
codex 5h/codex · General 12% 2026-05-07T14:00:00+09:00 - app-server
|
|
167
194
|
claude 5h 80% 2026-05-07T14:00:00+09:00 -
|
|
168
195
|
antigravity quota/gemini-weekly 6% 2026-07-06T16:50:32+09:00 560580s
|
|
169
196
|
claude quota/group-redacted · Model Redacted Alpha 38% 2031-02-03T15:05:06+09:00 - *
|
|
170
197
|
```
|
|
171
198
|
|
|
172
|
-
`
|
|
199
|
+
`SOURCE`/`REASON` are included when source-aware rows are present. Native
|
|
200
|
+
Codex rows report `app-server`; fresh rollout fallback and retained
|
|
201
|
+
last-known-good rows report their closed fallback/stale reasons. `STALE` is
|
|
202
|
+
`*` when `now - UpdatedAt > 10m`. A `--json` payload returns
|
|
173
203
|
the `Snapshot` array; if any adapter is in backoff, the wrapper
|
|
174
204
|
`{snapshots, backoff: {model: {until, consecutive}}}` is emitted
|
|
175
205
|
instead. A backoff note is appended to the human table:
|
|
@@ -215,6 +245,17 @@ excluded; only its aggregate official `5h` and `weekly` rows reach the HUD.
|
|
|
215
245
|
The Settings provider list consumes `aiprovider.UsageSupported()` order, but a
|
|
216
246
|
window toggle exists only when this same projection seam declares it. A future
|
|
217
247
|
provider or an opaque bucket cannot manufacture a window row.
|
|
248
|
+
For native Codex multi-bucket rows, the exact `codex` bucket wins the HUD
|
|
249
|
+
projection, then the legacy empty bucket, then lexical bucket order. The HUD
|
|
250
|
+
compact identity is derived from that same row: a healthy authoritative
|
|
251
|
+
`app-server` row is simply `Codex`, a fresh rollout row is
|
|
252
|
+
`Codex [fallback]`, and a retained last-known-good row is `Codex [stale]`.
|
|
253
|
+
Blank, malformed, or future non-stale provenance also fails conservatively to
|
|
254
|
+
the existing `[fallback]` identity rather than looking native or expanding the
|
|
255
|
+
compact vocabulary. The exact raw source and closed fallback/stale reason stay
|
|
256
|
+
in `agent usage --model codex` table/JSON output and in
|
|
257
|
+
`projmux diagnostics log --component usage`; compact labels never replace
|
|
258
|
+
those fields.
|
|
218
259
|
|
|
219
260
|
Settings > Appearance > Status Bar > Agent Usage HUD can hide the whole HUD,
|
|
220
261
|
a provider, or one supported window. Parent off states preserve child saved
|
|
@@ -250,8 +291,10 @@ The `~` / `~~` markers are the legacy stale vocabulary, carried inside the
|
|
|
250
291
|
indicator while the age text is still rendered and glued to the label once the
|
|
251
292
|
drop order has shed it. Staleness stays muted however far the segment has
|
|
252
293
|
degraded: warning and critical colors are reserved for usage thresholds, not
|
|
253
|
-
cache age. Codex
|
|
254
|
-
|
|
294
|
+
cache age. A healthy Codex row does not need a cosmetic age indicator, while a
|
|
295
|
+
retained Codex last-known-good row opts in and carries the compact `[stale]`
|
|
296
|
+
identity. Its exact closed stale reason remains available on the full table,
|
|
297
|
+
JSON, and diagnostics surfaces.
|
|
255
298
|
|
|
256
299
|
The statusbar usage **popup**'s sync line is a different surface with a
|
|
257
300
|
different meaning (last successful collect, 60s amber threshold) and is
|
|
@@ -284,10 +327,13 @@ When a collection fails, the failure is visible in three places:
|
|
|
284
327
|
projmux diagnostics log --component usage --tail 20
|
|
285
328
|
```
|
|
286
329
|
|
|
287
|
-
The row carries the provider and
|
|
330
|
+
The row carries the provider and closed source/failure enums and nothing else:
|
|
288
331
|
`collect-failed` (whole-adapter failure, `level=error`) or `rows-skipped`
|
|
289
|
-
(partial failure, `level=info`).
|
|
290
|
-
|
|
332
|
+
(partial failure, `level=info`). Codex rollout fallback records
|
|
333
|
+
`source=rollout` plus its closed fallback reason; retained data records
|
|
334
|
+
`source=last-known-good` plus its closed stale reason. A healthy native
|
|
335
|
+
collection writes no row at all. Identical `(provider, source, failure)`
|
|
336
|
+
tuples are recorded at most once per
|
|
291
337
|
process run — the same suppression the notify/focus recorder uses — so a
|
|
292
338
|
repeating failure cannot flood the bounded journal. Journal writes are
|
|
293
339
|
best-effort and never change what the usage command returns.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projmux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "tmux project session manager",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/crevissepartners/projmux#readme",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"package:npm:pack": "scripts/package-npm.sh --pack"
|
|
29
29
|
},
|
|
30
30
|
"optionalDependencies": {
|
|
31
|
-
"@projmux/linux-x64": "0.
|
|
32
|
-
"@projmux/linux-arm64": "0.
|
|
33
|
-
"@projmux/darwin-x64": "0.
|
|
34
|
-
"@projmux/darwin-arm64": "0.
|
|
31
|
+
"@projmux/linux-x64": "0.13.1",
|
|
32
|
+
"@projmux/linux-arm64": "0.13.1",
|
|
33
|
+
"@projmux/darwin-x64": "0.13.1",
|
|
34
|
+
"@projmux/darwin-arm64": "0.13.1"
|
|
35
35
|
}
|
|
36
36
|
}
|