projmux 0.15.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-ko.md +3 -4
- package/README.md +3 -3
- package/docs/agent-message-replies.md +82 -2
- package/docs/ai-agent-shortcuts.md +6 -5
- package/docs/architecture.md +149 -63
- package/docs/claude-coordination-endpoints.md +192 -21
- package/docs/cli-guide.md +322 -124
- package/docs/cli.md +605 -500
- package/docs/codex-installed-compatibility.md +6 -11
- package/docs/codex-native-required-migration.md +1 -59
- package/docs/configuration.md +164 -176
- package/docs/globalization.md +11 -1
- package/docs/heterogeneous-dialogue-canary.md +8 -3
- package/docs/hooks.md +83 -32
- package/docs/keybindings.md +108 -3
- package/docs/legacy-cli-retirement.md +3 -3
- package/docs/legacy-diagnostics-inventory.md +4 -4
- package/docs/native-picker.md +3 -5
- package/docs/notify-queue.md +1 -1
- package/docs/operational-diagnostics.md +53 -31
- package/docs/pr-guideline.md +66 -22
- package/docs/release.md +97 -0
- package/docs/replacement-contract.md +66 -58
- package/docs/resource-attribution.md +2 -2
- package/docs/session-restore.md +46 -80
- package/docs/settings-ia.md +43 -20
- package/docs/statusbar.md +25 -22
- package/docs/testing.md +15 -0
- package/docs/theme-palette.md +14 -0
- package/docs/tmux-surface-inventory.md +8 -10
- package/docs/troubleshooting.md +2 -4
- package/docs/upgrading.md +142 -7
- package/docs/usage-tracking.md +56 -53
- package/package.json +5 -5
- package/docs/agent-workflow.md +0 -2123
- package/docs/codex-generation-pool.md +0 -623
- package/docs/codex-stored-qualification.md +0 -45
|
@@ -1,623 +0,0 @@
|
|
|
1
|
-
# Codex app-server generation pool — Phase 0–5 contract
|
|
2
|
-
|
|
3
|
-
Phase 0 adds the identity, validation, qualification, immutable bundle, and
|
|
4
|
-
read-only planning contracts needed by a future bounded app-server pool. It
|
|
5
|
-
does not start or stop a product endpoint, change a current pointer, dial more
|
|
6
|
-
than the existing broker endpoint, or change Agent create/resume behavior.
|
|
7
|
-
|
|
8
|
-
Phase 2 adds a dark, bounded endpoint runtime pool and a private generation
|
|
9
|
-
host. It still does not change a current pointer or Agent create/resume
|
|
10
|
-
routing: even an initialized, ready `Preparing` endpoint refuses fresh create
|
|
11
|
-
admission before a provider, Registry, tmux, or lifecycle write.
|
|
12
|
-
|
|
13
|
-
Phase 4 adds the explicit rolling operation that prepares one private
|
|
14
|
-
successor, commits admission-current at most once, and marks the old generation
|
|
15
|
-
Draining without moving its live Agents. It does not stop the old endpoint,
|
|
16
|
-
resume a successor thread, rewrite an Agent endpoint ref, relaunch a Pane,
|
|
17
|
-
retire a generation, release a bundle lease, or adopt a foreign runtime.
|
|
18
|
-
|
|
19
|
-
Phase 5 adds a distinct linked handover journal. It pins the generation-wide
|
|
20
|
-
Agent/Pane/thread target set, fences admission and live bindings, stops only an
|
|
21
|
-
exact Projmux-owned old generation, resumes completed persisted threads on the
|
|
22
|
-
qualified successor, observes every snapshot before any cutover, then performs
|
|
23
|
-
endpoint CAS, same-Pane relaunch, terminal retirement, and old lease release.
|
|
24
|
-
It never replays prompt, turn, approval, or content and never kills or adopts a
|
|
25
|
-
foreign/default endpoint.
|
|
26
|
-
|
|
27
|
-
## Identity and schema
|
|
28
|
-
|
|
29
|
-
A native Codex Agent has two independent generation axes:
|
|
30
|
-
|
|
31
|
-
- `Pane.status.activation.generation` identifies one Pane child
|
|
32
|
-
materialization. Existing lifecycle and termination guards keep this meaning.
|
|
33
|
-
- `Agent.status.sessionRef.codex.endpoint` identifies the Codex state domain and
|
|
34
|
-
app-server generation that durably owns the thread.
|
|
35
|
-
|
|
36
|
-
The additive endpoint shape is:
|
|
37
|
-
|
|
38
|
-
```json
|
|
39
|
-
{
|
|
40
|
-
"stateDomainID": "opaque-state-domain",
|
|
41
|
-
"endpointGenerationID": "opaque-endpoint-generation"
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
The optional live activation authority is:
|
|
46
|
-
|
|
47
|
-
```json
|
|
48
|
-
{
|
|
49
|
-
"stateDomainID": "opaque-state-domain",
|
|
50
|
-
"endpointGenerationID": "opaque-endpoint-generation",
|
|
51
|
-
"brokerRuntimeID": "opaque-broker-runtime",
|
|
52
|
-
"connectionEpoch": 1,
|
|
53
|
-
"bindingEpoch": 1
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Phase 1 adds an optional durable projection input beside that endpoint. Planned
|
|
58
|
-
states carry the exact operation that authorized them; ordinary states carry
|
|
59
|
-
no operation:
|
|
60
|
-
|
|
61
|
-
```json
|
|
62
|
-
{
|
|
63
|
-
"state": "recovering",
|
|
64
|
-
"operation": {
|
|
65
|
-
"id": "opaque-operation",
|
|
66
|
-
"endpoint": {
|
|
67
|
-
"stateDomainID": "opaque-state-domain",
|
|
68
|
-
"endpointGenerationID": "opaque-endpoint-generation"
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
All five live authority dimensions must match. Connection and binding epochs
|
|
75
|
-
remain broker-local counters; equal numbers from another endpoint generation or
|
|
76
|
-
restarted broker are not authority. A registry written before these optional
|
|
77
|
-
fields existed decodes and re-encodes with them absent. Absence is
|
|
78
|
-
`legacy-generation-unavailable`, never an inference to the current endpoint.
|
|
79
|
-
|
|
80
|
-
## Bounded model and read-only plan
|
|
81
|
-
|
|
82
|
-
The v1 model permits at most two non-retired slots: one `current` and one
|
|
83
|
-
draining obligation (`draining` or `handover-pending`). Preparing, recovering,
|
|
84
|
-
and blocked candidates also consume a live slot. It rejects, among other
|
|
85
|
-
invalid states:
|
|
86
|
-
|
|
87
|
-
- two current generations;
|
|
88
|
-
- two draining/handover-pending generations;
|
|
89
|
-
- more than two non-retired generations;
|
|
90
|
-
- a live Agent obligation without an admission-current generation;
|
|
91
|
-
- obligations naming a missing or retired generation.
|
|
92
|
-
|
|
93
|
-
`codexgeneration.PlanUpgrade` is a pure value reduction. It has no Registry
|
|
94
|
-
writer, provider, tmux, process, or filesystem/current-pointer adapter. JSON and
|
|
95
|
-
text output include the exact Agent UID and endpoint-generation ID for every
|
|
96
|
-
blocker and an explicit zero counter for each mutation class. An exact-current
|
|
97
|
-
unmanaged endpoint is attach-only: the plan can report it, but cannot stop,
|
|
98
|
-
restart, kill, or adopt it.
|
|
99
|
-
|
|
100
|
-
## Shared-state qualification gate
|
|
101
|
-
|
|
102
|
-
The pool lane remains closed unless one exact old/new version pair passes all
|
|
103
|
-
of the following in a single isolated state domain:
|
|
104
|
-
|
|
105
|
-
1. different private sockets concurrently create and turn different threads;
|
|
106
|
-
2. both endpoints read and list the exact threads without duplicates;
|
|
107
|
-
3. both endpoints survive an observed crash/exit and restart barrier;
|
|
108
|
-
4. a successor resume is not invoked while the old generation owns the same
|
|
109
|
-
thread;
|
|
110
|
-
5. after the exact old process stops, only the completed persisted thread is
|
|
111
|
-
resumed and its exact completed-turn snapshot is observed;
|
|
112
|
-
6. cross-thread writes, store corruption, and ambient mutations remain zero;
|
|
113
|
-
7. copied `auth.json` and `config.toml` are shared only inside the private state
|
|
114
|
-
domain at mode `0600` and no values enter the receipt.
|
|
115
|
-
|
|
116
|
-
The strict `codexgeneration.QualificationResult` persists only versions,
|
|
117
|
-
booleans, counters, a closed verdict, and a closed reason. If distinct-thread
|
|
118
|
-
concurrency or same-thread ownership is `no`—or any other required fact is
|
|
119
|
-
missing—`GateQualification` keeps Phase 2+ closed and selects
|
|
120
|
-
`single-endpoint-journaled-handover`. It never stores prompts, responses,
|
|
121
|
-
credentials, paths, sockets, or rollout content.
|
|
122
|
-
|
|
123
|
-
## Release bundle lease
|
|
124
|
-
|
|
125
|
-
`codexbundle` retains the complete executable set a generation needs:
|
|
126
|
-
|
|
127
|
-
- the `codex` binary in both server and TUI roles;
|
|
128
|
-
- `codex-code-mode-host`, bundled `rg`, and bundled `bwrap` as helpers.
|
|
129
|
-
|
|
130
|
-
The lease ID hashes the canonical manifest, which covers version, protocol
|
|
131
|
-
range, role set, relative path, file mode, size, and SHA-256 for every artifact.
|
|
132
|
-
Creation copies into a private staging directory, verifies it, and only then
|
|
133
|
-
atomically commits the content-addressed directory. It never writes a current
|
|
134
|
-
symlink. Missing roles, manifest/hash/size/mode drift, or an unsupported
|
|
135
|
-
protocol range refuse before a usable lease is returned. `Open` repeats the
|
|
136
|
-
verification before launch, so removing the mutable upstream release directory
|
|
137
|
-
does not affect a valid lease.
|
|
138
|
-
|
|
139
|
-
## Validation workflow
|
|
140
|
-
|
|
141
|
-
The deterministic suite is part of `make test`. The real-Codex test is opt-in
|
|
142
|
-
and qualifies whichever version pair the operator declares. It needs the two
|
|
143
|
-
standalone executables of that pair plus a source Codex home containing
|
|
144
|
-
`auth.json` and `config.toml`. `scripts/test-generation-pool-qualification.sh`
|
|
145
|
-
owns the isolated roots and always leaves one terminal typed record behind:
|
|
146
|
-
|
|
147
|
-
```sh
|
|
148
|
-
PROJMUX_CODEX_GENERATION_OLD="/absolute/path/to/old/bin/codex" \
|
|
149
|
-
PROJMUX_CODEX_GENERATION_NEW="/absolute/path/to/new/bin/codex" \
|
|
150
|
-
PROJMUX_CODEX_GENERATION_SOURCE_HOME="/absolute/path/to/source-codex-home" \
|
|
151
|
-
scripts/test-generation-pool-qualification.sh <old-version> <new-version> artifacts/pair
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
The runner writes `artifacts/pair/outcome.json` on every exit — `pass`, `fail`,
|
|
155
|
-
`unsupported` when a declared binary or the credentialed state is unavailable,
|
|
156
|
-
or `infra-error` — and `artifacts/pair/receipt.json` whenever the harness
|
|
157
|
-
reached a verdict. Set `PROJMUX_CODEX_GENERATION_BUNDLE_TMPDIR` to place the
|
|
158
|
-
~700 MiB bundle root somewhere other than the temporary directory; the smoke
|
|
159
|
-
root itself must stay there so the private sockets fit `sun_path`.
|
|
160
|
-
|
|
161
|
-
The test can also be driven directly. Each declared version must match what its
|
|
162
|
-
binary reports, and the receipt path must be absolute:
|
|
163
|
-
|
|
164
|
-
```sh
|
|
165
|
-
smoke_root="$(mktemp -d /tmp/projmux-codex-generation-XXXXXX)"
|
|
166
|
-
bundle_root="$(mktemp -d /path/with/at-least-700MiB/projmux-codex-bundles-XXXXXX)"
|
|
167
|
-
env -u TMUX -u TMUX_PANE \
|
|
168
|
-
PROJMUX_CODEX_GENERATION_SMOKE_ROOT="$smoke_root" \
|
|
169
|
-
PROJMUX_CODEX_GENERATION_BUNDLE_SMOKE_ROOT="$bundle_root" \
|
|
170
|
-
PROJMUX_CODEX_GENERATION_OLD="/absolute/path/to/old/bin/codex" \
|
|
171
|
-
PROJMUX_CODEX_GENERATION_NEW="/absolute/path/to/new/bin/codex" \
|
|
172
|
-
PROJMUX_CODEX_GENERATION_OLD_VERSION="<old-version>" \
|
|
173
|
-
PROJMUX_CODEX_GENERATION_NEW_VERSION="<new-version>" \
|
|
174
|
-
PROJMUX_CODEX_GENERATION_SOURCE_HOME="/absolute/path/to/source-codex-home" \
|
|
175
|
-
PROJMUX_CODEX_GENERATION_RECEIPT="/absolute/path/to/receipt.json" \
|
|
176
|
-
go test ./internal/testutil/codexinstalled \
|
|
177
|
-
-run '^TestInstalledIsolatedGenerationPoolQualification$' -count=1 -v
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
The root must be an empty child of the system temporary directory. The test
|
|
181
|
-
uses two root-contained sockets, performs semantic readiness/completion/exit
|
|
182
|
-
barriers rather than fixed sleeps, removes the leased source directories, and
|
|
183
|
-
deletes only the exact owned root after both children and sockets are gone.
|
|
184
|
-
|
|
185
|
-
The declared pair is the only operator input. Every evidence boolean and
|
|
186
|
-
counter in the receipt stays measured by the harness, and `Validate` recomputes
|
|
187
|
-
the verdict from that evidence, so editing either a counter or the verdict in
|
|
188
|
-
the file makes it stop decoding rather than pass a stronger claim.
|
|
189
|
-
|
|
190
|
-
Recomputing the verdict is not, by itself, a check on the evidence. A YES
|
|
191
|
-
verdict is fully determined by the acceptance booleans and the violation
|
|
192
|
-
counters — every boolean true, every violation zero — so a receipt written by
|
|
193
|
-
hand to say YES is self-consistent and decodes cleanly. The **coverage
|
|
194
|
-
counters** are what separates it from a measured one: `observedThreadTurns`,
|
|
195
|
-
`observedThreadReads`, `observedCrashRestarts`, and `observedBundleLaunches`
|
|
196
|
-
tally what the harness actually did, and an acceptance claim with none of them
|
|
197
|
-
behind it is refused at the gate as unbacked. This does not make forgery
|
|
198
|
-
impossible; it makes the receipt state its coverage, so the evidence set that
|
|
199
|
-
costs nothing to write no longer passes. Receipts predating the coverage
|
|
200
|
-
counters carry `schemaVersion` 1 and are refused as incomplete rather than read
|
|
201
|
-
forward — there is no coverage in them to check.
|
|
202
|
-
|
|
203
|
-
### Installing a receipt
|
|
204
|
-
|
|
205
|
-
The emitted `receipt.json` reaches the generation entry paths through one route:
|
|
206
|
-
|
|
207
|
-
```sh
|
|
208
|
-
projmux agent app-server upgrade qualify --receipt /absolute/path/to/receipt.json
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
`qualify` re-reads the file through the same decoder and the same gate the entry
|
|
212
|
-
paths use, refuses it on exactly their terms, and stores it under the version
|
|
213
|
-
pair it names. Only an accepted receipt is stored, so the presence of a stored
|
|
214
|
-
receipt is itself the claim the gate honors. Both doors into a generation switch
|
|
215
|
-
read that store: managed activation refuses before it writes the journal, and a
|
|
216
|
-
handover resume refuses before it drives any effect. A receipt qualifies the one
|
|
217
|
-
pair it names and no other, and the refusal for a pair with no stored receipt
|
|
218
|
-
names the two commands that produce and install one.
|
|
219
|
-
|
|
220
|
-
`projmux doctor --section integrations` reports the saved version pairs and
|
|
221
|
-
their verdicts even without a generation journal. Text, JSON, and support
|
|
222
|
-
`doctor.json` distinguish an empty store from damaged or misfiled receipts;
|
|
223
|
-
see [Stored Codex version-pair qualification](codex-stored-qualification.md).
|
|
224
|
-
|
|
225
|
-
The same `receipt.json` also goes verbatim into the `qualification` field of an
|
|
226
|
-
`agent app-server upgrade plan|apply --request <absolute>.json` document. The
|
|
227
|
-
upgrade request requires that receipt's version pair to match the exact current
|
|
228
|
-
and target generation versions.
|
|
229
|
-
|
|
230
|
-
The `Generation Pool Qualification` workflow is the scheduled-lane counterpart.
|
|
231
|
-
It is `workflow_dispatch`-only and takes the pair as inputs: the qualification
|
|
232
|
-
needs a credentialed Codex state domain, which a GitHub-hosted runner does not
|
|
233
|
-
have, so a nightly run could only ever report `unsupported`. The lane installs
|
|
234
|
-
both declared versions, runs the same script, uploads the typed record, and is
|
|
235
|
-
green only on a measured `pass`.
|
|
236
|
-
|
|
237
|
-
Payload-free fresh create now has a stronger product boundary than the
|
|
238
|
-
generation model: canonical CLI, shortcut, and default AI intent choose the
|
|
239
|
-
plain-interactive lane before consulting `Current` or `Resolve`. They therefore
|
|
240
|
-
create no generation obligation, provider thread, turn, or resume barrier. The
|
|
241
|
-
generation-pool qualification below may still exercise a payload-free thread
|
|
242
|
-
directly as a provider capability probe; that lower-layer negative evidence is
|
|
243
|
-
not functional `projmux create codex` success.
|
|
244
|
-
|
|
245
|
-
Payload-free executable qualification is also distinct from pool health. The
|
|
246
|
-
`internal/integrations/agents/codexgeneration` record keys RoleTUI and
|
|
247
|
-
RoleAppServer digests, protocol, bound private socket route, state domain, and
|
|
248
|
-
platform/arch, then reduces stored zero-turn resume separately from remote-new
|
|
249
|
-
first-real-input identity. A healthy Current generation, successful
|
|
250
|
-
`thread/read`, or living TUI does not promote either predicate. Doctor and the
|
|
251
|
-
create planner share that record projection, but Phase 1 keeps every projected
|
|
252
|
-
create route on the Phase-0 plain fallback. Generation admission, drain,
|
|
253
|
-
handover, lease lifecycle, and first-turn production binding are unchanged.
|
|
254
|
-
|
|
255
|
-
## Phase 1 lifecycle projection
|
|
256
|
-
|
|
257
|
-
`codexgeneration.ProjectLifecycle` is the only interaction-plus-generation
|
|
258
|
-
mapper. A dash below means that the exact tmux option is absent. Draining,
|
|
259
|
-
handover-pending, recovering, and blocked rows require an operation ref whose
|
|
260
|
-
endpoint exactly matches the durable generation input; without that marker the
|
|
261
|
-
tuple is empty rather than inferred from a process exit or version change.
|
|
262
|
-
|
|
263
|
-
| Effective interaction | Preparing | Current | Draining | Handover pending | Retired | Recovering | Blocked |
|
|
264
|
-
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
265
|
-
| unknown | `-/-/-` | `-/-/-` | `draining/draining/-` | `draining/handover_pending/-` | `-/-/-` | `recovering/recovering/-` | `blocked/blocked/-` |
|
|
266
|
-
| idle | `-/-/-` | `idle/-/-` | `draining/draining/-` | `draining/handover_pending/-` | `-/-/-` | `recovering/recovering/-` | `blocked/blocked/-` |
|
|
267
|
-
| in progress | `-/-/-` | `thinking/in_progress/busy` | `draining/in_progress/busy` | `draining/handover_pending/-` | `-/-/-` | `recovering/recovering/-` | `blocked/blocked/-` |
|
|
268
|
-
| approval required | `-/-/-` | `waiting/approval_required/reply` | `draining/approval_required/reply` | `draining/handover_pending/-` | `-/-/-` | `recovering/recovering/-` | `blocked/blocked/-` |
|
|
269
|
-
| input required | `-/-/-` | `waiting/input_required/reply` | `draining/input_required/reply` | `draining/handover_pending/-` | `-/-/-` | `recovering/recovering/-` | `blocked/blocked/-` |
|
|
270
|
-
| response complete | `-/-/-` | `waiting/response_complete/reply` | `draining/response_complete/reply` | `draining/handover_pending/-` | `-/-/-` | `recovering/recovering/-` | `blocked/blocked/-` |
|
|
271
|
-
|
|
272
|
-
Provider-neutral callers use the same interaction tuples as `Current` without
|
|
273
|
-
claiming generation authority. Explicit generation events additionally require
|
|
274
|
-
the exact durable Agent endpoint and the live activation authority tuple:
|
|
275
|
-
|
|
276
|
-
```text
|
|
277
|
-
stateDomainID + endpointGenerationID + brokerRuntimeID
|
|
278
|
-
+ connectionEpoch + bindingEpoch
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
The event endpoint, durable endpoint, stored durable state/operation, stored
|
|
282
|
-
activation authority, presented authority, and exact target runtime are
|
|
283
|
-
compared before either bounded writer runs. A provider event cannot authorize a
|
|
284
|
-
syntactically valid operation that differs from the stored operation. Local
|
|
285
|
-
epoch numbers are never compared outside the endpoint and broker-runtime
|
|
286
|
-
namespace.
|
|
287
|
-
|
|
288
|
-
| Owner | Fence | Target | Effect |
|
|
289
|
-
| --- | --- | --- | --- |
|
|
290
|
-
| owner | current | target | semantic effect |
|
|
291
|
-
| owner | current | sibling | zero write |
|
|
292
|
-
| owner | stale | target | zero write |
|
|
293
|
-
| owner | stale | sibling | zero write |
|
|
294
|
-
| foreign | current | target | zero write |
|
|
295
|
-
| foreign | current | sibling | zero write |
|
|
296
|
-
| foreign | stale | target | zero write |
|
|
297
|
-
| foreign | stale | sibling | zero write |
|
|
298
|
-
|
|
299
|
-
Every native semantic `Apply` owns the existing exact-Pane authority fence for
|
|
300
|
-
its complete Registry/queue/tmux write set, including the provider-neutral
|
|
301
|
-
lane. `SetAuthority` therefore cannot invalidate between an older Apply's
|
|
302
|
-
Registry and tmux halves. Generation-aware Apply additionally repeats its
|
|
303
|
-
composite comparison inside the Registry transaction and after the Registry
|
|
304
|
-
commit before presentation writes. The production resource
|
|
305
|
-
reconciler consumes the same durable state/operation and exact stored
|
|
306
|
-
activation fence; an unavailable fence is zero-write rather than a reason to
|
|
307
|
-
overwrite the planned tuple with legacy interaction state. Reconciliation
|
|
308
|
-
compares the desired tuple with the exact live options, so its second full pass
|
|
309
|
-
emits no writes.
|
|
310
|
-
|
|
311
|
-
Contract enforcement is split by invariant rather than duplicated by adapter:
|
|
312
|
-
|
|
313
|
-
- C-1 Generation-pinned routing: `TestRuntimeMutationEquivalenceTableIsClosed`,
|
|
314
|
-
`TestRuntimeMutationClassesMatchDecisionKernel`,
|
|
315
|
-
`TestRuntimeMutationCompositeFenceAndSiblingRecorder`, and
|
|
316
|
-
`TestGenerationLifecycleSinkCompositeAuthorityHasZeroCrossWrites`, plus
|
|
317
|
-
`TestGenerationLifecycleProductionReconcileRejectsForeignOrSiblingAuthorityWithZeroWrites`.
|
|
318
|
-
- C-5 Exact lifecycle projection and actionability:
|
|
319
|
-
`TestGenerationLifecycleProjectionClosedTable`,
|
|
320
|
-
`FuzzGenerationLifecycleProjectionMatchesClosedTable`,
|
|
321
|
-
`TestPlannedGenerationProjectionRequiresExactDurableOperationRef`,
|
|
322
|
-
`TestMarkerlessCrashAndVersionDriftRemainOrdinaryFailure`,
|
|
323
|
-
`TestMarkerlessCrashAndVersionDriftRemainOrdinaryFailureThroughProductionReconcile`,
|
|
324
|
-
`TestGenerationLifecycleProjectionReconcileWritesOnceThenZero`, and
|
|
325
|
-
`TestGenerationLifecycleProjectionUsesIsolatedRealTmuxAndExactCleanup`.
|
|
326
|
-
|
|
327
|
-
### Mapping and authority test migration ledger
|
|
328
|
-
|
|
329
|
-
No canonical test symbol was deleted. Phase 0 endpoint-schema/authority tests,
|
|
330
|
-
the pure lifecycle reducer property, and the broker reconnect/C01 canaries each
|
|
331
|
-
retain a unique boundary. The one duplicate assertion family was merged in
|
|
332
|
-
place:
|
|
333
|
-
|
|
334
|
-
| Previous assertion or owner | Phase 1 action | Old mutant still detected | New mutant receipt / canonical owner |
|
|
335
|
-
| --- | --- | --- | --- |
|
|
336
|
-
| `agentTmuxProjection` interaction switch plus hard-coded `waiting`/badge expectations in `TestCodexSemanticDeliveryMatrix` | production switch replaced by the core mapper; policy test now consumes its tuple and owns only Notify/State only/Quiet overlay | quiet or state-only incorrectly retains actionability | wrong interaction or generation tuple fails `TestGenerationLifecycleProjectionClosedTable` and `FuzzGenerationLifecycleProjectionMatchesClosedTable` |
|
|
337
|
-
| `TestResourceReconcileProjectsAllAgentFieldsFromRegistryAuthority` | retained at the unique Registry effective-interaction/topic/offline adapter boundary | stale/offline options survive or a repeat writes | planned-state fixed point is owned by `TestGenerationLifecycleProjectionReconcileWritesOnceThenZero` and the isolated real-tmux test |
|
|
338
|
-
| test-only `planAuthorizedGenerationLifecycleProjection` | deleted; fake and real tests now execute `planResourceAgentProjections` with durable Registry lifecycle plus exact activation authority | a generation-aware sink writes a planned tuple, then ordinary production reconcile erases it through the legacy-only input | `TestGenerationLifecycleProjectionReconcileWritesOnceThenZero` fails both the production-input deletion mutant and a second-pass rewrite; the isolated real-tmux test owns the same boundary on an explicit returned physical socket |
|
|
339
|
-
| `TestCompositeAuthorityRejectsSameNumberCrossGenerationAndLegacyWithZeroWrites` | retained as the Phase 0 pure five-part schema fence | missing endpoint namespace authorizes a legacy ref | owner/foreign × current/stale × target/sibling closure is owned by `TestRuntimeMutationEquivalenceTableIsClosed`, `TestRuntimeMutationClassesMatchDecisionKernel`, and `TestRuntimeMutationCompositeFenceAndSiblingRecorder` |
|
|
340
|
-
| native hook authority and reconnect tests, including the C01 sentinel | retained at the provider-hook/control-plane ordering boundary; no generation expectation copied into them | hook writes during pending/invalidating, an older native Apply restores stale Pane semantics after disconnect, or a retired broker epoch writes | `TestNativeCodexHookAuthorityChangeAfterGuardCommitsZero` and `TestNativeSemanticApplyAndInvalidationShareExactPaneFence` close both hook and native-Apply split-write races; `TestGenerationLifecycleSinkCompositeAuthorityHasZeroCrossWrites` rejects reused epochs across generations and broker restarts before Registry or tmux writes |
|
|
341
|
-
| `FuzzCodexLifecycleReferenceModel` and its readable reducer regressions | retained unchanged as the pure provider-event reducer owner | duplicate, stale, foreign, or reordered provider operations mutate reducer state | generation presentation is a downstream mapper and is independently exhausted by the 6×7 table; no reducer transition was reimplemented |
|
|
342
|
-
|
|
343
|
-
The deletion count is zero test symbols, one obsolete test-only authorization
|
|
344
|
-
helper, and one merged duplicate mapping-expectation family. This is
|
|
345
|
-
intentional: removing any retained row
|
|
346
|
-
would delete a distinct schema, adapter, provider-ordering, or reducer mutant.
|
|
347
|
-
The new runtime inventory keeps `agent.presentation` as the sole typed mutation
|
|
348
|
-
surface and records all five live authority dimensions plus durable owner and
|
|
349
|
-
target runtime. Process pool/host, create routing, consumer notification/sidebar/
|
|
350
|
-
statusbar/reply behavior, badge rendering, reducer transitions, and Phase 2+
|
|
351
|
-
remain outside Phase 1.
|
|
352
|
-
|
|
353
|
-
## Phase 2 endpoint pool and private host
|
|
354
|
-
|
|
355
|
-
`codexbroker.GenerationPool` keys every managed endpoint by the canonical
|
|
356
|
-
`stateDomainID + endpointGenerationID` pair and enforces the Phase 0 two-slot
|
|
357
|
-
bound again at runtime construction. Each generation owns an independent
|
|
358
|
-
Broker, random broker-runtime ID, connection epoch, binding epoch, and
|
|
359
|
-
initialize/snapshot/reconnect/binding ledger. A sibling reconnect cannot touch
|
|
360
|
-
another generation's opener, fence, binding, or provider wire. A broker
|
|
361
|
-
restart closes a generation-local restart fence, restores the exact sorted
|
|
362
|
-
binding ledger, and issues a new broker-runtime ID; authority from the old
|
|
363
|
-
runtime writes zero even when its local epoch numbers repeat. The restart fence
|
|
364
|
-
also refuses a bind or second restart that raced the restore snapshot.
|
|
365
|
-
|
|
366
|
-
Thread routing is the exact endpoint plus exact thread ID. Presenting the same
|
|
367
|
-
thread ID under another state domain or generation returns the typed
|
|
368
|
-
`route-mismatch` refusal before the endpoint wire. `Preparing` readiness is a
|
|
369
|
-
separate fact from admission: Phase 2's `AdmitCreate` always returns
|
|
370
|
-
`admission-closed`, including after a snapshot proves the endpoint ready.
|
|
371
|
-
|
|
372
|
-
`codexgenerationhost` launches only from the Phase 0-qualified immutable
|
|
373
|
-
bundle layout. The required `codex`, `codex-code-mode-host`, bundled `rg`, and
|
|
374
|
-
bundled `bwrap` paths are a closed package-owned set, not caller-overridable
|
|
375
|
-
configuration. The content-addressed lease is re-opened and every manifest,
|
|
376
|
-
role, mode, size, and hash is revalidated before publication and again before
|
|
377
|
-
any lifecycle signal. The versioned socket must be directly below an existing
|
|
378
|
-
owner-private `0700` root and must use the exact endpoint-generation name;
|
|
379
|
-
ambient/default parents, symlinks, permissive roots, and occupied paths are
|
|
380
|
-
never repaired or changed.
|
|
381
|
-
|
|
382
|
-
Lifecycle authority is the full PID plus `Setsid` process-group ID, socket
|
|
383
|
-
device/inode/change-time, executable device/inode/change-time/mode/size/hash,
|
|
384
|
-
bundle ID, endpoint identity, and random endpoint-runtime ID proof. Change time
|
|
385
|
-
keeps replacement fail-closed even when a filesystem immediately reuses a
|
|
386
|
-
socket inode. That private app-server host proof feeds only an exact opener;
|
|
387
|
-
`codexbroker.GenerationPool` separately owns the broker-runtime ID and composite
|
|
388
|
-
connection/binding fence. The existing OS broker Host and hidden CLI remain
|
|
389
|
-
default-only and are not claimed as generation-enabled. Drift in any one axis,
|
|
390
|
-
an exited/reused PID, or bundle-helper drift
|
|
391
|
-
keeps stop/restart/kill effects at zero. Cleanup signals only the revalidated
|
|
392
|
-
private process group and waits for both the repeatable leader-exit channel and
|
|
393
|
-
EOF on an inherited session-lifetime token, including leader-first and
|
|
394
|
-
token-first exit orders, before removing the exact socket or letting the
|
|
395
|
-
installed smoke remove its caller-owned roots.
|
|
396
|
-
Readiness is driven by private-root filesystem events followed by an initialized
|
|
397
|
-
app-server handshake; neither readiness nor cleanup uses a fixed sleep. The
|
|
398
|
-
launch argv always names the leased executable and versioned private socket, so
|
|
399
|
-
mutable current/source removal cannot redirect it. Phase 2 exposes no successful
|
|
400
|
-
lease-release path: process exit and caller claims remain refused, and the later
|
|
401
|
-
journaled handover owner must establish terminal retirement. Phase 2 does not
|
|
402
|
-
delete bundle bytes.
|
|
403
|
-
|
|
404
|
-
The opt-in installed smoke uses only exact private `0.152.0` and `0.152.1`
|
|
405
|
-
leases and a unique empty root:
|
|
406
|
-
|
|
407
|
-
```sh
|
|
408
|
-
smoke_root="$(mktemp -d "${TMPDIR:-/tmp}/projmux-codex-host-XXXXXX")"
|
|
409
|
-
bundle_root="$(mktemp -d /var/tmp/projmux-codex-host-bundles-XXXXXX)"
|
|
410
|
-
env -u TMUX -u TMUX_PANE \
|
|
411
|
-
PROJMUX_CODEX_GENERATION_HOST_SMOKE_ROOT="$smoke_root" \
|
|
412
|
-
PROJMUX_CODEX_GENERATION_BUNDLE_SMOKE_ROOT="$bundle_root" \
|
|
413
|
-
PROJMUX_CODEX_GENERATION_OLD=/absolute/0.152.0/bin/codex \
|
|
414
|
-
PROJMUX_CODEX_GENERATION_NEW=/absolute/0.152.1/bin/codex \
|
|
415
|
-
PROJMUX_CODEX_GENERATION_SOURCE_HOME=/absolute/private/source-home \
|
|
416
|
-
go test ./internal/testutil/codexinstalled \
|
|
417
|
-
-run '^TestInstalledPrivateGenerationHostDualListenerSmoke$' -count=1 -v
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
It requires inherited tmux routing to be absent, observes two initialized
|
|
421
|
-
private listeners and complete held leases, compares ambient/default socket
|
|
422
|
-
and PID-record identity before/after, waits for both exact private process
|
|
423
|
-
groups and their token-bearing descendants to exit, and removes only the exact
|
|
424
|
-
two private sockets, leases, and smoke root.
|
|
425
|
-
|
|
426
|
-
### Phase 2 migration and deletion ledger
|
|
427
|
-
|
|
428
|
-
No canonical test symbol was deleted or renamed. Existing default-endpoint
|
|
429
|
-
broker reconnect, binding, foreign-event, approval, runtime-loss, Phase 0
|
|
430
|
-
bundle/qualification, and Phase 1 composite-fence/projection tests retain their
|
|
431
|
-
unique owners. Phase 2 adds generation-local mutants instead of replacing
|
|
432
|
-
those canaries. Current-pointer/drain, Agent create/resume, foreign adoption,
|
|
433
|
-
Settings/background UX, derived consumers, and unrelated cleanup remain
|
|
434
|
-
excluded for their later phases.
|
|
435
|
-
|
|
436
|
-
## Phase 4 rolling admission and drain
|
|
437
|
-
|
|
438
|
-
`projmux agent app-server upgrade plan|apply --request <absolute-json>` and
|
|
439
|
-
`resume|abort --operation <ref>` are explicit-only operations over one
|
|
440
|
-
owner-private journal. The journal contains exact state-domain/generation and
|
|
441
|
-
operation identities, private routing material, monotonic receipts, and a
|
|
442
|
-
content-free Agent obligation ledger. It never persists prompt, response,
|
|
443
|
-
approval, transcript, credential, or tmux title content. Plan is read-only and
|
|
444
|
-
reports all Registry, provider, tmux, process, and current-pointer mutation
|
|
445
|
-
counts as zero.
|
|
446
|
-
|
|
447
|
-
Plan re-opens both complete leases read-only and binds the request to the Phase
|
|
448
|
-
0 tuple before any candidate start. The current and target bundle IDs, manifest
|
|
449
|
-
versions, server/TUI/helper inventory, and qualification old/new version pair
|
|
450
|
-
must agree exactly. Both sockets must be distinct direct children of distinct
|
|
451
|
-
owner-private roots; those roots must be physically non-overlapping with each
|
|
452
|
-
other, the shared state-domain path, and either immutable lease root. Current
|
|
453
|
-
and target must name the same canonical state-domain path. Any mismatch is a
|
|
454
|
-
mutation-zero refusal.
|
|
455
|
-
|
|
456
|
-
Apply durably records launch intent before starting the exact leased successor.
|
|
457
|
-
The operation journal lock spans the final authorization check and physical
|
|
458
|
-
supervisor start, so an abort fence either wins before launch or observes the
|
|
459
|
-
exact operation-owned durable intent. A coordinator crash after supervisor
|
|
460
|
-
launch but before launch-proof publication resumes the existing candidate; it
|
|
461
|
-
cannot start a duplicate. The supervisor captures the exact socket identity,
|
|
462
|
-
keeps a semantic guard for the child lifetime, and cleans only that identity.
|
|
463
|
-
An unknown or rebound socket is refused and never unlinked. CandidateReady and
|
|
464
|
-
in-flight pre-admission abort both clean the exact new candidate while retaining
|
|
465
|
-
the immutable bundle lease. If a crash lands between intent unlink and guard
|
|
466
|
-
unlink, a retry may remove only the exact regular mode-0600 guard after proving
|
|
467
|
-
that it is unlocked and that no socket exists; a locked guard, rebound path, or
|
|
468
|
-
present socket is left untouched and refused.
|
|
469
|
-
|
|
470
|
-
Before candidate-ready publication and again while the Registry admission
|
|
471
|
-
barrier is held, the complete server/TUI/helper lease is re-opened and verified.
|
|
472
|
-
The recorded TUI executable must equal the lease's single `RoleTUI` artifact;
|
|
473
|
-
an arbitrary absolute path or post-proof TUI drift leaves admission-current
|
|
474
|
-
unchanged. The current pointer and the old/new Draining/Current route transition
|
|
475
|
-
are one atomic journal commit relative to Agent create transactions. A create
|
|
476
|
-
therefore uses either the old Current route before the barrier or the new
|
|
477
|
-
Current route after it; it can never create on Draining. Retired receipt rows do
|
|
478
|
-
not consume a live slot, while Current+Draining closes a third upgrade with all
|
|
479
|
-
mutation surfaces at zero.
|
|
480
|
-
|
|
481
|
-
Existing live Agents remain pinned to the old generation for turn, reply,
|
|
482
|
-
approval, observation, and TUI routing. An Offline resume on Draining or
|
|
483
|
-
HandoverPending never takes the ordinary rebind path: it creates or reuses one
|
|
484
|
-
generation-wide HandoverPending operation ref and then refuses with
|
|
485
|
-
`handover-required`. An unconfigured requester fails closed before provider,
|
|
486
|
-
Registry, or tmux writes. The drain ledger classifies exact Agent UID plus old
|
|
487
|
-
generation as `active`, `approval-pending`, `no-turn`, `unknown`,
|
|
488
|
-
`completed-persisted`, or `closed`; the first four remain durable blockers.
|
|
489
|
-
|
|
490
|
-
Every Phase 4 receipt carries seven explicit zero counters:
|
|
491
|
-
`oldEndpointStop`, `successorResume`, `endpointRefCAS`, `paneRelaunch`,
|
|
492
|
-
`retirement`, `leaseRelease`, and `foreignAdoption`. Those effects, same-thread
|
|
493
|
-
successor resume, endpoint-ref CAS, Pane relaunch, retirement, and foreign
|
|
494
|
-
adoption remain Phase 5 work. The default installed Codex endpoint remains
|
|
495
|
-
unmanaged attach-only; an absent rolling journal preserves that read-only route
|
|
496
|
-
and no Phase 4 lifecycle path can stop, restart, kill, or adopt it.
|
|
497
|
-
|
|
498
|
-
The installed observation is opt-in and must use one unique empty temp root,
|
|
499
|
-
which it partitions into separate state-domain, generation, bundle, and XDG
|
|
500
|
-
roots, plus the existing old/new installed bundle inputs:
|
|
501
|
-
|
|
502
|
-
```sh
|
|
503
|
-
PROJMUX_CODEX_PHASE4_SMOKE_ROOT=/tmp/<unique-empty-root> \
|
|
504
|
-
PROJMUX_CODEX_PHASE4_PROJMUX=/absolute/installed/projmux \
|
|
505
|
-
PROJMUX_CODEX_GENERATION_OLD=/absolute/0.152.0/bin/codex \
|
|
506
|
-
PROJMUX_CODEX_GENERATION_NEW=/absolute/0.152.1/bin/codex \
|
|
507
|
-
PROJMUX_CODEX_GENERATION_SOURCE_HOME=/absolute/private/source-home \
|
|
508
|
-
go test ./internal/testutil/codexinstalled \
|
|
509
|
-
-run '^TestInstalledPrivateRollingAdmissionReceipt$' -count=1 -v
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
It observes the ambient socket/PID record read-only, starts only fixture-owned
|
|
513
|
-
private process groups, checks the rendered seven-zero receipt and durable
|
|
514
|
-
candidate/admission/drain counters, re-observes the old Draining proof and
|
|
515
|
-
reads its exact content-free thread, then exercises catalog listing without
|
|
516
|
-
requiring that no-turn thread to appear and creates/reads one payload-free thread
|
|
517
|
-
through the journal-selected new Current. It finally uses the exact operation
|
|
518
|
-
proof for semantic teardown. It never signals or adopts the default endpoint.
|
|
519
|
-
|
|
520
|
-
### Phase 4 migration and deletion ledger
|
|
521
|
-
|
|
522
|
-
No canonical invariant test symbol is deleted or renamed. The canonical graph
|
|
523
|
-
baseline expands only for the four explicit upgrade leaves, and generated CLI
|
|
524
|
-
reference coverage remains owned by its existing exact-tree tests. Phase 0
|
|
525
|
-
qualification, Phase 1 lifecycle fencing, Phase 2 host/pool, and Phase 3
|
|
526
|
-
generation-pinned routing tests retain their unique boundaries. Phase 4 adds
|
|
527
|
-
model/fuzz, an exhaustive ten-coordinator-failpoint plus two-journal-hook restart
|
|
528
|
-
table whose four durable effects each converge exactly once, launch-intent
|
|
529
|
-
recovery, admission/abort race, Phase-0 tuple/root topology refusal, orphan-guard
|
|
530
|
-
recovery, TUI drift, two-slot, Draining/HandoverPending resume, direct old-route
|
|
531
|
-
continuity, and content-free receipt mutants rather than merging or deleting
|
|
532
|
-
those existing suites.
|
|
533
|
-
|
|
534
|
-
## Phase 5 journaled generation handover
|
|
535
|
-
|
|
536
|
-
`projmux agent app-server handover plan|apply --request <absolute-json>` and
|
|
537
|
-
`resume|abort --operation <ref>` operate on a Phase-5 record linked to the
|
|
538
|
-
unchanged Phase-4 rolling receipt under the same owner-private journal lock.
|
|
539
|
-
Plan is repeatable and read-only. It pins exact Agent UID, Pane UID, live Pane
|
|
540
|
-
runtime ID, Pane activation generation, thread ID, old/successor endpoint tuple,
|
|
541
|
-
owner class, and explicit no-turn decisions. Active, pending-approval, unknown,
|
|
542
|
-
unresolved no-turn, incomplete owner proof, tuple drift, or a successor thread
|
|
543
|
-
already loaded before stop leaves old stop, Pane exit, provider resume, and
|
|
544
|
-
endpoint-ref write at zero.
|
|
545
|
-
|
|
546
|
-
Apply prewrites an intent for every effect. For Projmux-private ownership it
|
|
547
|
-
revalidates the complete old launch authority and every Registry/live-Pane
|
|
548
|
-
tuple immediately before the exact owner stop. Official-managed, unmanaged,
|
|
549
|
-
and unknown ownership run the reversible fences and successor-absence checks,
|
|
550
|
-
then stop at `AwaitingOwnerStop` with lifecycle argv zero until an exact
|
|
551
|
-
user-owned stop receipt is supplied. Explicit no-turn close/replacement is
|
|
552
|
-
required; no automatic selection exists, and an applied close is a forward-only
|
|
553
|
-
boundary.
|
|
554
|
-
|
|
555
|
-
The forward-only order is old stop, one operation-qualified successor resume
|
|
556
|
-
per target, completed persisted snapshot for every target, endpoint-ref CAS,
|
|
557
|
-
same-Pane relaunch, terminal retirement, then lease release. The durable resume
|
|
558
|
-
receipt is content-free and survives a successor restart, so coordinator retry
|
|
559
|
-
does not send a second resume wire; a persisted completed not-loaded snapshot
|
|
560
|
-
can still satisfy the semantic barrier before the CAS. CAS keeps the same Agent,
|
|
561
|
-
Pane, and thread identities while issuing a new Pane activation generation.
|
|
562
|
-
Retirement must observe no remaining old endpoint ref, and the old immutable
|
|
563
|
-
lease remains held until the terminal receipt.
|
|
564
|
-
|
|
565
|
-
If the exact old private process disappeared, a read-only lease/intent probe
|
|
566
|
-
prefers a journaled same-generation durable-host restart. If that exact bundle
|
|
567
|
-
is unavailable but the qualified pair and exact stop authority remain, the
|
|
568
|
-
handover takes the qualified fallback and the normal stop Ensure proves owned
|
|
569
|
-
absence before successor work. No fixed sleep is used; host guard release,
|
|
570
|
-
app-server lifecycle snapshots, Registry CAS, tmux identity mirrors, and Pane
|
|
571
|
-
relaunch markers are the bounded semantic barriers.
|
|
572
|
-
|
|
573
|
-
### Retirement vacancy
|
|
574
|
-
|
|
575
|
-
The version-pair receipt proves that a thread survives a cross-version resume.
|
|
576
|
-
A draining generation that holds no thread has no subject for that proof, so
|
|
577
|
-
requiring the receipt there is a gate with nothing behind it. That case is real
|
|
578
|
-
and ordinary: `ActivateManagedCurrent` starts a rolling activation on its own
|
|
579
|
-
whenever the running Codex version differs from the managed one, and deleting
|
|
580
|
-
the Agents pinned to the old generation afterwards leaves a generation nobody
|
|
581
|
-
can retire -- the only production caller of the rolling handover request is
|
|
582
|
-
`agent resume` on a live pinned Agent.
|
|
583
|
-
|
|
584
|
-
`codexgeneration.EvaluateRetirementVacancy` names that one case and nothing
|
|
585
|
-
wider. Its evidence is a content-free census of the exact retiring endpoint,
|
|
586
|
-
and every field must be zero *and* both censuses must have actually run:
|
|
587
|
-
|
|
588
|
-
- freshly projected non-closed obligations, taken from a Registry snapshot read
|
|
589
|
-
for this decision. The durable journal ledger is never the input: an
|
|
590
|
-
obligation whose Agent has since been deleted is frozen at its last
|
|
591
|
-
projection, carries no ThreadID, and can neither be recomputed nor traced;
|
|
592
|
-
- Agents whose session ref still names the endpoint, including ones carrying no
|
|
593
|
-
ThreadID. An obligation requires a ThreadID, so this covers that hole on the
|
|
594
|
-
Registry side;
|
|
595
|
-
- Pane activations whose native authority names the endpoint. A Pane outlives
|
|
596
|
-
its Agent record, so the obligation census cannot see this axis at all;
|
|
597
|
-
- threads the shared state domain actually holds that a Registry record still
|
|
598
|
-
binds to the endpoint. The census reads rollout *file names* under
|
|
599
|
-
`<state domain>/sessions`; no rollout body is opened, and a state domain that
|
|
600
|
-
cannot be read yields no verdict of vacancy.
|
|
601
|
-
|
|
602
|
-
A vacant plan reports `request-handover`: `Apply` fires the exact rolling
|
|
603
|
-
handover request itself, through a declared requester seam, then re-plans and
|
|
604
|
-
authorizes the destructive operation from that second census. Everything else
|
|
605
|
-
is unchanged. The `multiple-draining` cap and the two-slot arithmetic are
|
|
606
|
-
untouched -- the freed slot comes from a retirement receipt, not a changed
|
|
607
|
-
topology rule -- retirement still only rewrites route state and obligation
|
|
608
|
-
rows, and no thread file is ever removed. One live obligation, one endpoint-
|
|
609
|
-
bound Registry record, or one unreadable state domain keeps both original
|
|
610
|
-
refusals exactly as they were.
|
|
611
|
-
|
|
612
|
-
### Phase 5 enforcement and deletion ledger
|
|
613
|
-
|
|
614
|
-
No canonical test symbol is deleted or renamed, and the Phase-4 rolling
|
|
615
|
-
operation plus its seven-zero effect invariant remain unchanged. The command
|
|
616
|
-
graph adds only the four explicit handover leaves. Phase 5 adds a separate
|
|
617
|
-
model/fuzz state machine, coordinator and journal failpoint restart coverage,
|
|
618
|
-
exact ownership/blocker negatives, Registry CAS and durable resume-receipt
|
|
619
|
-
tests, production effect tuple-drift tests, and private installed handover/no-turn
|
|
620
|
-
observations. Prompt/turn/approval resend, provider-content replay or inspection,
|
|
621
|
-
double same-thread ownership, duplicate Agent/Pane identity, foreign lifecycle
|
|
622
|
-
mutation, automatic no-turn migration, live-old same-thread resume, and Phase-6
|
|
623
|
-
consumer/Doctor changes remain enforced at zero.
|