projmux 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ # Installed Codex compatibility (legacy observation)
2
+
3
+ The machine-readable ledger is retained as historical liveness evidence only.
4
+ It is **not** capability authority for `durable-zero-turn-resume` or
5
+ `remote-new-session`; those predicates require the exact executable tuple and
6
+ the Phase-1 conformance record described in
7
+ [`codex-native-required-migration.md`](codex-native-required-migration.md).
8
+ The legacy [`codex-installed-capabilities.json`](codex-installed-capabilities.json)
9
+ schema separates method evidence from the semantic result:
10
+
11
+ - `method` records the CLI/RPC spellings used by one observation. Changing a
12
+ wire method does not itself change capability support.
13
+ - `result` is exactly `supported`, `unsupported`, or `infra-error`.
14
+ An unavailable endpoint and incomplete evidence are infrastructure errors,
15
+ never evidence that the upstream capability is unsupported.
16
+ - `evidence` contains only content-free semantic facts. A supported turn-free
17
+ attach requires an exact living tmux Pane and the same no-turn thread to stay
18
+ loaded with runtime state `idle` or `active` after the creator connection is
19
+ closed.
20
+ - `versions` independently records the installed CLI, managed payload, and
21
+ running app-server tuple. `last_observed` names the canonical probe and run.
22
+
23
+ ## Last observation
24
+
25
+ On 2026-09-02, installed tuple `0.152.0 / 0.152.0 / 0.152.0` produced a living
26
+ Pane observation for the old `turn-free-thread-live-attach` predicate. It is
27
+ now classified `infra-error/evidence-incomplete`, because liveness plus
28
+ `thread/loaded/list` did not prove stored resume and did not observe the exact
29
+ remote-new thread's first real turn. The canonical
30
+ `TestInstalledIsolatedPreTurnBootstrapSmoke` created a thread without a turn,
31
+ observed it in `thread/loaded/list`, started `codex resume --remote unix://` in
32
+ an exact isolated tmux Pane, closed the creating connection, then passed two
33
+ fresh loaded/runtime observations while the exact Pane remained alive. Runtime
34
+ status was `idle`; model, turn, and network calls were zero.
35
+
36
+ The checked-in observation preserves exact branch-head facts from hosted
37
+ [Actions run 33566050834](https://github.com/crevissepartners/projmux/actions/runs/33566050834),
38
+ attempt 1. Aggregate artifact `9823166206`
39
+ (`installed-codex-qualification-33566050834-1`) is the durable last
40
+ observation. It must not be cited as payload-free support.
41
+
42
+ The observation historically extended the earlier `pre-turn-attach` owner.
43
+ That hosted evidence remains run `33560743314`,
44
+ aggregate artifact `9821171919`, where the same tuple's direct pre-turn
45
+ qualification was `pass`. Neither pass is an input to the new exact
46
+ payload-free capability authority.
47
+
48
+ Scheduled and manual `Installed Codex Qualification` artifacts use
49
+ qualification schema v2 and embed this schema-versioned capability ledger.
50
+ `PROJMUX_CODEX_EVIDENCE_RUN` records the exact Actions run/attempt; this ledger
51
+ records `github-actions:33566050834:1`.
52
+
53
+ ## Canonical test list
54
+
55
+ - `TestCapabilityReducerSeparatesMethodFromSemanticResult` — method changes do
56
+ not alter supported/unsupported/infra-error reduction.
57
+ - `TestCapabilityReducerKeepsUnavailableEndpointAsInfraError` — an unavailable
58
+ endpoint cannot become unsupported.
59
+ - `TestInstalledIsolatedPreTurnBootstrapSmoke` — historical owner for
60
+ turn-free start/read/loaded observation and live-Pane liveness; not a
61
+ payload-free support verdict.
62
+ - `TestInstalledExactPayloadFreeCapabilityMatrix` — exact private owner for
63
+ zero-turn start/read/stored-resume plus content-free remote-new liveness. It
64
+ sends no input or turn, so remote-new remains unknown.
65
+ - `TestInstalledCensusDeletionReceiptHasOneOwnerPerPrimitive` — topology and
66
+ protocol ownership plus the Phase 2 merge receipt.
67
+
68
+ The maintained repository-wide list in `docs/agent-workflow.md` records the
69
+ current Phase-1 authority separately.
@@ -1,4 +1,83 @@
1
- # Codex Native-Required Create Migration (0.14.0)
1
+ # Codex Native-Required Create and Payload-Free Fallback
2
+
3
+ ## Current payload-free behavior (Codex 0.153.0)
4
+
5
+ `projmux create codex` and `projmux create agent --provider codex` with no
6
+ payload now open one managed plain-interactive Pane and return one Running
7
+ Agent for each exact target. The same pre-provider decision is used by the AI
8
+ picker's default launch intent. Projmux does not call app-server `Current` or
9
+ `Resolve`, start a thread or turn, or enter a durable-resume barrier for this
10
+ shape. The current Codex 0.153.0 tuple cannot durably hand a zero-turn thread
11
+ to an independent TUI, so a native attempt cannot be a prerequisite for a
12
+ usable payload-free create.
13
+
14
+ This automatic fallback uses the same provider argv and output as the existing
15
+ payload-free `--interactive-only` lane. It is distinguishable only through the
16
+ content-free lifecycle declaration `payload-free-fallback`: `describe agent`
17
+ shows it as `LifecycleDeclared`, and Doctor reports the aggregate
18
+ `payload_free_fallback` count. No prompt, hidden turn, transcript content, or
19
+ provider response is stored to produce that signal.
20
+
21
+ The Phase-7 zero-turn durable-readiness failure is retained only as historical
22
+ negative safety evidence. A typed Failed Agent with no Pane is not functional
23
+ create success. Prompted native create, app-server picker resume, existing
24
+ Agent resume, and generation-pinned routes keep their native contracts.
25
+
26
+ ## Exact payload-free capability authority (Phase 1)
27
+
28
+ Payload-free qualification is now owned by
29
+ `internal/integrations/agents/codexgeneration`. A record is valid for exactly
30
+ one tuple: RoleTUI binary SHA-256 and size, RoleAppServer binary SHA-256 and
31
+ size, app-server version, protocol transport/schema, private socket locator and
32
+ bound-runtime digests, state-domain identity/path digest, and platform/arch.
33
+ Changing any one axis is a cache miss. A missing, corrupt, trailing, future
34
+ schema, stopped, or rebound tuple projects `unknown` and the Phase-0
35
+ `plain-fallback`; no semver family, changelog, or successful `thread/read` is an
36
+ authority substitute.
37
+
38
+ The record reduces two independent executable predicates:
39
+
40
+ - `durable-zero-turn-resume` requires the same hashed exact thread to pass
41
+ zero-turn start, independent read, and stored resume. Read visibility alone
42
+ cannot promote it. Exact 0.153.0 private evidence is read-visible but stored
43
+ resume is `unsupported/no-rollout-found`.
44
+ - `remote-new-session` requires more than a living TUI. The remote-new thread
45
+ and the exact first real input's thread must match, the turn identity must be
46
+ present, and the content-free turn cardinality must be exactly one. An
47
+ unrelated first-turn event or liveness-only observation stays `unknown`.
48
+
49
+ Evidence stores timestamps, digests, closed outcomes, cardinality, and boolean
50
+ identity facts only. It has no field for prompts, provider output, turns,
51
+ transcripts, socket paths, or state paths. Doctor JSON/text and the create
52
+ planner consume the same immutable record projection. Phase 1 intentionally
53
+ maps every verdict—including a supported private observation—to
54
+ `plain-fallback`; the remote-new production launch belongs to Phase 2.
55
+
56
+ The private installed matrix is opt-in and starts/stops only exact root-owned
57
+ app-server and tmux fixtures:
58
+
59
+ ```sh
60
+ smoke_root="$(mktemp -d /tmp/projmux-payload-free-XXXXXX)"
61
+ env -u TMUX -u TMUX_PANE \
62
+ PROJMUX_CODEX_PAYLOAD_FREE_SMOKE_ROOT="$smoke_root" \
63
+ PROJMUX_CODEX_PAYLOAD_FREE_SOURCE_HOME=/absolute/private/copied-source-home \
64
+ PROJMUX_CODEX_PAYLOAD_FREE_0152_0=/absolute/0.152.0/bin/codex \
65
+ PROJMUX_CODEX_PAYLOAD_FREE_0152_1=/absolute/0.152.1/bin/codex \
66
+ PROJMUX_CODEX_PAYLOAD_FREE_0153_0=/absolute/0.153.0/bin/codex \
67
+ go test ./internal/testutil/codexinstalled \
68
+ -run '^TestInstalledExactPayloadFreeCapabilityMatrix$' -count=1 -v
69
+ ```
70
+
71
+ An unset binary row is logged `unavailable`; the fixture never synthesizes a
72
+ tuple. The installed probe sends no input, prompt, or turn. It may record
73
+ content-free TUI liveness/loaded state, but remote-new remains `unknown`
74
+ without a separately supplied exact first-real-input thread/turn observation.
75
+ Use the documented short smoke-root shape: an overlong private tmux socket path
76
+ is rejected before any lifecycle operation and cannot masquerade as liveness.
77
+ After fixture cleanup the socket has no current route identity, so transient
78
+ private evidence cannot be reused by another route.
79
+
80
+ ## Native-required prompted create (0.14.0)
2
81
 
3
82
  0.14.0 makes native authority a requirement for a *prompted* managed Codex
4
83
  create instead of something projmux attempts and silently gives up on. Where an
@@ -37,7 +116,7 @@ Exactly one create shape is native-required. All five conditions must hold:
37
116
  - `--interactive-only` is not passed, and
38
117
  - the create is not carrying a capability selection from the split-UI picker.
39
118
 
40
- Anything outside that shape keeps its previous behavior unchanged. The closed
119
+ The payload-free exception above is selected before this gate. The closed
41
120
  outcome table lives in `internal/app/codex_native_thread.go`
42
121
  (`codexNativeLaunchOutcomeTable`) and is pinned by
43
122
  `TestCodexNativeLaunchOutcomeTableIsClosed`.
@@ -120,9 +199,10 @@ rather than the app-server are unaffected and keep the current CLI lane.
120
199
 
121
200
  ## The escape hatch: `--interactive-only`
122
201
 
123
- `--interactive-only` is the only public spelling that asks for a plain
124
- interactive Codex Agent with no native thread binding, and it is the only way to
125
- reach the plain CLI lane on purpose.
202
+ `--interactive-only` remains the only public spelling that explicitly asks for
203
+ a plain interactive Codex Agent with no native thread binding. Payload-free
204
+ fresh create now chooses that existing lane automatically, without changing the
205
+ flag's spelling or prompted-create meaning.
126
206
 
127
207
  ```sh
128
208
  projmux create codex --interactive-only -- "interactive task"
@@ -151,8 +231,8 @@ projmux create agent --provider codex --interactive-only -- "interactive task"
151
231
  | Surface | Behavior |
152
232
  | --- | --- |
153
233
  | `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. |
154
- | 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. |
155
- | 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. |
234
+ | Payload-free Codex create (`projmux create codex` with no payload) | pre-provider plain fallback: one usable managed Pane and one Running Agent per exact target, with zero native thread/turn/resume-barrier calls and the content-free `payload-free-fallback` declaration. |
235
+ | Multi-operand payload (`projmux create codex -- a b`) | unchanged refusal. Native `turn/start` accepts one text item; Projmux does not join operands or silently reinterpret them as a plain fallback. |
156
236
  | Claude and Antigravity | unchanged lifecycle, fan-out, and hook activation contract. |
157
237
  | Public hook env and payload schema | unchanged. |
158
238
  | 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. |
@@ -160,7 +240,7 @@ projmux create agent --provider codex --interactive-only -- "interactive task"
160
240
  ## Verifying this yourself
161
241
 
162
242
  ```sh
163
- go test ./internal/app/ -run 'TestCodexInstallCapability|TestInteractiveOnlyIsTheOnlyPlainCodexLaneAndBothSpellingsAreEquivalent|TestDefaultNativeCodexFanOutRefusesWithZeroMutationsAndInteractiveOnlyKeepsCardinality|TestEmptyPromptCodexCreateIsByteForByteUnchangedByTheNativeRequiredGate|TestClaudeAndAntigravityLifecycleAndHookContractAreUnchangedByTheNativeGate|TestUnavailableNative|TestCodexNativeLaunchOutcomeTableIsClosed'
243
+ go test ./internal/app/ -run 'TestCodexCreatePayloadCardinalityInteractiveOnlyAndReadinessOutcomeTable|TestPayloadFreeCodexCreateUsesSafePlainFallbackAndInteractiveOnlyEquivalentLane|TestPayloadFreeCodexPlainLaunchFailureRollsBackWithoutProviderMutation|TestEmptyPromptCodexSplitProducersKeepOnePlainCLILane|TestPromptedNativeCodexCreateIssuesOneTurnAndNeverRepeatsThePromptInPaneArgv|TestNativeResumePicker|TestUnavailableNativeResume|TestClaudeAndAntigravity|TestCodexNativeLaunchOutcomeTableIsClosed'
164
244
  go test ./internal/integrations/agents/codexappserver/ -run TestStartDefaultThread
165
245
  ```
166
246
 
@@ -172,7 +252,16 @@ go test ./internal/integrations/agents/codexappserver/ -run TestStartDefaultThre
172
252
  | Picker resume refuses instead of rebinding onto the rollout lane, and offers no launch-mode escape hatch | `TestUnavailableNativePickerResumeRefusesInsteadOfRebindingOntoTheRolloutLane` |
173
253
  | `agent resume` keeps its safe fallback to one provider resume | `TestUnavailableNativeResumeKeepsTheStoredConversationOnTheProviderResumeLane` |
174
254
  | Both `--interactive-only` spellings are equivalent, and non-Codex providers refuse it at zero transactions | `TestInteractiveOnlyIsTheOnlyPlainCodexLaneAndBothSpellingsAreEquivalent` |
175
- | Empty-prompt create is unchanged | `TestEmptyPromptCodexCreateIsByteForByteUnchangedByTheNativeRequiredGate` |
255
+ | Payload cardinality × `--interactive-only` × readiness stays a closed pre-provider decision table | `TestCodexCreatePayloadCardinalityInteractiveOnlyAndReadinessOutcomeTable` |
256
+ | Canonical and shortcut payload-free create are byte/argv-equivalent to the explicit plain lane and touch no provider route | `TestPayloadFreeCodexCreateUsesSafePlainFallbackAndInteractiveOnlyEquivalentLane` |
257
+ | Exact tuple keys, immutable cache fixed points, and one-axis drift/corrupt/future/trailing fail closed | `TestCapabilityCacheInvalidatesEveryExactTupleAxis`, `TestCapabilityCacheCorruptFutureAndTrailingRecordsResolveUnknown` |
258
+ | Read visibility and stored resume reduce independently; remote-new requires the exact first real thread/turn | `TestExactPayloadFreeCapabilitySeparatesReadVisibleFromStoredResumable`, `TestRemoteNewCapabilityRequiresExactFirstRealInputThreadAndTurn` |
259
+ | Doctor and create plan consume byte-semantic identical capability projections while Phase 1 stays plain | `TestCodexPayloadFreeDoctorAndCreatePlannerShareExactRecordProjection`, `TestCodexPayloadFreeUnknownCapabilityCannotBypassPhaseZeroFallback` |
260
+ | Available exact installed binaries are qualified independently in creator-live/closed private rows | `TestInstalledExactPayloadFreeCapabilityMatrix` |
261
+ | Saved-default, provider-picker, and direct-provider AI intents produce one managed plain lane without native mutation | `TestEmptyPromptCodexSplitProducersKeepOnePlainCLILane` |
262
+ | A failed plain launch rolls Registry and tmux back and never tries a provider lane | `TestPayloadFreeCodexPlainLaunchFailureRollsBackWithoutProviderMutation` |
263
+ | The installed outcome requires a Running plain Agent/Pane, no session ref, zero provider-thread delta, diagnostic signals, isolated socket cleanup, and ambient mutation zero | `TestInstalledPayloadFreePlainFallbackOutcomeSmoke` |
264
+ | Phase-7 readiness failure remains negative safety evidence rather than functional create success | `TestPhase7PayloadFreeReadinessFailureIsNegativeSafetyEvidenceOnly`, `TestInstalledPayloadFreeCreateOutputClassificationRequiresFunctionalPlainSuccess` |
176
265
  | Claude and Antigravity are unchanged | `TestClaudeAndAntigravityLifecycleAndHookContractAreUnchangedByTheNativeGate` |
177
266
  | One payload sends exactly one `turn/start` and never repeats the prompt in Pane argv | `TestPromptedNativeCodexCreateIssuesOneTurnAndNeverRepeatsThePromptInPaneArgv` |
178
267
  | The post-mutation row still refuses a second lane | `TestIndeterminateNativeCreateRefusesASecondLaneAndWritesZero` |
@@ -0,0 +1,83 @@
1
+ # Column profiles
2
+
3
+ Plural Registry reads (`get projects|windows|panes|agents`) now default to
4
+ `NAME STATUS ACTIONS`. The route already selects the resource kind. Runtime
5
+ reads default to their identity, containment, and classification columns. Use `-o wide` (or `--output wide`) to print the full
6
+ column profile. Terminal width and data length never select a profile.
7
+
8
+ Removing KIND changes these four default tables from four columns to three and
9
+ moves NAME, STATUS and ACTIONS one position left. Existing positional parsers
10
+ can break; request `-o wide` or recover each resource's `kind` from `-o json`.
11
+ Mixed Registry/Runtime pickers keep KIND in both profiles.
12
+
13
+ Scripts that need diagnostic columns must request `-o wide` or a structured projection explicitly. Registry JSON items
14
+ retain `context.value`, `context.source`, and `context.observed`; `describe` keeps
15
+ resource and termination detail. Existing JSON, metadata, uid, name, ref and none
16
+ projections retain their contracts, as do singular reads and selector cardinality.
17
+ `wide` is accepted only on the seven plural columnar read routes.
18
+
19
+ | Surface / kind | Default | Wide |
20
+ | --- | --- | --- |
21
+ | Registry Project | NAME STATUS ACTIONS | KIND NAME STATUS ACTIONS CONTEXT SOURCE OBSERVED AGE |
22
+ | Registry Window | NAME STATUS ACTIONS | KIND NAME STATUS ACTIONS CONTEXT SOURCE OBSERVED PROJECT AGE |
23
+ | Registry Pane | NAME STATUS ACTIONS | KIND NAME STATUS ACTIONS CONTEXT SOURCE OBSERVED PROJECT WINDOW AGENT TERMINATION AGE |
24
+ | Registry Agent | NAME STATUS ACTIONS | KIND NAME STATUS ACTIONS CONTEXT SOURCE OBSERVED INTERACTION PROJECT WINDOW SESSION TERMINATION AGE |
25
+ | Runtime Session | SESSION NAME CLASS | SESSION NAME CLASS UID RESOURCE REASON |
26
+ | Runtime Window | WINDOW SESSION NAME CLASS | WINDOW SESSION NAME CLASS UID RESOURCE REASON |
27
+ | Runtime Pane | PANE WINDOW TITLE CLASS | PANE WINDOW TITLE CLASS UID RESOURCE REASON |
28
+
29
+ Registry defaults omit KIND, CONTEXT, SOURCE, OBSERVED, AGE and each kind's owner-chain,
30
+ INTERACTION, SESSION and TERMINATION diagnostics shown in the wide column above.
31
+ Runtime defaults omit UID, RESOURCE and REASON. Wide retains their full values;
32
+ stdout is unbounded and copyable, including long names, context, provider session
33
+ references and diagnostic reasons. Free text may contain spaces: structured JSON
34
+ is the recovery surface for machine consumers of those values. An empty resource
35
+ list still emits no bytes, while an empty runtime report retains its host and
36
+ availability header.
37
+
38
+ ACTIONS comes from the existing Registry navigation projector over the same
39
+ invocation graph used for context and runtime observation. It adds no tmux
40
+ observation or Registry write. Eligible actions retain their existing order;
41
+ ControlSession-owned rows retain their existing empty action set (`-` in the
42
+ CLI). NAME remains the durable Registry address; CONTEXT remains presentation.
43
+
44
+ The Registry and Runtime diagnostics pickers consume the same typed catalog in
45
+ `internal/app/column_profiles.go` and open with compact columns:
46
+
47
+ | Picker | Compact default | Explicit wide |
48
+ | --- | --- | --- |
49
+ | Registry mixed | KIND NAME STATUS ACTIONS | KIND NAME STATUS PROGRESS TERMINATION ACTIONS RUNTIME UID |
50
+ | Runtime mixed | KIND ID IN NAME CLASS | KIND ID IN NAME CLASS RESOURCE REASON |
51
+
52
+ Press **Alt-W** to toggle wide columns and press it again to return to compact.
53
+ The footer shows the effective key and the next projection. The query, selected
54
+ row, row order, full search values and current observation survive each toggle.
55
+ The choice lasts for this open picker, including visits to a row's action menu;
56
+ closing and reopening starts compact. Toggling writes no Registry or config.
57
+
58
+ Settings > Keybindings > Sidebar & picker actions exposes **Registry Inspector**
59
+ and **Runtime Diagnostics**, each with **Toggle compact / wide columns**. Their
60
+ canonical IDs are `resource-inspector.columns.toggle` and
61
+ `runtime-diagnostics.columns.toggle`. Existing single-key overrides are supported;
62
+ there is no saved/global column profile or picker-local key sequence.
63
+
64
+ Compact Registry NAME keeps the full invocation context (or the full durable name
65
+ when context is empty), and omits the repeated provider/phase and role adornments
66
+ that wide NAME retains. A full-UID/Hangul hierarchy with the existing action lists
67
+ fits the 75-cell content budget at 80 columns without cutting NAME. Compact uses
68
+ one separating cell per column; a previously clipped 76-cell regression row now
69
+ uses 73 cells with all values intact. Wide retains two separating cells. The picker
70
+ adds no NAME, RESOURCE, RUNTIME or REASON truncation bounds. Wide keeps the catalog
71
+ field order and full projected values, including the existing PROGRESS producer's
72
+ own policy.
73
+
74
+ Wide rows can be clipped by the fixed viewport. The representative 142-cell
75
+ Registry row clips at 80/120/180-column clients and fits from a 184-column client
76
+ with the production 80%-width borderless popup. The native frame, scrollbar gutter
77
+ and pointer reserve five cells: a 180-column client gives 139 label cells, and a
78
+ 184-column client gives 142. Actual action lists, runtime targets and free text can make rows wider,
79
+ so no fixed client width guarantees all wide values. Enter opens the existing
80
+ row action menu with each wide field on a separate detail row. JSON/`describe`
81
+ and CLI `-o wide` provide recovery for values that still do not fit. There is no
82
+ horizontal navigation or pagination; footer overflow and provider behavior are
83
+ unchanged.
@@ -596,6 +596,44 @@ without an app-server read. Only a thread-only candidate is validated with
596
596
  starts the shared daemon. Failure retains the persisted id or uses the current
597
597
  rollout fallback, and a read response can never substitute a different id.
598
598
 
599
+ ## Release channel
600
+
601
+ Which release line this install is offered updates from. The axis is orthogonal
602
+ to the install path: every installer can be judged on either channel.
603
+
604
+ Files:
605
+
606
+ ```text
607
+ ~/.config/projmux/config.toml # global/user
608
+ ```
609
+
610
+ Schema:
611
+
612
+ ```toml
613
+ [update]
614
+ release_channel = "rc" # "stable" (default) or "rc"
615
+ ```
616
+
617
+ Set it from Settings > About > Updates > Release channel. The default is
618
+ `stable`, and it is the default in the strong sense: an install that has never
619
+ touched the toggle, an empty value, and a channel this binary does not
620
+ recognise all resolve to `stable`, so no configuration mistake can put an
621
+ install on prereleases it never asked for.
622
+
623
+ Resolution priority is:
624
+
625
+ 1. `[update] release_channel`, once the key exists
626
+ 2. `PROJMUX_RELEASE_CHANNEL`
627
+ 3. built-in default (`stable`)
628
+
629
+ The stored setting takes the axis away from the environment only once it
630
+ exists, which is why merely opening the Settings row does not write it. An
631
+ install driven by `PROJMUX_RELEASE_CHANNEL` therefore keeps its opt-in until
632
+ somebody actually uses the toggle — and after that, turning the toggle off puts
633
+ the install back on the stable line even while the environment variable is
634
+ still set. Turning it off does not downgrade a prerelease that is already
635
+ installed; that install stays put until its stable line ships.
636
+
599
637
  ## Environment Variables
600
638
 
601
639
  | Variable | Purpose |
@@ -619,6 +657,7 @@ rollout fallback, and a read response can never substitute a different id.
619
657
  | `PROJMUX_SESSIONSTATE_DEBUG` | When non-empty, quiet autosave surfaces suppressed session-state errors to stderr. |
620
658
  | `PROJMUX_FOCUS_DEBUG` | When non-empty, `projmux focus` prints one telemetry line to stderr. |
621
659
  | `PROJMUX_INSTALLER` | Installer source hint used by update flows. npm installs set this automatically; advanced release installs can set `github-release`. |
660
+ | `PROJMUX_RELEASE_CHANNEL` | Release channel the update judgment is made against, orthogonal to `PROJMUX_INSTALLER`. Only an exact `rc` opts in; unset, empty, and unrecognised values all mean the default `stable` channel, which never sees a prerelease. An rc install is answered with whichever of the stable and rc lines is newer, so it returns to stable as soon as that line ships. Read only until `[update] release_channel` exists; see [Release channel](#release-channel). |
622
661
  | `PROJMUX_SHELL_UPDATE_CHECK_TIMEOUT_MS` | Timeout in milliseconds for the best-effort release check attempted by `projmux shell` when the update cache is missing or stale. Invalid, zero, or negative values use the default. |
623
662
 
624
663
  ## Welcome State
@@ -872,14 +911,14 @@ while `on` and `off` take precedence. Auto-save only updates the latest
872
911
  snapshot. Named snapshots are manual and are never updated by auto-save.
873
912
 
874
913
  With no saved preference, Project open from the Alt-1 sidebar shows a native
875
- `Start project` step with exactly `Continue project` and `Open fresh`.
914
+ `Start project` step with exactly `Continue project` and `Recreate Project`.
876
915
  Settings > Projects > Project Sidebar > Closed Project startup reports this as
877
- `Continue project / Open fresh - default`. A saved `on` keeps the same explicit
878
- choice and reports `Continue project / Open fresh - on - saved`. A saved `off`
916
+ `Continue project / Recreate Project - default`. A saved `on` keeps the same explicit
917
+ choice and reports `Continue project / Recreate Project - on - saved`. A saved `off`
879
918
  reports `Continue project - off - saved` and skips the picker: a registered root
880
919
  continues, while an unregistered root follows the existing Fresh adjudication.
881
920
  Resolving or cancelling the missing-file default never creates the preference
882
- file or changes saved bytes or mtime. `Open fresh` atomically replaces the old Project graph
921
+ file or changes saved bytes or mtime. `Recreate Project` confirms first, then atomically replaces the old Project graph
883
922
  with a new Project UID and a new canonical Window/shell UID pair. Exactly one
884
923
  same-root Project claimant remains. Snapshot bytes, the root directory,
885
924
  Git/worktree data, and the trust decision remain unchanged. Esc returns to