worktree-controller 1.30.4__py3-none-any.whl
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.
- worktree_controller/__init__.py +3 -0
- worktree_controller/activation_layout.py +483 -0
- worktree_controller/branch_retirement.py +1865 -0
- worktree_controller/canonical_refresh.py +150 -0
- worktree_controller/canonical_transition.py +324 -0
- worktree_controller/cleanup_attempts.py +442 -0
- worktree_controller/cleanup_evidence.py +75 -0
- worktree_controller/cleanup_recovery.py +912 -0
- worktree_controller/cli.py +1907 -0
- worktree_controller/controller.py +1974 -0
- worktree_controller/convergence.py +893 -0
- worktree_controller/delivered_closeout.py +928 -0
- worktree_controller/disposable.py +677 -0
- worktree_controller/environment.py +1038 -0
- worktree_controller/frozen_recovery.py +291 -0
- worktree_controller/git_backend.py +682 -0
- worktree_controller/governance.py +365 -0
- worktree_controller/hardlinks.py +294 -0
- worktree_controller/integration_cleanup.py +92 -0
- worktree_controller/integration_conflict.py +117 -0
- worktree_controller/integration_retry.py +526 -0
- worktree_controller/keyed_document_scope.py +162 -0
- worktree_controller/lifecycle.py +8992 -0
- worktree_controller/lifecycle_history.py +358 -0
- worktree_controller/lifecycle_observation.py +234 -0
- worktree_controller/metadata_codec.py +1694 -0
- worktree_controller/metadata_relocation.py +131 -0
- worktree_controller/models.py +546 -0
- worktree_controller/mount_diagnostics.py +324 -0
- worktree_controller/multiple_cleanup_recovery.py +730 -0
- worktree_controller/namespace_migration.py +1412 -0
- worktree_controller/native_session_recovery.py +171 -0
- worktree_controller/owner_actions.py +34 -0
- worktree_controller/policy.py +333 -0
- worktree_controller/preservation.py +983 -0
- worktree_controller/preserved_cleanup.py +849 -0
- worktree_controller/publication_execution.py +361 -0
- worktree_controller/reconciler.py +430 -0
- worktree_controller/recovery_guidance.py +205 -0
- worktree_controller/registration_backfill.py +166 -0
- worktree_controller/registration_prune.py +391 -0
- worktree_controller/rendering.py +179 -0
- worktree_controller/resource_admission.py +1736 -0
- worktree_controller/retirement_adapters.py +130 -0
- worktree_controller/retirement_execution.py +234 -0
- worktree_controller/semantics.py +459 -0
- worktree_controller/session.py +505 -0
- worktree_controller/session_admission.py +265 -0
- worktree_controller/source_cleanup.py +515 -0
- worktree_controller/source_preservation.py +692 -0
- worktree_controller/source_recovery.py +592 -0
- worktree_controller/store.py +1228 -0
- worktree_controller/tool_cli_contract.json +1791 -0
- worktree_controller/transaction_scope.py +876 -0
- worktree_controller/writer_lease.py +662 -0
- worktree_controller/writer_session.py +887 -0
- worktree_controller-1.30.4.dist-info/METADATA +3149 -0
- worktree_controller-1.30.4.dist-info/RECORD +61 -0
- worktree_controller-1.30.4.dist-info/WHEEL +4 -0
- worktree_controller-1.30.4.dist-info/entry_points.txt +5 -0
- worktree_controller-1.30.4.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,3149 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: worktree-controller
|
|
3
|
+
Version: 1.30.4
|
|
4
|
+
Summary: Govern Git worktree budgets, authority, ownership, and lifecycle
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Requires-Dist: websockets>=15,<16
|
|
10
|
+
Requires-Python: >=3.12, <3.13
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# Worktree Controller
|
|
14
|
+
|
|
15
|
+
`worktree-controller` is the independent control plane for Git worktree budget, allocation authority,
|
|
16
|
+
ownership, integration-candidate evidence, serialized main-publication authority, recovery, and
|
|
17
|
+
guarded checkout release. Git remains authoritative for registered worktrees, paths, branches,
|
|
18
|
+
HEADs, refs, index state, and cleanliness.
|
|
19
|
+
|
|
20
|
+
The controller stores only non-Git facts under the dynamically resolved Git common directory:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
<git-common-dir>/worktree-controller/v1/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Repository policy is read from tracked `.agents/worktree-policy.toml`. The JSON interface is schema
|
|
27
|
+
version 1 and ordinary commands expose `tool_version`, `schema_version`, `command`, `repository`, `decision`,
|
|
28
|
+
`worktrees`, `warnings`, and `errors`.
|
|
29
|
+
|
|
30
|
+
Licensed under the [Apache License 2.0](LICENSE).
|
|
31
|
+
|
|
32
|
+
Start with the [operations guide](docs/operations.md) for development, handoff, resource waits,
|
|
33
|
+
release and deployment, and the [architecture guide](docs/architecture.md) for module ownership
|
|
34
|
+
and maintenance boundaries. Exact interfaces remain in the [CLI contract](tool_cli_contract.json).
|
|
35
|
+
|
|
36
|
+
Current release: **1.30.4**, CLI contract **60**, storage layout **5**. Software, CLI,
|
|
37
|
+
capability and storage versions are independent compatibility requirements.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
Worktree Controller runs on **Linux** with **Python 3.12** (`>=3.12,<3.13`), **Git**, and a
|
|
42
|
+
readable `/proc` (procfs). Writer identity, runtime evidence and repository locking use Linux
|
|
43
|
+
process interfaces and POSIX `flock`; other operating systems and Python versions are not
|
|
44
|
+
supported by this release.
|
|
45
|
+
|
|
46
|
+
Install the published distribution with pip:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
python3.12 -m pip install worktree-controller
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or install from a source checkout with [uv](https://docs.astral.sh/uv/):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
git clone <repository-url> wc && cd wc
|
|
56
|
+
uv sync --locked --python 3.12
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Confirm the install. The CLI reports its software version, and `capabilities` reports the
|
|
60
|
+
independent CLI contract and storage layout versions:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
worktree-controller --version
|
|
64
|
+
worktree-controller capabilities --json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Three console scripts are installed: `worktree-controller` (the control plane),
|
|
68
|
+
`worktree-session` (the managed writer-session handshake) and `worktree-session-end`
|
|
69
|
+
(session closeout).
|
|
70
|
+
|
|
71
|
+
## Quickstart
|
|
72
|
+
|
|
73
|
+
This walkthrough uses a throwaway repository and a fake identity; it never touches an existing
|
|
74
|
+
project. The Controller only adds authority and lifecycle on top of Git facts — Git stays
|
|
75
|
+
authoritative for worktrees, paths, branches, HEADs, refs and cleanliness. Replace each
|
|
76
|
+
`<...>` placeholder with the value printed by the previous command.
|
|
77
|
+
|
|
78
|
+
1. Create a synthetic repository and enable policy. The Controller reads repository policy from a
|
|
79
|
+
tracked `.agents/worktree-policy.toml`; copy the example shipped in this repository:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
mkdir -p /tmp/wc-demo/.agents && cd /tmp/wc-demo
|
|
83
|
+
git init --initial-branch=main
|
|
84
|
+
git config user.email demo@example.invalid
|
|
85
|
+
git config user.name "WC Demo"
|
|
86
|
+
cp /path/to/worktree-controller/.agents/worktree-policy.toml .agents/
|
|
87
|
+
printf '# demo\n' > README.md
|
|
88
|
+
git add -A && git commit -m "initial"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
2. Observe the repository. `status` reports the canonical checkout, the resource budget and any
|
|
92
|
+
allocations; `doctor` checks metadata integrity:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
worktree-controller status --repo /tmp/wc-demo --json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
3. Preview admission with `plan` (read-only), then `acquire` a governed feature checkout on a new
|
|
99
|
+
branch from a base ref, declaring the repository-relative paths it may touch (`--scope`):
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
worktree-controller plan --repo /tmp/wc-demo --task-key demo-task \
|
|
103
|
+
--authority-key demo-authority --module demo --role feature --json
|
|
104
|
+
worktree-controller acquire --repo /tmp/wc-demo --task-key demo-task \
|
|
105
|
+
--authority-key demo-authority --module demo --role feature --owner demo-owner \
|
|
106
|
+
--branch feat/demo --base-ref main --scope README.md --json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
4. Bind a writer lease to the real, live process that will execute the writes, then require the
|
|
110
|
+
write guard to allow the exact operation before editing:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
worktree-controller writer-lease-acquire --repo /tmp/wc-demo --path <checkout-path> \
|
|
114
|
+
--owner demo-owner --client-id demo-client --holder-pid <real-pid> --json
|
|
115
|
+
worktree-controller write-guard --repo /tmp/wc-demo --path <checkout-path> \
|
|
116
|
+
--task-key demo-task --authority-key demo-authority --owner demo-owner \
|
|
117
|
+
--holder <real-pid> --operation feature-write --json
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
5. Do the work and commit it. A real delivery uses the companion **Git Finalizer** to produce the
|
|
121
|
+
commit/publication evidence, then returns the checkout to the repository control plane with
|
|
122
|
+
`handoff`, recording the exact commit and scope:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
worktree-controller handoff --repo /tmp/wc-demo --path <checkout-path> \
|
|
126
|
+
--expected-record-version <n> --kind integration-ready --commit <commit-oid> \
|
|
127
|
+
--finalizer-record-id <finalizer-record-id> --scope-changed --json
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
6. If a checkout is blocked, do not guess or force it. `recovery-guide` is a read-only explanation
|
|
131
|
+
of the observed state, its blockers and the next formal step:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
worktree-controller recovery-guide --repo /tmp/wc-demo --path <checkout-path> --json
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
7. At session end, reconcile every checkout the session leased. Use `--check` first (read-only),
|
|
138
|
+
then end with `--require-complete`; read each per-allocation decision and
|
|
139
|
+
`summary.terminal_complete`, not the exit code alone:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
worktree-session-end --path /tmp/wc-demo --client-id demo-client --holder <real-pid> \
|
|
143
|
+
--check --expected-allocation-id <allocation-id>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Integration into main, resource waits, aborted-integration recovery, branch retirement and
|
|
147
|
+
guarded release are covered in the [operations guide](docs/operations.md). Module ownership and
|
|
148
|
+
maintenance boundaries are in the [architecture guide](docs/architecture.md); exact command,
|
|
149
|
+
capability and result schemas live in the [CLI contract](tool_cli_contract.json).
|
|
150
|
+
|
|
151
|
+
## Optional integrations and dependencies
|
|
152
|
+
|
|
153
|
+
The core control plane has a single runtime dependency (`websockets>=15,<16`) and works standalone
|
|
154
|
+
against any local Git repository on the supported platform. Everything below is optional and is
|
|
155
|
+
exercised only when the corresponding context or companion tool is present:
|
|
156
|
+
|
|
157
|
+
- **Companion delivery tools.** WC composes with, but does not require or embed, the separate
|
|
158
|
+
**Context Loader** (context reading), **Snapshot Runner** (read-only review evidence) and
|
|
159
|
+
**Git Finalizer** (commit and publication). Each owns its own authority; WC never merges,
|
|
160
|
+
publishes or commits on their behalf. The handoff and integration flows in
|
|
161
|
+
[docs/operations.md](docs/operations.md) assume Git Finalizer is installed for delivery evidence.
|
|
162
|
+
- **Client-specific recovery.** The `archived_codex_binding_recovery_version` capability
|
|
163
|
+
(`--recover-archived-codex-session`) recovers a released writer lease whose original session
|
|
164
|
+
belonged to the Codex CLI client, by verifying that client's own archived-session and
|
|
165
|
+
app-server-control state under the user's home directory. It is inert for other clients.
|
|
166
|
+
- **Keyed document scope.** The `integration_keyed_document_scope_version` capability proves
|
|
167
|
+
independent progress-document records from frozen Git objects for one specific known handbook
|
|
168
|
+
profile (`repo_id` `quant-platform`, schema 2). It activates only for that exact document profile
|
|
169
|
+
and otherwise leaves the ordinary file-scope fence unchanged.
|
|
170
|
+
- **Legacy namespace migration.** `namespace_migration` performs the one-time move of a repository's
|
|
171
|
+
historical `codex-worktree/v1` metadata namespace to the current `worktree-controller/v1` layout.
|
|
172
|
+
Repositories created under the current namespace never invoke it.
|
|
173
|
+
- **Maintainer deployment.** `scripts/controller-activate` and `scripts/controller-runtime` are the
|
|
174
|
+
source-owned runtime activation and start entry points used by a maintainer's host; they are not
|
|
175
|
+
required to install or use the published package.
|
|
176
|
+
|
|
177
|
+
From 1.30.1, a completed cleanup at a retained source may survive normal park/resume
|
|
178
|
+
cycles followed by a formal handoff of the same HEAD and owner. Verification requires
|
|
179
|
+
the complete exact-CAS history, original cleanup identity, action proofs and preserved
|
|
180
|
+
generations. It rewrites no receipt and accepts no arbitrary CAS or HEAD drift; normal
|
|
181
|
+
runtime, lease and release guards still apply.
|
|
182
|
+
|
|
183
|
+
From 1.30.0, read-only `recovery-guide` and explicit expected-allocation coverage are available for
|
|
184
|
+
strict session closeout. Both are additive capabilities on CLI 60/storage 5; inspect installed
|
|
185
|
+
capabilities before use. See [recovery and closeout](docs/operations.md#recovery-guidance-and-session-closeout).
|
|
186
|
+
|
|
187
|
+
From 1.30.2, `recovery-guide --intent-id` also explains an already published and released
|
|
188
|
+
integration through complete matching immutable evidence. It points to lifecycle history instead
|
|
189
|
+
of retrying a completed integration. Incomplete or drifted evidence remains blocked.
|
|
190
|
+
|
|
191
|
+
From 1.29.2, scope attribution accepts a retained formal publication reflog suffix
|
|
192
|
+
only when it includes the exact frozen task base and every retained transition is
|
|
193
|
+
continuous and forward, ending at the current formal ref. Git may expire older
|
|
194
|
+
entries without making already-published background changes part of the task's
|
|
195
|
+
scope. A suffix missing the task base, a broken or rewritten chain, unavailable
|
|
196
|
+
history, replacement graphs, symbolic refs and ambiguous merge bases still keep
|
|
197
|
+
the frozen comparison bound. No reflog, scope or allocation metadata is rewritten.
|
|
198
|
+
|
|
199
|
+
Capability `aborted_integration_preserved_cleanup_version` 1 extends the existing exact
|
|
200
|
+
preservation and typed cleanup interfaces to explicitly aborted unpublished integrations.
|
|
201
|
+
Every removed tree needs immutable preservation coverage; quarantine, request identity,
|
|
202
|
+
leases, runtime, ownership and release guards remain mandatory. Only formal checkout release
|
|
203
|
+
settles its admission reservation. See the [operational flow](docs/operations.md#aborted-integration-with-preserved-build-output).
|
|
204
|
+
|
|
205
|
+
The supported runtime is Python 3.12 on Linux with Git and readable procfs. Writer identity,
|
|
206
|
+
runtime evidence and repository locking depend on Linux process interfaces and POSIX flock;
|
|
207
|
+
other operating systems are not supported by this release.
|
|
208
|
+
|
|
209
|
+
From 1.28.2, `reconcile release-plans` preserves every plan referenced by a durable release
|
|
210
|
+
receipt. Closed lifecycle queries and branch retirement continue to use the original plan bytes;
|
|
211
|
+
only unreferenced leftover plans for already released allocations may be retired. Missing
|
|
212
|
+
historical plans still require their original evidence and are never inferred from Git ancestry.
|
|
213
|
+
|
|
214
|
+
From 1.28.1, admission reconciliation observes completed checkout requests in one batch per
|
|
215
|
+
repository under a shared repository lock. Host-ledger accounting and execution observations
|
|
216
|
+
also use shared locks: they do not change repository metadata. This avoids unnecessary writer
|
|
217
|
+
lock contention with status/history readers and repeated full Git/metadata scans. Snapshots
|
|
218
|
+
never survive their held lock or authorize a later queued acquisition. Acquire recovery that
|
|
219
|
+
changes repository evidence still takes an exclusive lock; runtime removal and new acquisitions
|
|
220
|
+
retain their original guarded mutation paths. Missing locks, registration or owner drift,
|
|
221
|
+
uncertain transactions and incomplete release evidence still block reconciliation.
|
|
222
|
+
|
|
223
|
+
From 1.28.0, typed `resource-clean` verifies the hardlink topology before it marks an action
|
|
224
|
+
`STARTED`, so a topology refusal leaves no action evidence and `resource-recover --stage
|
|
225
|
+
reconcile-typed` closes it as a no-op. A journal left by an earlier runtime, whose first action is
|
|
226
|
+
`FAILED_OR_PARTIAL` with `HARDLINK_TOPOLOGY_BLOCKED` and whose only evidence is that action's
|
|
227
|
+
`0000-before.json`, is accepted too: unchanged whole-tree resource evidence and in-scope topology
|
|
228
|
+
prove no deletion began. ctime/nlink drift is tolerated only on inodes that have links outside the
|
|
229
|
+
scope before and after (another environment sharing the files). Capability
|
|
230
|
+
`typed_cleanup_verify_boundary_version` 1.
|
|
231
|
+
|
|
232
|
+
From 1.27.0, `reconcile binding-registrations --dry-run` lists the live linked bindings written
|
|
233
|
+
before 1.26.0 and the evidence for each; applying the same command with `--expected-plan-id`
|
|
234
|
+
writes their registration marker and document. A binding qualifies only when its record exists, its
|
|
235
|
+
Git `gitdir` pointer names the observed checkout, no marker is already present, Git created the
|
|
236
|
+
registration no later than 900 seconds after the allocation's `created_at`, and any recorded base
|
|
237
|
+
commit is in the checkout's history. Refused bindings are listed with typed blockers and keep their
|
|
238
|
+
legacy meaning. Records, bindings and history are unchanged. Capability
|
|
239
|
+
`binding_registration_backfill_version` 1.
|
|
240
|
+
|
|
241
|
+
From 1.26.0, a linked binding belongs to the exact Git registration it was written for.
|
|
242
|
+
`acquire` and `adopt` write a `worktree-registration/v1` token into the worktree's Git
|
|
243
|
+
administrative directory (`worktree-controller-registration`) and record it under
|
|
244
|
+
`binding-registrations/`. If the checkout is removed outside the Controller and Git later
|
|
245
|
+
registers another worktree under the same administrative name, the token is missing: the binding
|
|
246
|
+
is withdrawn from every snapshot, `status` warns `BINDING_IDENTITY_DRIFT`, and the new checkout is
|
|
247
|
+
unmanaged, so write-guard, writer leases and release refuse it instead of granting the old task's
|
|
248
|
+
authority. A binding whose administrative directory is gone keeps the existing `ORPHAN_BINDING`
|
|
249
|
+
meaning, and bindings written before 1.26.0 keep their previous meaning until
|
|
250
|
+
`reconcile binding-registrations` ties them to their registration (1.27.0, below). Git commands run by the
|
|
251
|
+
Controller ignore inherited repository selection (`GIT_DIR`, `GIT_WORK_TREE`, `GIT_INDEX_FILE`,
|
|
252
|
+
`GIT_COMMON_DIR`, `GIT_OBJECT_DIRECTORY` and related variables), so a call from a Git hook observes
|
|
253
|
+
the repository it names. An unreadable metadata directory now fails closed
|
|
254
|
+
(`FILESYSTEM_PERMISSION`/`INVALID_METADATA`) instead of reading as empty. The release runtime scan
|
|
255
|
+
also refuses a checkout whose path or inode changes during the scan; same-UID processes whose procfs
|
|
256
|
+
entries deny access remain host visibility gaps, as in typed cleanup. Storage layout 5 declares the
|
|
257
|
+
existing `cleanup-actions`, `cleanup-recoveries`, `native-session-recoveries` and
|
|
258
|
+
`session-closeouts` directories and adds `binding-registrations`; closeout observations whose holder
|
|
259
|
+
is provably gone are pruned. CLI contract 57.
|
|
260
|
+
|
|
261
|
+
From 1.22.4, direct `writer-session check` and hosted `host-check` do not retain decoded
|
|
262
|
+
metadata or historical mapping caches during their single-snapshot decisions. Each new call
|
|
263
|
+
still reads and validates every metadata file under the same locks, with unchanged byte decoding,
|
|
264
|
+
JSON error details and authority checks. Commands that reload snapshots retain command-local reuse.
|
|
265
|
+
|
|
266
|
+
From 1.22.3, direct `writer-session check` holds one shared repository lock through its guard
|
|
267
|
+
and handoff continuation. It loads one snapshot instead of reloading after a refusal, so concurrent
|
|
268
|
+
Controller lease/lifecycle changes wait for the decision. Each new call still reads and validates
|
|
269
|
+
current metadata; grants, refusals, CLI contract 54 and storage layout 4 are unchanged.
|
|
270
|
+
|
|
271
|
+
From 1.22.2, runtime scans compare path boundaries without repeatedly splitting ordinary procfs
|
|
272
|
+
paths for each checkout root. Lexical path rules, deleted references, scan scope and fresh release
|
|
273
|
+
checks are unchanged. Five alternating pairs of three host-namespace acceptance cases had median
|
|
274
|
+
times of 18.90 / 14.04 seconds before / after (26% lower); this local shared-host sample does not
|
|
275
|
+
establish a full-suite or portable latency improvement. CLI contract 54 and storage layout 4.
|
|
276
|
+
|
|
277
|
+
From 1.22.1, an entitlement check decodes repository metadata once instead of three times: the
|
|
278
|
+
caller's locked snapshot is passed to the write guard, and governance discovery leaves validation to
|
|
279
|
+
that load. Decisions and fail-closed behavior are unchanged; the median hook check dropped from
|
|
280
|
+
about 0.47 s to 0.31 s on a repository with about 20,000 metadata records. CLI contract 54.
|
|
281
|
+
|
|
282
|
+
From 1.22.0, `writer-session check` accepts the same `--access`/`--target` continuation for a
|
|
283
|
+
direct (non-hosted) holder, and a frozen holder may also write into another checkout whose writer
|
|
284
|
+
lease the same live process holds, subject to that checkout's own feature-write guard. Entitlement
|
|
285
|
+
checks skip the full status scan (about a 0.5 s saving per hook call). `handoff --scope-changed`
|
|
286
|
+
records the exact paths changed since the publication base; an integration-ready handoff of
|
|
287
|
+
unscoped changed work now returns warning `MISSING_HANDOFF_SCOPE` instead of failing later at
|
|
288
|
+
`integration-enqueue`. A consumer may report `required_controller_capabilities`; the rollout gate
|
|
289
|
+
refuses activation or rollback to a runtime that lacks any declared minimum. Capability
|
|
290
|
+
`host_handoff_continuation_version` 2; CLI contract 54, storage layout 4.
|
|
291
|
+
|
|
292
|
+
From 1.21.0, a host that names an access class keeps its own frozen handoff's Controller flow.
|
|
293
|
+
`writer-session host-check <path> --access read|exec|write [--target <absolute-path>]` returns
|
|
294
|
+
`CONTINUATION_GRANTED` (`HOST_HANDOFF_CONTINUATION`) only to the exact live holder of the current
|
|
295
|
+
lease on its own `HANDOFF_READY` or `PRESERVED` feature allocation. Reads and commands continue;
|
|
296
|
+
writes continue only outside Controller governance or into an integration candidate built from
|
|
297
|
+
that allocation. The frozen checkout, other checkouts and checks without `--access` keep their
|
|
298
|
+
refusal, and HEAD drift, a missing lease or a foreign holder refuse every class. Capability
|
|
299
|
+
`host_handoff_continuation_version` 1; CLI contract 53, storage layout 4.
|
|
300
|
+
|
|
301
|
+
From 1.20.3, session-end reconciliation pins the selected allocation and original record
|
|
302
|
+
version. Terminal parking checks for a writer lease under the same repository lock as
|
|
303
|
+
the state change, including a new lease whose runtime binding has not yet been written.
|
|
304
|
+
A delayed hook reports its per-allocation refusal and preserves the new writer; path-only
|
|
305
|
+
hooks also refuse a current writer. Explicit owner `park` keeps its existing semantics.
|
|
306
|
+
|
|
307
|
+
From 1.20.2, `release` and `lease-complete` resolve a stable canonical repository context
|
|
308
|
+
before checkout removal. Passing `--repo` from the released checkout or its subdirectory
|
|
309
|
+
completes receipt writes, publication acknowledgement and source release in the same call.
|
|
310
|
+
Release guards, record CAS, retained refs and exact recovery identity remain unchanged.
|
|
311
|
+
|
|
312
|
+
From 1.20.1, command-local history reuse retains a private copy of validated metadata mappings.
|
|
313
|
+
Each snapshot still scans current directory entries and checks every file's identity; added,
|
|
314
|
+
removed or changed files use the existing decode and filename validation path. Snapshot mappings
|
|
315
|
+
remain independent, and no history cache survives the command. Locking, authority rechecks,
|
|
316
|
+
durable writes, CLI contract and storage layout are unchanged.
|
|
317
|
+
|
|
318
|
+
From 1.19.23, nonempty metadata string validation reuses its first whitespace-normalization
|
|
319
|
+
result. Accepted text, empty/NUL rejection and error priority stay unchanged. Snapshot freshness,
|
|
320
|
+
schema checks, command-local decode reuse and durable write boundaries retain their existing gates.
|
|
321
|
+
|
|
322
|
+
From 1.20.0, `integration_keyed_document_scope_version` 1 proves independent QP Handbook
|
|
323
|
+
record changes from frozen Git objects. Distinct records may prepare concurrently; source scope,
|
|
324
|
+
ownership, validation and serialized main publication retain their existing authority.
|
|
325
|
+
|
|
326
|
+
From 1.19.20, uncached historical metadata reads open each fresh directory entry directly,
|
|
327
|
+
avoiding a per-file `Path` construction. UTF-8 text/newline handling, JSON object checks,
|
|
328
|
+
errors and full metadata validation are unchanged. Each snapshot scans current directory
|
|
329
|
+
entries; command-local decode reuse keeps its existing file-identity checks and lifetime.
|
|
330
|
+
|
|
331
|
+
From 1.19.18, optional metadata UUIDs in lowercase hyphenated form are validated by a
|
|
332
|
+
compiled ASCII pattern. All other inputs retain the standard-library UUID validator, including
|
|
333
|
+
legacy accepted formats and their original text after outer whitespace is stripped. There are no
|
|
334
|
+
UUID version or variant restrictions. Schema validation, errors, snapshot freshness and
|
|
335
|
+
command-local cache lifetime are unchanged.
|
|
336
|
+
|
|
337
|
+
Git evidence uses the stored commit graph with replacement objects disabled. Inventory accepts
|
|
338
|
+
Unicode ref names and literal rename/copy paths. Separate Git directories are resolved through Git;
|
|
339
|
+
when invoking from a linked checkout, configure `core.worktree` with the canonical checkout path.
|
|
340
|
+
Metadata timestamps retain the UTC+08:00 offset without a system timezone database requirement.
|
|
341
|
+
Expected filesystem failures on the CLI return the ordinary error envelope (`FILESYSTEM_ERROR`),
|
|
342
|
+
including under `--json`.
|
|
343
|
+
|
|
344
|
+
Storage layout v4 declares the existing `registration-prune-receipts/` directory introduced in
|
|
345
|
+
1.9.2; upgrading the contract does not move data. Controller writes each
|
|
346
|
+
`registration-prune/v1` receipt once and refuses a different document at the same path. A receipt
|
|
347
|
+
is authoritative proof when classifying an interrupted registration-prune transaction as terminal,
|
|
348
|
+
but it is not repository identity or lock authority. The receipt records the reviewed GC plan id;
|
|
349
|
+
its bytes are not an input to that earlier plan digest. Namespace migration includes these JSON
|
|
350
|
+
documents in its merged-content digest and moves or verifies them byte for byte. The separate
|
|
351
|
+
`metadata-reference-relocation/v1` cleanup inventory does not include this directory because it
|
|
352
|
+
has no historical cleanup reference. Existing v3 readers can continue reading their known paths;
|
|
353
|
+
consumers that pin the exact layout must accept v4 before activation.
|
|
354
|
+
|
|
355
|
+
## Resource admission and completion
|
|
356
|
+
|
|
357
|
+
From 1.1.0, checkout and writer counts are statistics across every readable policy
|
|
358
|
+
schema. One per-user host ledger under
|
|
359
|
+
`$XDG_STATE_HOME/worktree-controller/resource-admission/v1` atomically reserves
|
|
360
|
+
filesystem bytes/inodes, expected growth and preservation space across repositories.
|
|
361
|
+
Existing occupied space is already reflected by kernel counters; private materialized
|
|
362
|
+
blocks receive credit without charging shared caches or hardlinks twice. Execution
|
|
363
|
+
uses a memory budget and CPU/memory/I/O pressure hysteresis. Initial checkout growth
|
|
364
|
+
is 2 GiB and 100,000 inodes, with 16 GiB/100,000 inode filesystem headroom; execution
|
|
365
|
+
initially reserves 2 GiB with 4 GiB available-memory headroom. New admission tightens
|
|
366
|
+
under pressure; protected running work is not preempted.
|
|
367
|
+
|
|
368
|
+
From 1.19.17, host ledger saves use sorted-key compact UTF-8 JSON with a final newline.
|
|
369
|
+
Existing indented ledgers remain readable, and readers do not rewrite them. Request and holder
|
|
370
|
+
digests, ledger contract v1, save boundaries, locking, private file permissions, file fsync,
|
|
371
|
+
atomic replacement and directory fsync are unchanged. Repository metadata and immutable receipts
|
|
372
|
+
retain their existing encoding.
|
|
373
|
+
|
|
374
|
+
`admission-queue --all-repositories` observes durable requests; `--apply` reconciles
|
|
375
|
+
through ordinary acquisition and recovery. Waiting grants no writer ownership.
|
|
376
|
+
Agent Workspace's existing process reaper periodically forwards this reconciliation
|
|
377
|
+
and approved release-intent reconciliation, including after a restart. Request keys,
|
|
378
|
+
allocation identities and formal receipts prevent duplicate creation or action replay.
|
|
379
|
+
An attempted operation with uncertain effects requires formal reconciliation.
|
|
380
|
+
|
|
381
|
+
From 1.29.1, an explicitly abandoned integration intent settles its matching, unattempted
|
|
382
|
+
checkout request as `CANCELLED`. Reconciliation verifies the original owner and frozen source
|
|
383
|
+
identity, no transaction/allocation, and absent target and branch under the repository lock,
|
|
384
|
+
then compares the exact ledger row before saving the terminal event. Dry-run reports the same
|
|
385
|
+
decision without writing. Attempted or uncertain requests stay blocked; this does not complete
|
|
386
|
+
the Task or change its source handoff.
|
|
387
|
+
|
|
388
|
+
With `integration_lineage_admission_version` 1, a queued integration task may share its frozen
|
|
389
|
+
feature/audit/hotfix/emergency source authority. Controller verifies that exact intent and source
|
|
390
|
+
lineage under the repository lock before reserving another checkout; the source request, frozen
|
|
391
|
+
evidence, scope and queue position remain intact. Unrelated or same-task request drift is still
|
|
392
|
+
refused. Reuse the original intent and authority. If a durable resource request is already waiting
|
|
393
|
+
and main has advanced, replay that request through `admission-queue --repo <canonical-repo> --apply`
|
|
394
|
+
before preparation/refresh; a fresh claim can otherwise report `ADMISSION_REQUEST_DRIFT`.
|
|
395
|
+
See the [resource-wait recovery sequence](docs/operations.md#resource-wait-and-main-advancing).
|
|
396
|
+
A repeated exact claim of an unchanged clean `PREPARING_INTEGRATION` candidate returns its existing
|
|
397
|
+
allocation without another reservation, event or publication lease. A resource wait reports
|
|
398
|
+
`RESOURCE_WAIT` with the original queued intent and no candidate; scope fences remain separate.
|
|
399
|
+
|
|
400
|
+
Failed acquisition retains its journal whenever Git effects are partial or uncertain. Only the
|
|
401
|
+
existing positive no-effect classifier permits archiving the exact journal in the host request's
|
|
402
|
+
history and making its reservation retryable. Proof is saved before journal removal; interrupted
|
|
403
|
+
settlement resumes through `admission-queue --apply`, without replaying the acquire in that pass.
|
|
404
|
+
An exact guarded release remains terminal after its retained branch is retired through a matching
|
|
405
|
+
Controller authorization and observed deletion receipt. The release record, receipt, allocation,
|
|
406
|
+
branch and OID must agree; missing or consumer-only retirement proof retains the acquire fence.
|
|
407
|
+
During settlement the exact open authorization can supply that proof, while its independent
|
|
408
|
+
outstanding-authorization fence remains until completion. Historical journals stay unchanged.
|
|
409
|
+
`acquire_no_effect_recovery_version=1` declares this recovery support. While archived proof still
|
|
410
|
+
belongs to a reserved attempt, runtime switching requires that capability; settle the attempt before
|
|
411
|
+
rolling back to an older runtime.
|
|
412
|
+
Standalone execution admission reconciles finished execution bindings through the existing
|
|
413
|
+
holder, descendant and runtime checks when that can change its answer: a request that would wait, or
|
|
414
|
+
one refused because an earlier execution of the same task is still recorded, reconciles and is asked
|
|
415
|
+
once more with the same durable request. An admission that is immediately available proceeds without
|
|
416
|
+
that pass and leaves finished executions to their host's own exit handling and the periodic reaper.
|
|
417
|
+
From 1.19.11, standalone execution admission and cancellation do not retain decoded metadata on
|
|
418
|
+
their first, single-snapshot attempt. Admission enables command-local decode reuse only if it
|
|
419
|
+
needs recovery, which can decode unchanged history twice. Composite session admission keeps
|
|
420
|
+
its existing cache across plan, acquisition and execution admission. Validation and guards are
|
|
421
|
+
unchanged; this does not guarantee faster recovery.
|
|
422
|
+
Live or uncertain processes keep their budgets. A final exited session can be settled by `admission-queue --all-repositories --apply`
|
|
423
|
+
or the existing external reaper; each newly launched execution uses a new run identity.
|
|
424
|
+
That pre-admission pass skips a reservation whose exact recorded holder is still running: it has
|
|
425
|
+
nothing to recover yet, and its repository is neither scanned nor locked until the holder exits.
|
|
426
|
+
An execution admitted but never bound to a holder keeps its reservation while its allocation is held,
|
|
427
|
+
because a launch may still be in flight. Formal allocation release settles it with the allocation's
|
|
428
|
+
release receipt; the same passes settle one left behind by an older release, only with that unique
|
|
429
|
+
receipt and the existing Git, path and journal checks.
|
|
430
|
+
A checkout budget whose repository no longer exists settles only in the all-repositories
|
|
431
|
+
`admission-queue --apply` pass, and only on positive absence: the repository, its common directory
|
|
432
|
+
and the checkout path each report ENOENT, and each one's nearest existing ancestor is a real directory
|
|
433
|
+
(not a symlink) on the very filesystem the budget was reserved on. The row records that
|
|
434
|
+
`vanished-repository/v1` evidence. Anything present, any failed observation (EACCES, EIO, ENOTDIR and
|
|
435
|
+
the like), or an ancestor on another filesystem keeps the reservation.
|
|
436
|
+
The same fresh absence proof excludes a vanished checkout and its exact terminal execution
|
|
437
|
+
from repository maintenance enrollment. Historical rows for live repositories still enroll them,
|
|
438
|
+
so approved release intents remain discoverable after the last admission has settled. Unmatched
|
|
439
|
+
or nonterminal executions retain their own observations and budgets.
|
|
440
|
+
|
|
441
|
+
Executor hosts with capability `session_admission_version` 1 may admit a session in two
|
|
442
|
+
commands instead of one command per step. `session-admit` runs `plan`, `acquire` and
|
|
443
|
+
`execution-admit` in one process for a new feature allocation acquired from the canonical
|
|
444
|
+
checkout, and answers every other start (an existing allocation for the task, a non-canonical
|
|
445
|
+
workspace, a plan other than `CREATE_ALLOWED`) with `SESSION_ADMISSION_FALLBACK` without changing
|
|
446
|
+
anything. `session-bind --holder-pid PID` then runs `writer-lease-acquire` for that exact live
|
|
447
|
+
process, the holder-aware `write-guard` and the runtime `binding-add`. Each step is the existing
|
|
448
|
+
command's own function with unchanged guards, journals, receipts and record-version CAS; only
|
|
449
|
+
the repository status every intermediate command would attach is omitted. A bind that fails after
|
|
450
|
+
its lease was granted attempts to release that lease and reports `decision.lease_outcome` for
|
|
451
|
+
Controller and filesystem errors. If release also fails, the outcome retains the exact lease and
|
|
452
|
+
holder identity for recovery; it does not report a successful release. If the execution budget
|
|
453
|
+
was recorded before the runtime binding write failed or was interrupted, that budget stays
|
|
454
|
+
occupied while the holder or its process group is alive, even when lease compensation succeeds.
|
|
455
|
+
After runtime exit, `admission-queue --apply` settles the budget through the existing runtime-clear
|
|
456
|
+
gates without completing the task; a later run resumes the same allocation when needed and
|
|
457
|
+
requests its own execution admission. If the runtime binding was durably written but the
|
|
458
|
+
`SESSION_BOUND` response was lost, retrying with the exact live holder, client/session/run
|
|
459
|
+
identity and binding kind reuses the same lease ID, binding and execution budget; the existing
|
|
460
|
+
lease renewal may update its renewal time and lease record version. A different live holder
|
|
461
|
+
is refused with `WRITER_LEASE_HELD`; a different session/run identity is refused with
|
|
462
|
+
`EXTERNAL_BINDING`, preserving the original binding and budget. Exit recovery still waits for
|
|
463
|
+
the holder and its process group to finish and does not complete the task. If runtime binding
|
|
464
|
+
removal was persisted but the execution-budget release was interrupted or its ledger write
|
|
465
|
+
failed, the budget remains reserved. `admission-queue` observes this pending recovery without
|
|
466
|
+
writing; `admission-queue --apply` rechecks runtime clearance and releases the budget once,
|
|
467
|
+
without restoring a synthetic binding or completing the task. A subsequent session waits while
|
|
468
|
+
the previous holder or its process group is alive, then resumes the retained allocation and
|
|
469
|
+
requests its own budget before binding a new lease. A delayed old `binding-remove` keeps its
|
|
470
|
+
original record-version CAS; stale requests fail with `CAS_MISMATCH`. A queue pass that read the
|
|
471
|
+
old execution before the handover rechecks current runtime occupancy. Neither path can clear
|
|
472
|
+
the new session's binding, lease or budget; replaying an old lease after runtime exit still
|
|
473
|
+
does not release the new lease's budget.
|
|
474
|
+
The detached retention pass that admission starts runs
|
|
475
|
+
after the acquisition and is skipped while one is running or within 300 seconds of the last
|
|
476
|
+
one started.
|
|
477
|
+
|
|
478
|
+
`execution-admit` / `execution-cancel` budget a never-launched managed execution.
|
|
479
|
+
`host-execution` uses the same budget for a process outside a repository; it grants
|
|
480
|
+
no writer lease. `binding-transfer` formally transfers a verified process descendant
|
|
481
|
+
through the existing runtime binding contract. Writer ownership, process occupancy,
|
|
482
|
+
business completion and allocation release remain separate facts.
|
|
483
|
+
Host reservation recovery is scoped to its exact typed holder chain (boot ID, PID,
|
|
484
|
+
start ticks, client/session/run and process group). Another active allocation using
|
|
485
|
+
the same repository or canonical path never keeps a dead reservation alive. Missing
|
|
486
|
+
or malformed identity, any writer lease, and a host target spanning the user home
|
|
487
|
+
or a broader ancestor remain fail-closed. From 1.4.0 a home-or-broader reservation is
|
|
488
|
+
released only by `host-execution --action clear --recover-dead-holder`, which requires the
|
|
489
|
+
caller to present that reservation's own recorded holder digest plus a matching terminal
|
|
490
|
+
record for its session and run, and writes an immutable `host-recoveries/<request-id>.json`
|
|
491
|
+
receipt before the release. An unqualified clear of such a row is still refused.
|
|
492
|
+
The producer refuses it up front: `host-execution --action admit` rejects the home
|
|
493
|
+
root and any of its ancestors, including symlinked aliases, so an unbounded
|
|
494
|
+
reservation cannot be minted again; a concrete working directory is admitted
|
|
495
|
+
unchanged. `host-execution --action inspect` is the read-only recovery
|
|
496
|
+
intelligence surface: for each host-execution reservation owned by the caller's
|
|
497
|
+
uid it publishes the canonical holder bytes (sorted-key compact JSON over
|
|
498
|
+
client_id, pid, boot_id, start_ticks, session_id, run_id) and their SHA-256, the
|
|
499
|
+
live kernel observation proving the recorded holder dead, a target observation
|
|
500
|
+
(`PRESENT`, `ABSENT` or `IDENTITY_CHANGED`), and the terminal-evidence template a
|
|
501
|
+
typed caller must present — no caller needs to read the private ledger layout,
|
|
502
|
+
and inspection never writes ledger bytes. A reservation whose target has
|
|
503
|
+
provably vanished closes through the same typed authority: the orphan's typed
|
|
504
|
+
clear records `target_observation: "ABSENT"` in the single immutable receipt,
|
|
505
|
+
while other actions against a missing target stop with
|
|
506
|
+
`RESOURCE_IDENTITY_CHANGED`. From 1.19.19, normal `cancel` also accepts a missing
|
|
507
|
+
concrete target for an unattempted `WAITING` or `RESERVED` host request owned by
|
|
508
|
+
the caller's uid, with no allocation, holder, runtime binding or writer lease.
|
|
509
|
+
The original request identity remains unchanged; repeat cancellation is idempotent
|
|
510
|
+
and accounts zero memory. A present target must still match its recorded device
|
|
511
|
+
and inode, and an uncertain observation never proves absence. Cancellation grants
|
|
512
|
+
no dead-holder recovery authority or task completion. Crash-replayed identical recovery authority reuses
|
|
513
|
+
the original immutable receipt; differing bytes are always refused.
|
|
514
|
+
|
|
515
|
+
From 1.1.2, acquiring or renewing a writer never creates an execution reservation.
|
|
516
|
+
AW and `worktree-session` admit resources before launch and bind the existing typed
|
|
517
|
+
runtime occupancy before command execution. A waiting session holds no writer.
|
|
518
|
+
The independent terminal wrapper retains its occupancy through parent exit; the
|
|
519
|
+
existing reconciler verifies exit and descendants, clears occupancy, and runs the
|
|
520
|
+
ordinary closeout path. Nested host-managed commands only check their parent's
|
|
521
|
+
full identity. Old writer-only budget records are settled against exact formal
|
|
522
|
+
allocation release receipts, without claiming their coordinator process exited.
|
|
523
|
+
|
|
524
|
+
Status separates `writer_leases_recorded` from `runtime_occupied`. The latter counts
|
|
525
|
+
registered allocations with a `runtime:` binding, including retained and canonical
|
|
526
|
+
allocations; multiple bindings on one allocation count once. `live_executions` is
|
|
527
|
+
retained as an occupancy compatibility alias, with its meaning in
|
|
528
|
+
`live_executions_semantics`. These metadata counts do not observe process liveness
|
|
529
|
+
or imply business completion. A dead historical writer lease is never runtime use.
|
|
530
|
+
|
|
531
|
+
New task resources belong to their recorded owner. With the approved automatic
|
|
532
|
+
feature policy, exact remote-verified, handed-off results carry receipt-bound cleanup
|
|
533
|
+
authority; unknown content and unpublished work remain protected. Persistent data
|
|
534
|
+
must live outside disposable checkouts, or be explicitly preserved before cleanup.
|
|
535
|
+
Only registered reproducible resources and exact verified preservation contracts
|
|
536
|
+
authorize deletion. A normal `worktree-session-end --path <checkout>` resolves its
|
|
537
|
+
allocation and existing evidence itself; it never marks unfinished work successful.
|
|
538
|
+
Stale intents are revalidated into new identities. Integration and publication keep
|
|
539
|
+
their existing serialization; this repository does not enable automatic integration.
|
|
540
|
+
|
|
541
|
+
## Retention convergence
|
|
542
|
+
|
|
543
|
+
Admission is resource based and does not count checkouts, so the population only stays bounded
|
|
544
|
+
if unused checkouts leave again. From 1.12.0, policy schema 8 adds:
|
|
545
|
+
|
|
546
|
+
```toml
|
|
547
|
+
[convergence]
|
|
548
|
+
enabled = true
|
|
549
|
+
grace_minutes = 30 # published / no-change / closed checkouts
|
|
550
|
+
idle_hours_max = 72 # unfinished work while the population is within its reference
|
|
551
|
+
idle_hours_min = 6 # floor under pressure
|
|
552
|
+
standing_reserve = 3 # idle checkouts tolerated beside the live ones
|
|
553
|
+
release_unintegrated_handoffs = true # optional, default false (see below)
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
`reconcile retention --actor <reconciler> [--trigger manual|admission|background] [--apply]`
|
|
557
|
+
reports (default) or applies convergence. Only the `[reconciler]` actor may apply. A checkout is
|
|
558
|
+
live when it holds a writer lease or bindings or a publication lease, or when any process uses
|
|
559
|
+
it. An `ACTIVE` feature/audit allocation with none of that evidence is dormant: clients without
|
|
560
|
+
writer leases end sessions without telling the Controller, so after `idle_hours_max` (pressure
|
|
561
|
+
never shortens it for an `ACTIVE` lane) it is parked exactly as a finished session would park
|
|
562
|
+
itself. Clean, unused feature/audit checkouts, and frozen hotfix handoffs, are routed to an
|
|
563
|
+
existing guarded verb and then to the unchanged `release`:
|
|
564
|
+
|
|
565
|
+
| Facts | Route | Waits |
|
|
566
|
+
| --- | --- | --- |
|
|
567
|
+
| HEAD, base and task commit are in the publication branch | `close-delivered` | grace |
|
|
568
|
+
| HEAD equals base | `complete-no-change` | grace |
|
|
569
|
+
| Frozen feature/audit/hotfix handoff equals HEAD and is published | release | grace |
|
|
570
|
+
| `release_unintegrated_handoffs`: integration-ready feature/audit/hotfix handoff not yet integrated, with task/handoff/Finalizer identities, local branch and `<remote>/<branch>` all equal to HEAD and no open integration intent | release (branch kept) | dynamic idle threshold |
|
|
571
|
+
| Already `RELEASE_READY` / `ARCHIVED` / `NO_CHANGE_COMPLETED` | release | grace |
|
|
572
|
+
| Unfinished commits | `archive-retained` (branch kept) | dynamic idle threshold |
|
|
573
|
+
| `ACTIVE` without lease, process, binding or Git activity | `park`, then the row above that fits | `idle_hours_max` (never shortened by pressure) |
|
|
574
|
+
|
|
575
|
+
The idle threshold is `idle_hours_max` while `registered <= live + standing_reserve`; above that
|
|
576
|
+
it is `max(idle_hours_min, idle_hours_max / pressure^2)`. Idle time is the latest of the
|
|
577
|
+
checkout's Git admin files (HEAD, index, reflog) and its lifecycle events. Dirty, detached,
|
|
578
|
+
locked and missing checkouts are reported as `ATTENTION_REQUIRED` and never touched. An
|
|
579
|
+
unregistered root `.venv` is registered as a reproducible `python-venv`; the release teardown
|
|
580
|
+
preflight still decides whether it may be removed. A hotfix `.venv` is the exception: it needs
|
|
581
|
+
its owner's `handoff-owner-attestation`, so the row stays `BLOCKED` with that recovery command.
|
|
582
|
+
Branches, tags and task evidence remain.
|
|
583
|
+
|
|
584
|
+
Releasing an unintegrated handoff loses only the checkout directory. Its record keeps the frozen
|
|
585
|
+
evidence, and `integration-enqueue` resolves a source without a checkout from its release receipt,
|
|
586
|
+
but only while the retained branch still equals the handoff commit; a moved branch fails with
|
|
587
|
+
`SOURCE_BRANCH_UNAVAILABLE`. Without the switch such handoffs stay `BLOCKED` with a reason that
|
|
588
|
+
names the missing integration step. Release is still not business completion or publication.
|
|
589
|
+
|
|
590
|
+
Every result carries `reason` and `outcome_counts` built from the actual findings, `RELEASED`
|
|
591
|
+
rows carry `receipt_id` / `release_plan_id` of the immutable release evidence, and dirty rows carry
|
|
592
|
+
`changes` plus `owner_action` (review, commit through Git Finalizer or discard explicitly, then
|
|
593
|
+
`park`/`handoff`). Convergence never commits, stashes, resets or discards uncommitted work.
|
|
594
|
+
|
|
595
|
+
Clients whose launch directory is canonical but whose writer leases sit in linked checkouts
|
|
596
|
+
end with `worktree-session-end --path <repo> --client-id <client> --session-id <session> --holder
|
|
597
|
+
ancestor:<program>`: only leases held by exactly that live session are released, and each
|
|
598
|
+
allocation then takes the ordinary terminal route (unfinished work is retained). The hook keeps
|
|
599
|
+
the selected allocation and record CAS: a new writer lease, resumed record or replacement
|
|
600
|
+
allocation blocks terminal parking. Read each allocation's decision; exit status remains advisory
|
|
601
|
+
even when parking is refused. Hosted children never end their parent's allocation, and a repeated
|
|
602
|
+
notification does not repeat lifecycle mutations.
|
|
603
|
+
|
|
604
|
+
With `hosted_session_closeout_check_version` 1, hosted children may run the read-only preparation
|
|
605
|
+
check with their verified host selector. That check never ends, parks or releases an allocation;
|
|
606
|
+
hosted session-ending calls remain refused.
|
|
607
|
+
|
|
608
|
+
With `session_closeout_report_version` 1, run the same selector with `--check` before normal
|
|
609
|
+
exit. It is read-only and returns 2 while files, artifact classification, a Task checkpoint or a
|
|
610
|
+
handoff still need owner work. Include `--run-id` when the lease records one; omitting it never
|
|
611
|
+
inherits a replacement run's lease. `--require-complete` makes the ending call return 2 for an
|
|
612
|
+
incomplete selection or any unsettled allocation. Default hook exit remains 0 for compatibility:
|
|
613
|
+
neither that exit nor `SESSION_ENDED` proves success. Consume `allocations[].terminal_complete`
|
|
614
|
+
and `summary.terminal_complete`. `previous_allocations` re-observes the original selection after
|
|
615
|
+
its lease was released, so retrying an incomplete closeout cannot turn it into an empty success.
|
|
616
|
+
Those persisted observations grant no mutation authority. A retained dirty checkout is preserved,
|
|
617
|
+
not closed.
|
|
618
|
+
|
|
619
|
+
Status, convergence and session results expose `checkout-owner-action/v1` requests carrying the
|
|
620
|
+
allocation, original owner/task/authority, HEAD, expected record CAS and a stable action ID. The
|
|
621
|
+
existing Task consumer matches its binding and re-observes these facts before restoring its owner;
|
|
622
|
+
the request never grants a writer, commit, discard or cleanup. The human `owner_action` convergence
|
|
623
|
+
explanation stays available to older clients.
|
|
624
|
+
|
|
625
|
+
For dependency waiting, persist the checkpoint in the existing Task first. Once its writer and
|
|
626
|
+
bindings end normally, `park --checkpoint-id <content-addressed-artifact> --checkpoint-head <HEAD>
|
|
627
|
+
--waiting-on <dependency>` pins the owner's attestation without claiming content verification or
|
|
628
|
+
completion. A clean retained checkout may immediately use the reviewed `archive-retained` plan
|
|
629
|
+
and ordinary guarded `release`; no idle timeout is required for this owner's explicit closeout.
|
|
630
|
+
Registered resources still use normal teardown; unknown files still block release. The Task keeps
|
|
631
|
+
the dependency, branch and receipt, and obtains a controlled checkout from the exact retained HEAD
|
|
632
|
+
when it resumes. No branch or tag is deleted by waiting or release.
|
|
633
|
+
|
|
634
|
+
Triggers: `acquire` starts a detached pass in its own session (reported as
|
|
635
|
+
`retention_convergence: CONVERGENCE_SCHEDULED`), so it neither slows admission nor can be
|
|
636
|
+
interrupted by a caller's timeout; applying passes are serialized per repository by a
|
|
637
|
+
non-blocking host lock (`CONVERGENCE_BUSY`). Each applying pass writes `RUNNING`, then
|
|
638
|
+
`COMPLETED` (code, reason, counts, released receipt ids) or `FAILED` (error code) beside that
|
|
639
|
+
lock; a dry run returns it as `last_applied_pass`, and a `RUNNING` report whose lock is free reads
|
|
640
|
+
as `INTERRUPTED`. The report is an observation for detached callers, never release evidence.
|
|
641
|
+
The background timer runs `reconcile retention --trigger
|
|
642
|
+
background --apply` beside `reconcile-writers`, which first moves provably dead writers from
|
|
643
|
+
`ACTIVE` to `RETAINED`.
|
|
644
|
+
|
|
645
|
+
## Commands
|
|
646
|
+
|
|
647
|
+
- `status [--json] [--namespace-pid PID]`: join live Git inventory with controller metadata
|
|
648
|
+
and report advisory lifecycle and environment observations when the read succeeds. If it
|
|
649
|
+
fails, the CLI retains the classified error code; use `doctor` for mount evidence.
|
|
650
|
+
- `plan`: make a read-only reuse/budget/authority decision.
|
|
651
|
+
- `write-guard`: bind the selected allocation to the declared task and operation before writes;
|
|
652
|
+
integration allocations reject ordinary feature implementation.
|
|
653
|
+
- `adopt`: register metadata for an existing real worktree without changing Git.
|
|
654
|
+
- `doctor [--namespace-pid PID]`: distinguish Controller authority or lock failures,
|
|
655
|
+
filesystem permission failures, mount namespace mismatches, and `UNKNOWN`.
|
|
656
|
+
It probes repo, `.git`, Git common dir, lock path and parent, and namespace identity; unavailable
|
|
657
|
+
evidence remains `UNKNOWN`.
|
|
658
|
+
- `gc-plan [--namespace-pid PID] [--path PATH --owner-release OWNER | --intent-id ID]`:
|
|
659
|
+
classify every Git registration with lifecycle, owner, session, lease, filesystem and
|
|
660
|
+
namespace observations; a selected target with release authority also runs the existing
|
|
661
|
+
read-only release preflight.
|
|
662
|
+
- `gc-apply --path PATH --plan-id ID [--owner-release OWNER | --intent-id ID]`: revalidate one
|
|
663
|
+
reviewed plan and execute it through the existing release path and receipt.
|
|
664
|
+
- `acquire`: create one explicitly specified policy-approved linked worktree.
|
|
665
|
+
- `canonical-transition`: move a clean frozen canonical handoff to one exact published commit on
|
|
666
|
+
`main` or `master` selected by repository policy, without releasing the canonical checkout.
|
|
667
|
+
It requires the old record CAS, owner, branch,
|
|
668
|
+
HEAD, local publication branch and remote HEAD, plus explicit new control-plane identity. The
|
|
669
|
+
old handoff branch remains at its commit; the publication branch only fast-forwards. A retry
|
|
670
|
+
accepts the exact partial
|
|
671
|
+
state after the ref or checkout moves, and an invalid target policy restores the old checkout.
|
|
672
|
+
With `canonical_main_refresh_version` 1, a handoff already frozen on the policy publication
|
|
673
|
+
branch uses the same entry with `--expected-branch main` (or `master`), the original frozen
|
|
674
|
+
`--expected-head` and the observed `--expected-main-head`. It validates the immutable published
|
|
675
|
+
policy before advancing the checked-out branch and records `CANONICAL_MAIN_REFRESHED`, preserving
|
|
676
|
+
the old handoff history without an extra baseline ref. Interrupted progress keeps main advanced
|
|
677
|
+
and metadata frozen: retry with exact observed main and remote OIDs after proving their ancestry;
|
|
678
|
+
never rewind main or fabricate a release receipt. Unpublished or divergent main stays blocked.
|
|
679
|
+
- `integration-enqueue` / `integration-queue`: persist and inspect ordered `IntegrationIntentV2`
|
|
680
|
+
records without occupying a checkout; source branch, handoff, Finalizer, Snapshot, and scope
|
|
681
|
+
identities are frozen once. An explicit schema v1 policy can use this manual flow with its
|
|
682
|
+
default `origin/main` publication target; no automatic continuation is enabled.
|
|
683
|
+
- `integration-claim`: claim the oldest selected intent when a candidate slot is available and build
|
|
684
|
+
its exact revision from frozen handoffs plus live main without touching canonical checkout state.
|
|
685
|
+
- `integration-refresh`: rebuild a `NEEDS_REPREPARE` candidate on live main and clear old prepared
|
|
686
|
+
eligibility, or move only that candidate to `ATTENTION_REQUIRED` on a real conflict.
|
|
687
|
+
- `integration-validate`: persist an immutable prepared-candidate receipt binding tests, Snapshot,
|
|
688
|
+
base-main, tree, scope, attempt, and candidate identities.
|
|
689
|
+
- `integration-reopen`: invalidate prepared evidence before allowing more integration writes.
|
|
690
|
+
- `lifecycle-events` / `lifecycle-event-ack`: consume and idempotently acknowledge the durable
|
|
691
|
+
integration lifecycle outbox.
|
|
692
|
+
- `integration-recovery-matrix`: expose evidence, repeat, resume, and fail-closed rules by phase.
|
|
693
|
+
- `integration-equivalent`: bind reviewed equivalent-replay evidence and advance the candidate
|
|
694
|
+
identity before validation.
|
|
695
|
+
- `review`: record owner review or explicit owner transfer under record-version CAS. A clean
|
|
696
|
+
`ACTIVE` source task can repair omitted committed files with `--add-scope <exact-file>`
|
|
697
|
+
(repeatable), `--expected-head <full-oid>` and `--scope-reason <reviewed-reason>`.
|
|
698
|
+
Scope review only adds actual out-of-scope files from the original base-to-HEAD diff and records
|
|
699
|
+
the owner, reason, HEAD, base, CAS and before/after scopes in history. Frozen handoffs require
|
|
700
|
+
`reopen` first; integration scopes and sources bound by active intents cannot be amended.
|
|
701
|
+
The ordinary handoff and publication gates still apply after review.
|
|
702
|
+
- `close-preserved`: close verified archived source without declaring task completion or requiring a fabricated Finalizer record.
|
|
703
|
+
- `close-delivered`: explicitly close a retained or legacy preserved checkout whose HEAD and historical commits
|
|
704
|
+
are already in the live publication branch; retain original task evidence and require
|
|
705
|
+
ordinary guarded release for physical removal.
|
|
706
|
+
- `archive-retained`: preserve a clean unfinished retained task on its exact branch, without
|
|
707
|
+
asserting completion or publication; physical removal still uses guarded release.
|
|
708
|
+
Completed preservation cleanup receipts are frozen by actual receipt ID, path and raw SHA.
|
|
709
|
+
Release rechecks these immutable bytes and the exact archive record transition
|
|
710
|
+
(`after CAS = before CAS + 1`); later unrelated metadata changes require a new reviewed plan.
|
|
711
|
+
Formal abandoned integration references use their existing validated effective terminal identity;
|
|
712
|
+
historical source handoffs must still be ancestors of the retained branch. Other open integration
|
|
713
|
+
obligations remain blockers in archive, delivered closeout and retention convergence.
|
|
714
|
+
- `handoff`: record either `integration-ready` or `preserved` evidence.
|
|
715
|
+
- `quarantine`: isolate a semantically drifted integration candidate.
|
|
716
|
+
- `recovery-adopt`: explicitly move a compatible existing checkout under recovery capacity.
|
|
717
|
+
- `externalize`: bind quarantined lineage to a dedicated recovery allocation and retained branch.
|
|
718
|
+
- `resource-register` / `resource-clean`: classify and explicitly clean a task-local Python
|
|
719
|
+
`.venv` using its reproducibility/creator/teardown contract. In addition to existing active,
|
|
720
|
+
retained and receipt-authorized paths, capability `published_handoff_resource_registration_version`
|
|
721
|
+
1 allows a published `HANDOFF_READY` hotfix to classify its existing reproducible on-release
|
|
722
|
+
`.venv` with exact owner/CAS and `--creator handoff-owner-attestation:<owner>`. It verifies frozen
|
|
723
|
+
task/handoff/Finalizer identity, live publication and branch/base ancestry, no open integration
|
|
724
|
+
or transaction obligations, and the existing runtime/environment preflight. It appends proof
|
|
725
|
+
to the new resource event without reopening or changing frozen evidence; physical cleanup and
|
|
726
|
+
release still use their existing guards. Explicit `resource-clean --typed` also supports the
|
|
727
|
+
three nested resources described below.
|
|
728
|
+
- Capabilities `retained_typed_cleanup_version` 1 and `preservation_leaf_version` 1 permit explicit
|
|
729
|
+
exact preservation/cleanup on clean `RETAINED` feature/audit allocations and selected ignored
|
|
730
|
+
regular-file or literal symlink leaves. The existing preservation `plan` keeps `--repo`, `--path`,
|
|
731
|
+
`--allocation-id`, `--owner`, `--expected-record-version`, `--plan-file` and exact `--source`.
|
|
732
|
+
Copy and verify use that immutable plan; they do not authorize cleanup. `resource-clean --typed`
|
|
733
|
+
continues to require reviewed plan identity and the existing primary/additional receipt pairs.
|
|
734
|
+
Original ancestor pins and all allocation, runtime, binding, ref and transaction guards remain
|
|
735
|
+
mandatory. A symlink target is not followed or removed; a regular leaf cannot authorize deleting
|
|
736
|
+
its tracked parent. Cleanup, archival and physical release are separate guarded operations.
|
|
737
|
+
- `lease-acquire`: refresh configured main, validate candidate lineage, and acquire the single
|
|
738
|
+
publication lease.
|
|
739
|
+
- `publication-execution-session`: public, provider-neutral JSON-line verification, authorization
|
|
740
|
+
and result observation for an exact leased candidate; the Controller holds the repository lock
|
|
741
|
+
while the caller performs its Git push. See the protocol below.
|
|
742
|
+
- `lease-complete` / `lease-abort`: verify published remote state or release unchanged authority;
|
|
743
|
+
both bind the exact publication run, and completion defaults to receipt-authorized auto-release.
|
|
744
|
+
- `release-intent`: bind one short-lived feature/audit release authority to exact HEAD, record CAS,
|
|
745
|
+
handoff/Finalizer identity, integration identity, and trigger.
|
|
746
|
+
- `reopen`: invalidate handoff and pending release authority before continuing feature work.
|
|
747
|
+
- `binding-add` / `binding-remove`: maintain explicit Agent Workspace/runtime bindings under CAS;
|
|
748
|
+
new bindings require `ACTIVE` lifecycle.
|
|
749
|
+
- `reconcile receipts`: plan or record immutable receipt reconciliation without rewriting history.
|
|
750
|
+
- `reconcile retention`: report or apply delegated retention convergence (see above).
|
|
751
|
+
- `reconcile release-intents`: evaluate only existing intents and, when policy allows, invoke guarded
|
|
752
|
+
release; capacity pressure never discovers arbitrary clean worktrees.
|
|
753
|
+
- `reconcile integration-intents`: plan or converge v1 migration, historical terminal drift, scope
|
|
754
|
+
waits, missing outbox events, and optional dispatch of already queued intents.
|
|
755
|
+
- `release`: build the same guarded `ReleasePlan` for dry-run and execution, remove only the exact
|
|
756
|
+
evidence-cleared linked checkout, and tear down only explicitly registered `on-release` resources;
|
|
757
|
+
branches and tags remain.
|
|
758
|
+
- `acquire-across`: admit one task in every required repository, or in none; `acquire-when-free`:
|
|
759
|
+
acquire, waiting a bounded time for capacity that may free.
|
|
760
|
+
- `adopt-delivered`: adopt an allocation whose deliverable is already in the publication branch.
|
|
761
|
+
- `integration-abort` / `integration-abandon`: terminate a failed or owner-aborted integration
|
|
762
|
+
preparation, or one whose preparation is already gone.
|
|
763
|
+
- `integration-retry`: plan/CAS a retry of the original failed intent after its aborted candidate
|
|
764
|
+
has complete release evidence; optionally bind a verified source-ancestor prerequisite.
|
|
765
|
+
- `park`: retain an unfinished checkout and free its writer slot.
|
|
766
|
+
- `reconcile-session`: terminal reconciliation when a task session stops; `reconcile-writers`:
|
|
767
|
+
report, and optionally converge, provably inactive writer entitlement.
|
|
768
|
+
- `writer-lease-acquire` / `writer-lease-release`: bind write entitlement to a verifiable live
|
|
769
|
+
process, or give it up without asserting the task finished.
|
|
770
|
+
- `admission-queue`: inspect or resume durable resource admission requests across repositories.
|
|
771
|
+
- `execution-admit` / `execution-cancel`: budget a never-launched managed execution.
|
|
772
|
+
- `host-execution`: budget and track a process outside a repository writer lease; `--action
|
|
773
|
+
inspect` is the read-only recovery-intelligence surface, `--action clear --recover-dead-holder`
|
|
774
|
+
is the only authority that closes a provably dead or orphaned historical reservation.
|
|
775
|
+
- `branch-retirement-plan`: after release, classify one exact allocation/branch, recheck checkout,
|
|
776
|
+
allocation, writer/runtime, intent/transaction, protection, upstream, OID, and ancestry state, then
|
|
777
|
+
persist an immutable execution plan only for release-backed `ANCESTRY` integration.
|
|
778
|
+
Released `feature` and `audit` allocations remain eligible. A released `integration` candidate
|
|
779
|
+
additionally needs its exact remote-verified publication receipt, linked by the release plan
|
|
780
|
+
or by a unique typed release acknowledgement after owner release. The acknowledgement must match
|
|
781
|
+
the publication, integration intent and released allocation version. Repository, allocation,
|
|
782
|
+
candidate, Finalizer and policy target identities must match. Missing
|
|
783
|
+
or mismatched evidence is `INTEGRATION_PUBLICATION_NOT_VERIFIED`; all occupancy and CAS guards
|
|
784
|
+
still apply. Release retains the branch; retirement remains a separate authorized operation.
|
|
785
|
+
- `branch-retirement-status` / `branch-retirement-record`: revalidate the plan and file an
|
|
786
|
+
independent local/remote result. Partial completion is expected and safely resumable. A record is
|
|
787
|
+
accepted only when the Controller observes the ref gone and governance unchanged.
|
|
788
|
+
- `branch-retirement-verify`: the published pre-mutation verdict for exactly one operation
|
|
789
|
+
(`EXECUTABLE`, `STALE`, `BLOCKED`, or `COMPLETE`) plus the CAS fields a ref mutation must carry.
|
|
790
|
+
- `namespace-migration-plan` / `namespace-migrate`: report and, on the caller's exact plan id,
|
|
791
|
+
perform the one-time move of a repository's legacy `codex-worktree/v1` metadata namespace to
|
|
792
|
+
`worktree-controller/v1` under the destination repository lock. Idempotent, replayable, and
|
|
793
|
+
fail-closed on conflict or untrusted evidence; see the migration section below.
|
|
794
|
+
- `namespace-migration-receipts` / `namespace-migration-verify`: list a repository's write-once
|
|
795
|
+
migration receipts, and re-derive a completed migration from that evidence instead of trusting a
|
|
796
|
+
flag.
|
|
797
|
+
|
|
798
|
+
The tool never pushes, commits, merges, rebases, resets, performs `git clean`, force-removes, deletes
|
|
799
|
+
a branch/tag, or runs a daemon. `lease-acquire` and `lease-complete` are the only network operations:
|
|
800
|
+
they fetch exactly the configured publication branch into its remote-tracking ref. Actual main
|
|
801
|
+
publication remains Git Finalizer's authority. Legacy `resource-clean --resource-path .venv`
|
|
802
|
+
requires an owner-registered environment. `resource-clean --typed` requires an exact classified
|
|
803
|
+
plan and the separate cleanup contract below.
|
|
804
|
+
|
|
805
|
+
## Branch retirement
|
|
806
|
+
|
|
807
|
+
Release deliberately retains refs. A formally released feature/audit allocation can then request an
|
|
808
|
+
OID-bound plan without transferring Git mutation authority to Controller.
|
|
809
|
+
|
|
810
|
+
Select the required operations from the authorized task: local retirement and remote retirement
|
|
811
|
+
are separate actions. `branch-retirement-status` counts filed local/remote receipts;
|
|
812
|
+
`pending_operations` lists missing receipts for both actions, including a remote action the task
|
|
813
|
+
did not select. A candidate branch that was never published can therefore remain
|
|
814
|
+
`BRANCH_RETIREMENT_PARTIAL` after its local ref is retired. Preserve that status and its original
|
|
815
|
+
receipts. Record an unselected remote action as outside this task's retirement scope, with a
|
|
816
|
+
separate current remote observation; never manufacture a deletion receipt or execute an extra
|
|
817
|
+
deletion to make the global status complete.
|
|
818
|
+
|
|
819
|
+
After each selected action, query its existing operation-specific entry:
|
|
820
|
+
|
|
821
|
+
```bash
|
|
822
|
+
worktree-controller branch-retirement-verify \
|
|
823
|
+
--repo /absolute/repository \
|
|
824
|
+
--plan-id <sha256-plan-id> --operation local --json
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
For current closeout, require all three facts together: `RETIREMENT_OPERATION_COMPLETE` with the
|
|
828
|
+
matching `receipt`, `minimum_revalidation.ref_state = ABSENT`, and empty `blockers`. Check the exact
|
|
829
|
+
plan and operation identities. A completed operation may survive an ordinary integrated-main
|
|
830
|
+
fast-forward when its exact receipt matches, its ref stays absent, all other plan identity and
|
|
831
|
+
governance facts are unchanged, and the original integrated OID is an ancestor of the current OID.
|
|
832
|
+
The original plan and receipt retain their OIDs; `minimum_revalidation` reports the current
|
|
833
|
+
observation. `RETIREMENT_OPERATION_COMPLETE` remains a historical marker even when current
|
|
834
|
+
revalidation has blockers; it never suffices for closeout alone. Recreated refs, lost ancestry,
|
|
835
|
+
mismatched receipts and actual governance drift fail
|
|
836
|
+
closed. Pending operations keep exact integrated-OID CAS, including the other half of a partially
|
|
837
|
+
completed plan. Repeated read-only queries neither delete refs nor file receipts. Close a task
|
|
838
|
+
requiring both actions only after checking both operations independently.
|
|
839
|
+
|
|
840
|
+
The remote operation's `ref_state` observes the local remote-tracking ref. Use Git Finalizer's
|
|
841
|
+
live remote verification or a successful exact `git ls-remote --exit-code <remote> <full-ref>` to
|
|
842
|
+
verify the actual remote as well. Accept only an exact returned full ref name: exit 0 with its OID
|
|
843
|
+
means present; exit 2 with no match means absent. Unexpected output and transport/authentication
|
|
844
|
+
errors are unknown and do not prove absence. A remote ref that remains
|
|
845
|
+
present must be reported explicitly, and selected remote retirement stays open. An absent ref
|
|
846
|
+
without an existing receipt yields `ABSENT_UNATTESTED`; keep that verdict instead of claiming a
|
|
847
|
+
deletion occurred. Release, main publication and task/business completion keep their separate
|
|
848
|
+
evidence requirements.
|
|
849
|
+
|
|
850
|
+
Capability `canonical_transition_retirement_version` 1 also covers the old baseline branch left by
|
|
851
|
+
`canonical-transition`. Select that branch explicitly with `--branch <old-baseline-branch>` rather
|
|
852
|
+
than `--allocation-id`. Exactly one complete transition event in the current canonical allocation
|
|
853
|
+
must prove its old HEAD and the recorded publication ancestry on the policy remote/target. The plan
|
|
854
|
+
reports `CANONICAL_TRANSITION_BACKED`, a null release receipt, and `canonical_transition_evidence`
|
|
855
|
+
bound into its identity. Canonical stays registered on main; no release receipt is synthesized.
|
|
856
|
+
Ordinary unregistered refs remain report-only. Missing, ambiguous or changed history, occupied
|
|
857
|
+
branches, leases, runtime bindings, recovery state and unresolved obligations still block retirement.
|
|
858
|
+
|
|
859
|
+
If an owner released the checkout without naming an existing release intent, its exact completed
|
|
860
|
+
release plan and receipt can resolve that intent for retirement, including an intent later marked
|
|
861
|
+
`STALE`. Repository, allocation, frozen owner/task/authority, HEAD and record-version identities must
|
|
862
|
+
match; the original intent remains unchanged. An unrelated or incomplete receipt still blocks
|
|
863
|
+
retirement, and checkout, ancestry, publication, runtime and transaction guards still apply.
|
|
864
|
+
|
|
865
|
+
For a published integration candidate released through `--owner-release`, run the existing
|
|
866
|
+
`reconcile receipts --receipt-id <publication-receipt-id>` after release. Retirement accepts its
|
|
867
|
+
unique `RELEASE_STATE_ACKNOWLEDGED` receipt only for the exact publication and released record
|
|
868
|
+
version. Original release plans and publication receipts stay unchanged; missing, ambiguous or
|
|
869
|
+
stale acknowledgements fail closed. Retirement plans bind acknowledgement bytes and recheck them
|
|
870
|
+
before execution.
|
|
871
|
+
|
|
872
|
+
```bash
|
|
873
|
+
worktree-controller branch-retirement-plan \
|
|
874
|
+
--repo /absolute/repository \
|
|
875
|
+
--allocation-id <released-allocation-uuid> \
|
|
876
|
+
--remote origin --integrated-into main --json
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
An eligible response includes a provider-neutral `execution` recipe plus, for compatibility only,
|
|
880
|
+
the released `finalizer` argument array. The consumer then asks the Controller for a grant:
|
|
881
|
+
|
|
882
|
+
```bash
|
|
883
|
+
worktree-controller branch-retirement-authorize \
|
|
884
|
+
--repo /absolute/repository \
|
|
885
|
+
--plan-id <sha256-plan-id> --operation local \
|
|
886
|
+
--request-id <uuid> --holder self --json
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
The grant is single-use and durable. It is minted while the Controller holds `repo.lock`
|
|
890
|
+
exclusively, and it binds the plan identity, the `controller_state_digest` of that section, the
|
|
891
|
+
`minimum_revalidation` eight-tuple (`plan_id`, `operation`, `branch`, `expected_oid`,
|
|
892
|
+
`expected_integrated_oid`, `integrated_ref`, `ref_state`, `controller_state_digest`) and an
|
|
893
|
+
`observed` vector of the literal Git facts that digest cannot see: watched ref to OID, checked-out
|
|
894
|
+
paths, upstream ref and target, classification and blockers. It also returns a 256-bit execution
|
|
895
|
+
`token` — the only capability the grant carries. The consumer performs its own expected-OID
|
|
896
|
+
`git update-ref -d` / `push --force-with-lease` and settles:
|
|
897
|
+
|
|
898
|
+
```bash
|
|
899
|
+
worktree-controller branch-retirement-complete \
|
|
900
|
+
--repo /absolute/repository \
|
|
901
|
+
--authorization-id <sha256> --token <token> --result-file execution-result.json --json
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
`complete` re-reads the refs, re-derives the digest and re-evaluates governance inside its own
|
|
905
|
+
exclusive section. It files `RETIREMENT_EXECUTION_COMPLETE` only for exactly one authorized delta —
|
|
906
|
+
the target ref moving from its expected OID to absent — and answers
|
|
907
|
+
`RETIREMENT_EXECUTION_REFUSED` with `OBSERVATION_*` blockers, or `EXECUTION_OBSERVATION_MISMATCH`
|
|
908
|
+
when the ref is still there. A result document is never a witness; `abandon` closes a grant with
|
|
909
|
+
immutable evidence and deliberately never files a receipt, because absence alone does not prove who
|
|
910
|
+
caused it. Outstanding grants are visible in `branch-retirement-executions --action list`, in
|
|
911
|
+
`branch-retirement-verify` (`RETIREMENT_AUTHORIZATION_ACTIVE`, for the granted operation only) and
|
|
912
|
+
in `doctor` (`RETIREMENT_AUTHORIZATION_OUTSTANDING`).
|
|
913
|
+
|
|
914
|
+
A grant expires on its own clock without releasing anything: expiry means the claim needs
|
|
915
|
+
verification, never that the work is done. An expired grant still fences occupancy, still has to be
|
|
916
|
+
settled or abandoned with its token, and a settlement that lands late is recorded as
|
|
917
|
+
`grant_expired_at_settle` rather than refused, so a slow remote compare-and-delete can still be
|
|
918
|
+
attested honestly. `--holder` binds the grant to the caller's own process tree (`self` or
|
|
919
|
+
`ancestor:<program>`); a detached pid is refused, because naming somebody else's process proves
|
|
920
|
+
nothing.
|
|
921
|
+
|
|
922
|
+
`branch-retirement-verify` remains the released pre-mutation verdict, and
|
|
923
|
+
`branch-retirement-record` remains the released filing path. Both are now Controller-observed rather
|
|
924
|
+
than caller-attested: `record` refuses a completion whose ref it can still see, refuses an operation
|
|
925
|
+
holding a grant, and refuses with `EXECUTION_GOVERNANCE_MOVED` when authority or occupancy changed
|
|
926
|
+
after the plan. Receipts carry `provenance` — `AUTHORIZED_EXECUTION`, `CONTROLLER_OBSERVED`, or
|
|
927
|
+
`CONSUMER_CLAIM` for history filed before this contract existed — so an auditable distinction
|
|
928
|
+
replaces the assumption that a receipt implies an authorized execution. Consumer-specific argument
|
|
929
|
+
and summary vocabulary lives only in `retirement_adapters.py`; governance speaks
|
|
930
|
+
`retirement-execution-result/v1` and never names a consumer product.
|
|
931
|
+
|
|
932
|
+
Contract compatibility is checked against a real consumer in
|
|
933
|
+
`tests/test_branch_retirement_git_finalizer_acceptance.py`, which is skipped unless
|
|
934
|
+
`WORKTREE_CONTROLLER_GIT_FINALIZER` and `WORKTREE_CONTROLLER_GIT_FINALIZER_COMPANION` point at an
|
|
935
|
+
installation; the default suite needs only Git, and drives grant, settle, recovery and the installed
|
|
936
|
+
wheel through the mock executor in `tests/test_retirement_standalone.py`.
|
|
937
|
+
|
|
938
|
+
Only direct ancestry is executable in v1. `CHERRY_EQUIVALENT`, `MERGE_ONLY`, `UNIQUE`, `MIXED`, and
|
|
939
|
+
historical unregistered refs are classification/report output only. Protected publication refs and
|
|
940
|
+
rescue/archive/recovery lineage are never eligible. Controller does not invoke `git branch`,
|
|
941
|
+
`update-ref`, or remote deletion.
|
|
942
|
+
|
|
943
|
+
When Controller is available for a repository with enabled policy, linked worktree creation and
|
|
944
|
+
removal must use `acquire`/`release`. If Controller is unavailable or metadata is corrupt, use
|
|
945
|
+
`doctor`/recovery and fail closed instead of raw worktree add/remove. Read-only Git and branch,
|
|
946
|
+
merge, and commit operations remain outside Controller mutation authority.
|
|
947
|
+
|
|
948
|
+
## Public integration publication session
|
|
949
|
+
|
|
950
|
+
Capability `integration_publication_execution_version` 1 advertises
|
|
951
|
+
`integration-publication-session/v1`. Start it with the exact candidate checkout and the identities
|
|
952
|
+
returned by `lease-acquire`:
|
|
953
|
+
|
|
954
|
+
```bash
|
|
955
|
+
worktree-controller publication-execution-session --repo /absolute/candidate \
|
|
956
|
+
--candidate-oid <full-oid> --lease-id <uuid> --run-id <run> --json
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
This command exchanges UTF-8 JSON lines rather than the ordinary status envelope. It emits
|
|
960
|
+
`verified` with an opaque public `identity`, an `action` and `remote_oid`. Send
|
|
961
|
+
`{"schema_version":1,"contract":"integration-publication-session/v1","action":"authorize","identity":<identity>}`.
|
|
962
|
+
The producer rechecks authority and returns `authorized`; only `PUSH_CANDIDATE` authorizes the
|
|
963
|
+
exact non-force branch push. `RECOVER_ALREADY_PUBLISHED` requires no push. Afterwards send
|
|
964
|
+
`complete` with the same identity and `result` containing `record_id`,
|
|
965
|
+
`executor: {name, version}`, boolean `push_executed` and the observed `remote_oid`. The Controller
|
|
966
|
+
independently reobserves Git before returning `completed` with matching execution evidence.
|
|
967
|
+
|
|
968
|
+
One Controller-owned exclusive lock spans all three phases. Frames are limited to 64 KiB and
|
|
969
|
+
input waits to 300 seconds. Wrong identities, duplicate keys, missing or drifted authority,
|
|
970
|
+
disconnects and timeouts fail closed. A disconnected executor may already have pushed: retry the
|
|
971
|
+
same lease/run/candidate after observing the remote, and an already-published candidate is
|
|
972
|
+
recovered without another mutation even after lease expiry. The local caller and OS are trusted;
|
|
973
|
+
this does not protect against arbitrary interference by other processes running as the same user.
|
|
974
|
+
|
|
975
|
+
A completed session does not change allocation lifecycle or release authority. Pass its consumer
|
|
976
|
+
`record_id` to the existing `lease-complete`; that command persists the publication receipt and
|
|
977
|
+
owns lease release and guarded cleanup. No daemon, new state directory or external executor is
|
|
978
|
+
introduced. `tests/test_publication_execution.py` tests the producer and enables real Finalizer
|
|
979
|
+
acceptance when `GIT_FINALIZER_ACCEPTANCE` names the consumer entrypoint.
|
|
980
|
+
|
|
981
|
+
## Closed lifecycle history
|
|
982
|
+
|
|
983
|
+
`status` observes currently registered Git worktrees and their live allocation bindings. Releasing a
|
|
984
|
+
checkout removes that registration and binding; it retains the allocation record, immutable release
|
|
985
|
+
plan/receipt and any integration, preparation and publication evidence. From 1.13.0, the read-only
|
|
986
|
+
`closed-lifecycle/v1` query exposes those existing records without creating another state source:
|
|
987
|
+
|
|
988
|
+
```bash
|
|
989
|
+
worktree-controller lifecycle-history --repo /absolute/repository --allocation-id <uuid> --json
|
|
990
|
+
worktree-controller lifecycle-history --repo /absolute/repository --authority-key <authority> --json
|
|
991
|
+
worktree-controller lifecycle-history --repo /absolute/repository --task-key <task> --json
|
|
992
|
+
worktree-controller lifecycle-history --repo /absolute/repository --integration-intent-id <uuid> --json
|
|
993
|
+
worktree-controller lifecycle-history --repo /absolute/repository --release-receipt-id <uuid> --json
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
Exactly one selector is required. `decision.live_allocations` is a current observation, while
|
|
997
|
+
`decision.closed_releases` is historical evidence keyed by immutable `release_receipt_id`. The latter
|
|
998
|
+
contains allocation/authority/task, the owner from the release plan, original branch/path/HEAD,
|
|
999
|
+
release status/time, and existing integration candidate, publication time, prepared validation receipt
|
|
1000
|
+
and Finalizer references. Publisher identity is separate from the source task owner. An authority/task
|
|
1001
|
+
may have several allocations: never choose a historical owner by recency or reuse the current owner.
|
|
1002
|
+
Source delivery joins use the immutable publication receipt's `source_results`, not the current
|
|
1003
|
+
integration intent. All matching immutable preparations are returned; an ambiguous single preparation
|
|
1004
|
+
is null. Later publication receipts can enrich the query, while existing receipt identities remain
|
|
1005
|
+
stable. A closed receipt remains queryable if its mutable integration intent is no longer available.
|
|
1006
|
+
Closed releases sort by receipt ID; paginate with `--limit` (1..1000, default 100) and the returned
|
|
1007
|
+
`next_after` as `--after`. A missing match returns empty lists, never an inferred completion.
|
|
1008
|
+
|
|
1009
|
+
Receipt joins reuse query-local indexes, and only the returned page's evidence is serialized.
|
|
1010
|
+
Every metadata file and every matching release remains validated, including linked identity
|
|
1011
|
+
conflicts beyond that page. Repository locking and atomic disk storage live in `store.py`;
|
|
1012
|
+
versioned format validation and serialization live in `metadata_codec.py`, with the existing
|
|
1013
|
+
store import interface retained.
|
|
1014
|
+
|
|
1015
|
+
Legacy RELEASED records without a formal release receipt appear in `incomplete_history`. Missing
|
|
1016
|
+
optional publication/validation evidence is null; contradictory linked identities fail closed. The
|
|
1017
|
+
query neither backfills evidence nor changes retention/release gates. Storage layout and metadata
|
|
1018
|
+
schema remain unchanged, and no database, daemon or history tracker is added.
|
|
1019
|
+
|
|
1020
|
+
Handbook consumers should retain only the stable receipt reference in their narration and fetch this
|
|
1021
|
+
query during verification. Verify `proof_status=COMPLETE`, exact allocation/authority/task/owner and
|
|
1022
|
+
the required delivery's existing publication receipt; confirm its candidate exists and is an ancestor
|
|
1023
|
+
of the Handbook baseline. Resource release alone, including archived unfinished work, does not prove
|
|
1024
|
+
publication or business completion. A `release:<integration intent>` citation can continue naming the
|
|
1025
|
+
same intent: use its historical source allocation's release receipt to prove the source owner, rather
|
|
1026
|
+
than the integration publisher. Keep live writer authorization for narrative writes and do not copy
|
|
1027
|
+
Controller history into Handbook state. Old missing evidence stays unverified until its owner uses
|
|
1028
|
+
an existing supported recovery path.
|
|
1029
|
+
|
|
1030
|
+
## Legacy namespace migration
|
|
1031
|
+
|
|
1032
|
+
`namespace-migration/v1` is the supported per-repository transition in 1.11.0. Plan and verify are
|
|
1033
|
+
read-only. Apply requires an operator, the exact reviewed plan id, and the destination lock. The
|
|
1034
|
+
runtime defects from the 1.8.0 audit are closed in this release. Storage layout v4 only documents
|
|
1035
|
+
the already existing registration-prune receipt directory. This release does not migrate any
|
|
1036
|
+
repository.
|
|
1037
|
+
|
|
1038
|
+
Repositories created before the current layout keep their metadata under
|
|
1039
|
+
`<git-common-dir>/codex-worktree/v1`. Nothing in the runtime discovers a namespace, so such a
|
|
1040
|
+
repository reads as unmanaged and its real state stays invisible. `namespace-migrate` is the only
|
|
1041
|
+
supported way to move it; a caller never copies, edits or deletes namespace JSON:
|
|
1042
|
+
|
|
1043
|
+
```bash
|
|
1044
|
+
worktree-controller namespace-migration-plan --repo /absolute/repository --json
|
|
1045
|
+
worktree-controller namespace-migrate --repo /absolute/repository \
|
|
1046
|
+
--actor <who-is-responsible> --expected-plan-id <plan-id-from-the-previous-command> --json
|
|
1047
|
+
worktree-controller namespace-migration-receipts --repo /absolute/repository --json
|
|
1048
|
+
worktree-controller namespace-migration-verify --repo /absolute/repository --json
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
`namespace-migration-plan` is read-only, and its decision is exactly what `namespace-migrate` will
|
|
1052
|
+
do with it:
|
|
1053
|
+
|
|
1054
|
+
| Legacy and current state | Decision | Effect |
|
|
1055
|
+
| --- | --- | --- |
|
|
1056
|
+
| content only under `codex-worktree/v1` | `NAMESPACE_MIGRATION_REQUIRED` | documents move and `repo.json` lands last |
|
|
1057
|
+
| legacy absent and no unsettled prepared receipt | `NAMESPACE_ALREADY_CURRENT` | an exact no-op CAS writes nothing; a settled transition replays its original receipt |
|
|
1058
|
+
| both namespaces hold equivalent content, or the destination holds a document the runtime cannot parse | `NAMESPACE_CONVERGENCE_REQUIRED` | redundant legacy copies clear and the destination document is rewritten into its authoritative form |
|
|
1059
|
+
| both namespaces hold the same path with different content | `NAMESPACE_CONFLICT` | refuses; nothing moves and no receipt is written |
|
|
1060
|
+
| evidence the Controller cannot trust: a non-JSON or unreadable document, a symlink, a missing `repo.lock`, no `repo.json` in either namespace, an unfinished journal | `NAMESPACE_EVIDENCE_INVALID`, `NAMESPACE_LOCK_MISSING`, `NAMESPACE_IDENTITY_MISSING`, `NAMESPACE_UNSAFE` | refuses before any write |
|
|
1061
|
+
|
|
1062
|
+
The plan id digests the reviewed merged content and is the compare-and-set the caller must present.
|
|
1063
|
+
A new transition re-derives it inside the lock. A `PREPARED` receipt stores that original identity,
|
|
1064
|
+
the per-document digests, lease conversions and policy move. If interrupted, the read-only plan
|
|
1065
|
+
reports `recovery_required` and the original plan id; apply accepts only that id and checks source
|
|
1066
|
+
and immutable evidence before finishing. Later Controller updates to mutable destination records
|
|
1067
|
+
do not create a second migration identity.
|
|
1068
|
+
|
|
1069
|
+
The whole transition holds an exclusive `flock` on the **destination** `repo.lock`, creating it
|
|
1070
|
+
before any content lands. That is the same lock `adopt`, `acquire` and `integration-enqueue` take
|
|
1071
|
+
when they bootstrap metadata, so a concurrent verb queues behind the migration instead of minting a
|
|
1072
|
+
second repository identity beside the tree being moved. The legacy lock is taken without waiting: a
|
|
1073
|
+
caller holding it is an activation in flight, and blocking there would invert that flow's lock
|
|
1074
|
+
order, so the migration fails closed with `NAMESPACE_BUSY` and is retried. A read-only plan never
|
|
1075
|
+
queues at all; it defers the unfinished-journal question to the fence and reports
|
|
1076
|
+
`"preflight": "under_fence"`.
|
|
1077
|
+
|
|
1078
|
+
`repo.json` is the only file that makes a namespace authoritative, so it is the last thing to land
|
|
1079
|
+
and the destination copy is re-digested against the plan before and after the switch. After that the
|
|
1080
|
+
legacy namespace is removed only when it is demonstrably empty (`NAMESPACE_RESIDUE` otherwise), and
|
|
1081
|
+
the `metadata-reference-relocation/v1` record is written only once it is gone - the two together are
|
|
1082
|
+
what lets immutable completion receipts keep naming the old path and still resolve. Converted leases
|
|
1083
|
+
are published complete, and interrupted JSON scratch files do not change the plan identity. Ten
|
|
1084
|
+
fault injection points cover the prepared receipt, conversion and rewrite windows, authority switch,
|
|
1085
|
+
relocation record, policy move and settlement; the exact names are in `tool_cli_contract.json`.
|
|
1086
|
+
|
|
1087
|
+
Receipts under `namespace-migrations/` are write-once `0400` documents. The receipts verb reports
|
|
1088
|
+
them, and `namespace-migration-verify` flags an unsettled `PREPARED` receipt or invalid settlement
|
|
1089
|
+
evidence. A settled receipt must still describe the moved tree, the relocation record must validate
|
|
1090
|
+
against its own directory identity, and the legacy namespace must be gone. Migration does not change
|
|
1091
|
+
retirement semantics: plans, grants and receipts move byte for byte, and a released branch's plan
|
|
1092
|
+
keeps resolving after the move.
|
|
1093
|
+
|
|
1094
|
+
## Unified release plan and disposable resources
|
|
1095
|
+
|
|
1096
|
+
Clean `RETAINED` feature/audit checkouts can use `close-delivered` when their committed
|
|
1097
|
+
content is already published. First run `close-delivered --path PATH
|
|
1098
|
+
--expected-record-version N --owner-release OWNER --actor ACTOR --dry-run --json`.
|
|
1099
|
+
Review its exact record, branch and publication evidence, then repeat with
|
|
1100
|
+
`--plan-id PLAN_ID` in place of `--dry-run`. This writes a closeout receipt and moves
|
|
1101
|
+
the checkout to `RELEASE_READY`. It does not declare the business task complete or
|
|
1102
|
+
change HEAD, files, ownership, original task commits or refs. A live publication query
|
|
1103
|
+
must agree with the local tracking ref; stale tracking is refused rather than fetched.
|
|
1104
|
+
|
|
1105
|
+
From 1.13.4, the same reviewed plan also closes a legacy `PRESERVED` feature/audit checkout
|
|
1106
|
+
whose HEAD, base and task commit are already contained in live publication, but which lacks
|
|
1107
|
+
frozen handoff/Finalizer evidence. It preserves that original evidence rather than inventing a
|
|
1108
|
+
handoff or declaring completion. Bindings, leases, runtime uncertainty, ignored content and any
|
|
1109
|
+
uncleaned resource still block it. Verified source archives keep their existing preservation
|
|
1110
|
+
release flow; this recovery does not enter automatic convergence or enable preserved auto-release.
|
|
1111
|
+
|
|
1112
|
+
Use `gc-plan --path PATH --owner-release OWNER` and the returned plan with `gc-apply`,
|
|
1113
|
+
or the normal `release --dry-run` / `release`, to remove the checkout. Every release
|
|
1114
|
+
rechecks the immutable closeout receipt, publication ancestry, live processes, bindings,
|
|
1115
|
+
resources and ignored content. Dirty, ACTIVE, frozen-handoff, unintegrated or uncertain
|
|
1116
|
+
targets cannot use this closeout. A retry after interruption uses the same plan and
|
|
1117
|
+
receipt. `RELEASE_READY` requires `delivered_closeout_version` 1; older runtimes reject
|
|
1118
|
+
that lifecycle, so rollback must pass the rollout gate with the actual repository state.
|
|
1119
|
+
|
|
1120
|
+
For clean `RETAINED` work that is still unpublished, use `archive-retained --path PATH
|
|
1121
|
+
--expected-record-version N --owner-release OWNER --actor ACTOR --dry-run --json`.
|
|
1122
|
+
Review the branch and original task record, then repeat with `--plan-id PLAN_ID`.
|
|
1123
|
+
The immutable receipt moves the allocation to `ARCHIVED` without changing its commits,
|
|
1124
|
+
owner or task evidence. The exact local branch must contain the recorded task lineage;
|
|
1125
|
+
unresolved integration, active use, unknown ignored content and unsafe resources block
|
|
1126
|
+
archiving. `gc-plan` / `gc-apply` then apply the ordinary release gates. An unreleased
|
|
1127
|
+
archive can return to `RETAINED` through `reopen`. `ARCHIVED` requires
|
|
1128
|
+
`retained_archive_version` 1, so rollback must pass the actual repository-state gate.
|
|
1129
|
+
|
|
1130
|
+
`release --dry-run` is an official read-only preflight for every managed releasable worktree,
|
|
1131
|
+
including worktrees without `.venv`. Dry-run and execution call the same `build_release_plan()`
|
|
1132
|
+
guard path. Execution persists the exact plan, rechecks HEAD and record-version CAS plus all runtime,
|
|
1133
|
+
editable, external, dirty, ignored-content, and retained-ref guards, and writes an immutable release
|
|
1134
|
+
receipt. An interrupted execution resumes only from that persisted identity; an exact repeated call
|
|
1135
|
+
returns `ALREADY_RELEASED`.
|
|
1136
|
+
|
|
1137
|
+
Governance v1.1 adds no lifecycle state or deletion mechanism. `status` and `gc-plan` observe
|
|
1138
|
+
recorded owner, lifecycle, writer lease, host session and runtime occupancy. Host session health
|
|
1139
|
+
is `UNKNOWN` without independent evidence. Lease renewal is reported as its own evidence; it is
|
|
1140
|
+
not an independent heartbeat. Stale and orphan findings are advisory; neither changes stored
|
|
1141
|
+
lifecycle state nor grants release eligibility. `gc-plan`
|
|
1142
|
+
reports allocation, path, branch, lifecycle, owner, dirty state, session, lease, candidate reason,
|
|
1143
|
+
filesystem and namespace health, and blocking facts for every registered checkout. A global
|
|
1144
|
+
plan probes each registered checkout's repo path and `.git` entry independently, alongside its
|
|
1145
|
+
view of the Git common dir and lock path. A path that cannot be inspected has an `UNKNOWN` mount state;
|
|
1146
|
+
the plan does not infer another candidate's mount state from the requested checkout. The global
|
|
1147
|
+
report is advisory: a clean worktree alone is never release authority. For one reviewed target,
|
|
1148
|
+
provide its exact recorded owner via `--owner-release` or an existing `--intent-id`; the resulting
|
|
1149
|
+
`gc_plan_id` is stable across timestamp-only ReleasePlan changes. `gc-apply` checks that identity
|
|
1150
|
+
again inside the release lock before any mutation. Drift or a current lease stops the apply;
|
|
1151
|
+
normal `release` remains the recovery entry for an interrupted release. Release plans, receipts,
|
|
1152
|
+
and storage layout remain unchanged.
|
|
1153
|
+
|
|
1154
|
+
Environment diagnosis distinguishes Controller authority or lock faults from filesystem permission
|
|
1155
|
+
faults and mount differences seen in a selected process namespace. A repo can be writable while
|
|
1156
|
+
its `.git` entry is read-only in another namespace. `doctor` retains detailed mount evidence and
|
|
1157
|
+
reports it before attributing an observed lock failure to Controller authority. A successful
|
|
1158
|
+
`status` response includes the same environment diagnosis; on failure, its CLI response keeps the
|
|
1159
|
+
classified error code and `doctor` supplies the detailed evidence. Controller lock attribution
|
|
1160
|
+
requires the relevant environment probes to confirm writable access. An
|
|
1161
|
+
unavailable namespace comparison remains `UNKNOWN` for that comparison, while a confirmed
|
|
1162
|
+
read-only mount still establishes a filesystem fault. A namespace difference alone does not
|
|
1163
|
+
identify the sandbox provider. No diagnostic changes mounts or permissions.
|
|
1164
|
+
`mount_diagnostics.environment` and top-level `environment_diagnosis` classify the observed
|
|
1165
|
+
environment as `HEALTHY`, `MOUNT_NAMESPACE_MISMATCH`, `FILESYSTEM_PERMISSION`, or `UNKNOWN`;
|
|
1166
|
+
`doctor` also exposes `decision.primary_diagnosis` to distinguish `CONTROLLER_LOCK_AUTHORITY`,
|
|
1167
|
+
other `CONTROLLER_METADATA` findings, and `NONE` after environment checks.
|
|
1168
|
+
|
|
1169
|
+
`status` and `gc-plan` include `governance_capacity` in `observe` mode. `active_limit` and
|
|
1170
|
+
`retained_limit` use the policy feature/audit slot declarations and count those linked roles in
|
|
1171
|
+
their respective lifecycle states. `total_limit` uses `hard_limit` and counts all Git registrations.
|
|
1172
|
+
These observations do not alter resource admission or the historical top-level `retained_limit`
|
|
1173
|
+
pool statistic.
|
|
1174
|
+
|
|
1175
|
+
`resource-clean --typed --dry-run` adds `typed-disposable-resource/v1` for a source allocation in
|
|
1176
|
+
`HANDOFF_READY`, without reopening it or changing its handoff/publication identity. The initial
|
|
1177
|
+
paths are deliberately limited to `backend/.venv` (tracked Python project and uv lock, venv
|
|
1178
|
+
identity and standard structure), `frontend/node_modules` (tracked npm project/lock and matching
|
|
1179
|
+
installed dependency identities), and `frontend/dist` (the declared default Vite build output).
|
|
1180
|
+
Root `.venv`, user/provider state, arbitrary caches, other package managers and ambiguous build
|
|
1181
|
+
configurations are outside this contract. Existing release-cache handling is unchanged and receives
|
|
1182
|
+
no typed cleanup action; unknown ignored content blocks the entire cleanup.
|
|
1183
|
+
|
|
1184
|
+
Every dry-run resource reports `resource_type`, `path`, `classification`, `cleanup_action` and
|
|
1185
|
+
`blocker`. A disposable identity alone does not authorize deletion: paths must be canonical and
|
|
1186
|
+
owned, with no tracked or staged content, mounts, special objects, external bindings, writer lease,
|
|
1187
|
+
or an allocation-scoped managed/observed runtime reference. Runtime scanning pins each proc directory, reads
|
|
1188
|
+
`stat`/`status`, and reopens the numeric PID to compare directory identity, `start_ticks`, UIDs and
|
|
1189
|
+
whether it is multi-threaded (a resizing thread pool is the same process; becoming single- or
|
|
1190
|
+
multi-threaded is not). Identity changes cause at most three immediate attempts, without waiting. A
|
|
1191
|
+
missing field alone is not process-exit evidence: `ENOENT`/`ESRCH` require a PID identity recheck; a
|
|
1192
|
+
missing fd requires confirmation that that descriptor closed. A process whose fields vanish, with
|
|
1193
|
+
or without its fd directory also denying access, is rescanned after a short pause up to the same
|
|
1194
|
+
bound: a finished exit resolves to terminal or absent, a live process stays uncertain. A stable single-thread `Z`/`X` process has
|
|
1195
|
+
already released its mm/files/fs in Linux `do_exit`, so inaccessible fields cannot represent active
|
|
1196
|
+
references. A zombie leader with other threads does not qualify for this exclusion.
|
|
1197
|
+
|
|
1198
|
+
Typed cleanup uses `allocation-runtime-authority/v1`:
|
|
1199
|
+
|
|
1200
|
+
| Classification | Authority/evidence | Effect |
|
|
1201
|
+
| --- | --- | --- |
|
|
1202
|
+
| `MANAGED_REFERENCE` | Allocation external/runtime binding, writer/publication lease, recorded holder, or verified holder descendant | Block |
|
|
1203
|
+
| `OBSERVED_REFERENCE` | Readable `cwd`/`exe`/`fd`/`maps` points into the target | Block |
|
|
1204
|
+
| `MANAGED_VISIBILITY_UNCERTAIN` | Recorded holder or positively associated lineage has inaccessible fields or unverifiable identity | `RUNTIME_SCAN_UNCERTAIN`, block |
|
|
1205
|
+
| `HOST_VISIBILITY_GAP` | `EACCES`/`EPERM` without any formal holder/lineage association to the allocation | Warning/evidence for authenticated typed resources |
|
|
1206
|
+
|
|
1207
|
+
AW Run and Native Session identity enters Controller through the existing external bindings or
|
|
1208
|
+
writer leases; these registrations remain blocking until formally removed. Opaque bindings block
|
|
1209
|
+
without needing a PID. A PID holder uses the existing boot ID/start ticks/session/run identity.
|
|
1210
|
+
Descendant proof follows numeric PPIDs, checks parent start ticks, and revalidates every identity
|
|
1211
|
+
and PPID edge. Reuse or changing lineage cannot become a host permission gap. No names, cgroups,
|
|
1212
|
+
provider names, process ages, command lines or environment variables grant an exclusion.
|
|
1213
|
+
|
|
1214
|
+
`plan.runtime_evidence` records governance identities, observed references, managed uncertainties
|
|
1215
|
+
and per-PID host gaps with exact field/errno. `plan.warnings` exposes the gap; an otherwise eligible
|
|
1216
|
+
nonempty plan reports `CLEANUP_READY_WITH_HOST_VISIBILITY_GAP`. Unknown ignored content, invalid
|
|
1217
|
+
resource identity, malformed proc entries, unresolved races and global scan errors still block.
|
|
1218
|
+
Cleanup receipts preserve the original plan, every resource's runtime preflight and the post-scan
|
|
1219
|
+
evidence immutably. Release after typed cleanup rechecks the same managed-runtime boundary.
|
|
1220
|
+
|
|
1221
|
+
This boundary does not prove that an unassociated host process has no references. The existing
|
|
1222
|
+
trusted-user/OS scope observes same-UID processes, plus any positively associated managed process
|
|
1223
|
+
across UIDs. Foreign UID exclusion alone is not a filesystem-access proof. Unregistered or reparented
|
|
1224
|
+
processes without retained governance lineage remain outside provable managed authority; readable
|
|
1225
|
+
positive references still block. This is a bounded observation, not an atomic host-wide snapshot or
|
|
1226
|
+
process isolation. A compliant launcher must retain its runtime registration until its workload is
|
|
1227
|
+
quiescent. The existing leases, bindings and repeated cleanup preflights remain necessary.
|
|
1228
|
+
|
|
1229
|
+
Internal symlinks may stay within the resource. The only external-link exception is
|
|
1230
|
+
the standard venv `bin/python`, `bin/python3`, and matching `bin/python3.x` interpreter aliases:
|
|
1231
|
+
their shape, common executable target, version/home declaration and executable identity are checked.
|
|
1232
|
+
Plans bind the worktree, parent and resource directory device/inode/type identities. Cleanup opens
|
|
1233
|
+
each directory component with `O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC`, verifies its identity, and
|
|
1234
|
+
matches a fresh FD inventory against the planned tree fingerprint. Recursive deletion uses only
|
|
1235
|
+
relative `unlink`/`rmdir` with verified directory FDs. The directory chain and entry identities are
|
|
1236
|
+
rechecked around deletion; renamed, missing or substituted entries retain the blocking journal.
|
|
1237
|
+
Symlink aliases are pinned without following them and unlinked without modifying their targets.
|
|
1238
|
+
|
|
1239
|
+
Run `resource-clean --typed --expected-plan-id <dry-run-plan-id>` with the same `--repo`, `--path`,
|
|
1240
|
+
`--owner` and `--expected-record-version`. Execution rechecks the plan, journals before deletion,
|
|
1241
|
+
rechecks before each resource, rescans, validates release eligibility, and writes a new cleanup
|
|
1242
|
+
receipt. It never releases the allocation. Then use the ordinary `release --dry-run` and `release`
|
|
1243
|
+
commands. Release following typed cleanup also requires managed-runtime clearance. Cleanup failure
|
|
1244
|
+
or interruption retains a blocking journal for explicit recovery; it is not automatically retried
|
|
1245
|
+
through partial deletion. Historical receipts and handoff identities are never rewritten.
|
|
1246
|
+
|
|
1247
|
+
The existing typed runtime binding covers agent, test, preview, background and command
|
|
1248
|
+
occupancy with allocation/worktree, boot/PID/start ticks and run/session identity.
|
|
1249
|
+
Execution exit does not require closing a human terminal that provably predates all
|
|
1250
|
+
declared holders: kernel creation order excludes that process from their descendants.
|
|
1251
|
+
Unknown or newer observed processes and managed uncertainties still block execution
|
|
1252
|
+
clearance. Allocation cleanup continues to reject every observed reference, including
|
|
1253
|
+
that preexisting terminal. No second use-lease schema or supervisor is introduced.
|
|
1254
|
+
|
|
1255
|
+
`.venv` is not a prerequisite for a plan. It is one supported disposable resource and must be
|
|
1256
|
+
registered before handoff with path, type, reproducibility, creator, and `manual` or `on-release`
|
|
1257
|
+
teardown policy. The controller never guesses that an ignored directory is disposable. Unknown
|
|
1258
|
+
ignored content stays fail-closed. The legacy `--teardown-reproducible-venv` flag remains an explicit
|
|
1259
|
+
compatibility assertion; registered `on-release` resources are already represented in the plan.
|
|
1260
|
+
|
|
1261
|
+
The preflight requires a clean, non-active, releasable checkout; matching task, handoff, Finalizer,
|
|
1262
|
+
and HEAD commits; the recorded owner and authority; tracked `uv.lock` and `pyproject.toml`; regular
|
|
1263
|
+
`pyvenv.cfg` and standard `CACHEDIR.TAG`; an executable Python identity; no external binding; no
|
|
1264
|
+
other managed environment referring to the checkout; and no same-UID `cwd`, executable, file
|
|
1265
|
+
descriptor, or memory mapping into the checkout that is visible through Linux procfs. Procfs may
|
|
1266
|
+
hide non-dumpable same-UID system services; those remain inside the documented trusted-user/OS
|
|
1267
|
+
boundary and must be represented by Controller external bindings when they own repository runtime.
|
|
1268
|
+
|
|
1269
|
+
Receipts under Controller metadata bind the worktree, HEAD, owner/authority, lock and project
|
|
1270
|
+
checksums, Python/environment identity, pre-removal byte/entry counts, and final result. A crash
|
|
1271
|
+
before Git worktree removal leaves a fail-closed journal; rerunning the identical command with the
|
|
1272
|
+
same CAS identity resumes from the receipt. A started removal may resume across partial deletion
|
|
1273
|
+
only while its recorded root inode and authority still match; path or root-identity drift remains
|
|
1274
|
+
blocked for review.
|
|
1275
|
+
|
|
1276
|
+
If teardown already reached `VENV_REMOVED` or `PREPARED`, the identical `release --dry-run` returns
|
|
1277
|
+
`RELEASE_RECOVERY_PLAN_READY` only after revalidating the persisted plan, journal, receipt, exact
|
|
1278
|
+
HEAD and record version, retained branch, recovery lineage, worktree content, and all runtime,
|
|
1279
|
+
editable, ignored-content, and external bindings. Execution then continues from checkout removal;
|
|
1280
|
+
it does not recreate or reclassify the removed `.venv`. Missing or altered transaction evidence and
|
|
1281
|
+
an unjournaled missing resource remain blocked.
|
|
1282
|
+
|
|
1283
|
+
## Legacy preserved state
|
|
1284
|
+
|
|
1285
|
+
From 1.18.8 (`no_change_cleanup_closeout_version` 1), a genuine `complete-no-change` also freezes
|
|
1286
|
+
its original record and the exact successful cleanup receipt set. Its write-once completion
|
|
1287
|
+
receipt is pinned by raw SHA256 in the normal +1 history transition. Release derives the full
|
|
1288
|
+
original record before rechecking cleanup at its unchanged exact CAS, including every action,
|
|
1289
|
+
preserved generation and absent source. Completion does not repeat cleanup or authorize runtime
|
|
1290
|
+
or resource guard exceptions.
|
|
1291
|
+
|
|
1292
|
+
An older no-change completion created after successful typed cleanup may be blocked because the
|
|
1293
|
+
old completion advanced the record without freezing this relationship. Recover that exact task
|
|
1294
|
+
through the explicit append-only entrance below, using observed trusted receipt IDs and raw
|
|
1295
|
+
digests. A newly observed digest is a new baseline, not proof of an earlier observation. The
|
|
1296
|
+
producer requires one unique legacy completion, the entire successful cleanup set, original
|
|
1297
|
+
allocation/owner/HEAD/branch and exact +1 transition, clean resources, no leases/bindings/runtime
|
|
1298
|
+
references or conflicting transaction. Missing, duplicate, changed or ambiguous evidence blocks.
|
|
1299
|
+
|
|
1300
|
+
```bash
|
|
1301
|
+
worktree-controller reconcile no-change-completion --repo /absolute/canonical-repo \
|
|
1302
|
+
--path /absolute/source-worktree --owner <recorded-owner> --expected-record-version <version> \
|
|
1303
|
+
--completion-receipt-id <uuid> --completion-receipt-sha256 <trusted-raw-sha256> \
|
|
1304
|
+
--cleanup-receipt-id <uuid> --cleanup-receipt-sha256 <trusted-raw-sha256> --dry-run --json
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
Repeat the same command without `--dry-run` and with `--expected-plan-id <reviewed-plan-id>`.
|
|
1308
|
+
Additional cleanup IDs/digests use paired repeatable flags. Formal recovery writes a new receipt
|
|
1309
|
+
and advances only the normal record/history CAS; the old receipts, refs and resources remain
|
|
1310
|
+
unchanged. While the checkout remains registered, an identical original request returns the same
|
|
1311
|
+
pinned receipt without another transition. Physical release remains a separate guarded command;
|
|
1312
|
+
after release, use its normal replay or exact `lifecycle-history` receipt query.
|
|
1313
|
+
|
|
1314
|
+
`python -m worktree_controller.preservation` implements `legacy-preserved-state/v1`.
|
|
1315
|
+
The directory example below uses the original root `.venv/agent-workspace` legacy state shape.
|
|
1316
|
+
Capabilities `retained_typed_cleanup_version` 1 and `preservation_leaf_version` 1 also support
|
|
1317
|
+
clean `RETAINED` feature/audit allocations and an exact `--source` selecting a Git-ignored,
|
|
1318
|
+
untracked directory, regular file or literal symlink leaf. This is a separate preservation
|
|
1319
|
+
entry point, not a disposable resource type. Preservation does not mutate allocations, bindings,
|
|
1320
|
+
handoffs, historical receipts or Controller transactions. Cleanup requires the separate exact
|
|
1321
|
+
receipt contract below and does not itself release an allocation.
|
|
1322
|
+
|
|
1323
|
+
The command binds an eligible clean allocation's repository/owner/authority, record
|
|
1324
|
+
version, HEAD, worktree/source node identities, and a fresh complete source manifest.
|
|
1325
|
+
It uses the existing allocation runtime guards: managed and observed references or uncertain
|
|
1326
|
+
managed visibility block; host permission gaps are recorded without claiming host-wide absence
|
|
1327
|
+
of references. Inspection FDs are closed before runtime scans; there is no self-PID exclusion.
|
|
1328
|
+
Per-resource classification blockers are recorded as `cleanup_not_ready` during preservation
|
|
1329
|
+
of the selected source; allocation, runtime, binding and editable-reference guards still block
|
|
1330
|
+
unsafe preservation. Copy and verify do not authorize cleanup, including nested typed resources.
|
|
1331
|
+
|
|
1332
|
+
Directory ancestors and tree directories are traversed with directory FDs and
|
|
1333
|
+
`O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC` at every directory component. Regular files are pinned
|
|
1334
|
+
with `O_PATH | O_NOFOLLOW`, confirmed regular by
|
|
1335
|
+
`fstat`, then read through the kernel's `/proc/self/fd` descriptor link. This prevents a changed
|
|
1336
|
+
source pathname from opening a symlink target or a FIFO endpoint. Provider/private files,
|
|
1337
|
+
including DB/WAL/SHM, and unknown files are copied and hashed only as opaque bytes; their
|
|
1338
|
+
contents are never parsed or printed. Unreadable files and unstable entry identities block.
|
|
1339
|
+
|
|
1340
|
+
Canonical, sorted JSON manifest rows use distinct node types:
|
|
1341
|
+
|
|
1342
|
+
| Node | Tree digest input, in addition to relative path/type/mode |
|
|
1343
|
+
| --- | --- |
|
|
1344
|
+
| Regular file | Size and SHA-256 of every byte |
|
|
1345
|
+
| Directory | No content payload; empty directories are included |
|
|
1346
|
+
| Symlink | Literal link target, including broken or external links; never followed |
|
|
1347
|
+
| FIFO | No content digest or payload; reconstructed with fd-relative `mkfifo` and exact mode |
|
|
1348
|
+
|
|
1349
|
+
UID/GID are recorded for every source node, but never restored with `chown`. Device/inode,
|
|
1350
|
+
mode/size, mtime/ctime, UID/GID and link count form the separate source race identity. Inodes
|
|
1351
|
+
and ownership are excluded from source/destination tree equality; directory sizes and timestamps
|
|
1352
|
+
are not portable tree equality fields. FIFO is never opened for I/O, read, waited on, or converted
|
|
1353
|
+
to a regular file. Its creation temporarily clears and immediately restores this single-threaded
|
|
1354
|
+
command's process umask. Sockets, devices and other node types remain blocked.
|
|
1355
|
+
|
|
1356
|
+
Generations live under the formal private state root
|
|
1357
|
+
`~/.local/state/worktree-controller/preservations/v1/<repository-id>/<allocation-id>/<uuid>/`,
|
|
1358
|
+
outside all repository worktrees. Plans pin existing destination ancestors. Creation is exclusive,
|
|
1359
|
+
all writes use verified directory FDs, and destination identity races or collisions fail closed.
|
|
1360
|
+
Failures retain an incomplete generation without a successful receipt; retry requires a new plan
|
|
1361
|
+
and UUID, never overwriting an earlier generation.
|
|
1362
|
+
|
|
1363
|
+
```bash
|
|
1364
|
+
python -m worktree_controller.preservation plan \
|
|
1365
|
+
--repo /absolute/canonical-repo --path /absolute/source-worktree \
|
|
1366
|
+
--allocation-id <uuid> --owner <recorded-owner> --expected-record-version <version> \
|
|
1367
|
+
--source .venv --plan-file /absolute/task-owned-directory/preservation-plan.json
|
|
1368
|
+
python -m worktree_controller.preservation copy --plan-file /absolute/task-owned-directory/preservation-plan.json
|
|
1369
|
+
python -m worktree_controller.preservation verify --plan-file /absolute/generation/plan.json
|
|
1370
|
+
```
|
|
1371
|
+
|
|
1372
|
+
Planning hashes the entire current source and reports counts, bytes, unreadable/special entries,
|
|
1373
|
+
runtime evidence and the proposed generation. It creates only the explicitly requested, exclusive
|
|
1374
|
+
plan file, not the generation or a source modification. Copy rechecks the plan and every source
|
|
1375
|
+
entry, copies the complete tree, verifies destination contents, then rescans source contents and
|
|
1376
|
+
inode identities. Reverification detects missing/changed destination files, changed FIFO modes,
|
|
1377
|
+
wrong receipts and any source change since planning.
|
|
1378
|
+
|
|
1379
|
+
A successful generation contains `tree` (a directory, regular file or literal symlink matching
|
|
1380
|
+
the selected source), `plan.json`, `manifest.json` and `receipt.json`.
|
|
1381
|
+
The receipt binds source/allocation/generation identities, manifest and equal tree hashes, node
|
|
1382
|
+
counts and bytes, source verification time, runtime warnings and implementation digest. Evidence
|
|
1383
|
+
files are write-once `0400`; the generation is finalized `0500`. These permissions and hashes
|
|
1384
|
+
provide the existing trusted-user/OS evidence boundary, not protection from hostile same-UID
|
|
1385
|
+
modification. Later source or destination drift invalidates current coverage. There is no atomic
|
|
1386
|
+
snapshot of unregistered host writers and no claim that host permission gaps prove absence.
|
|
1387
|
+
`LEGACY_PRESERVATION_COMPLETE` means the recorded preservation checks passed.
|
|
1388
|
+
|
|
1389
|
+
### Exact receipt cleanup
|
|
1390
|
+
|
|
1391
|
+
`legacy-preserved-state-cleanup/v1` requires the exact source already covered by a complete
|
|
1392
|
+
`legacy-preserved-state/v1` generation; the example below uses the root `.venv`. Supply the
|
|
1393
|
+
canonical receipt path and previously recorded SHA-256 explicitly; the Controller does not discover receipts by directory name or trust a newly
|
|
1394
|
+
computed hash as proof of an earlier receipt. The ordinary disposable types stay unchanged.
|
|
1395
|
+
|
|
1396
|
+
```bash
|
|
1397
|
+
worktree-controller resource-clean --typed --dry-run \
|
|
1398
|
+
--repo /absolute/canonical-repo --path /absolute/source-worktree \
|
|
1399
|
+
--owner <recorded-owner> --expected-record-version <version> \
|
|
1400
|
+
--preservation-receipt /absolute/generation/receipt.json \
|
|
1401
|
+
--preservation-receipt-sha256 <trusted-receipt-sha256> --json
|
|
1402
|
+
```
|
|
1403
|
+
|
|
1404
|
+
Capability `multiple_preservation_cleanup_version` 1 (CLI contract 40) also accepts paired,
|
|
1405
|
+
repeatable `--additional-preservation-receipt` and `--additional-preservation-receipt-sha256`
|
|
1406
|
+
options after the primary pair. Every receipt must cover the same exact allocation identity and
|
|
1407
|
+
one distinct, non-overlapping classified source path. Missing pairs, duplicate receipts, overlap
|
|
1408
|
+
or any invalid source blocks the entire cleanup. The normalized proof set is bound to the plan
|
|
1409
|
+
ID and immutable journal/receipt under `legacy-preserved-state-multiple-cleanup/v1`; single-input
|
|
1410
|
+
plans retain their existing contract and shape. All resources and global guards remain mandatory.
|
|
1411
|
+
Every action verifies its own source against the full recorded hardlink topology. Only a source
|
|
1412
|
+
with a verified `COMPLETED` action may use destination-only verification, and it must remain absent.
|
|
1413
|
+
Release re-verifies every preserved generation and the complete proof/resource/action relationship.
|
|
1414
|
+
|
|
1415
|
+
Capability `multiple_preservation_recovery_version` 1 adds one bounded recovery path: a nonempty
|
|
1416
|
+
verified `COMPLETED` prefix followed only by never-started `PENDING` actions. Supply the original
|
|
1417
|
+
transaction ID and its previously recorded journal and failed-receipt SHA-256 values:
|
|
1418
|
+
|
|
1419
|
+
```bash
|
|
1420
|
+
worktree-controller resource-recover --stage prepare-multiple --dry-run \
|
|
1421
|
+
--repo /absolute/canonical-repo --path /absolute/source-worktree \
|
|
1422
|
+
--owner <recorded-owner> --expected-record-version <version> \
|
|
1423
|
+
--transaction-id <original-transaction-id> \
|
|
1424
|
+
--transaction-sha256 <trusted-journal-sha256> \
|
|
1425
|
+
--cleanup-receipt-sha256 <trusted-failed-receipt-sha256> --json
|
|
1426
|
+
```
|
|
1427
|
+
|
|
1428
|
+
After reviewing the dry-run, repeat it without `--dry-run` and with the exact fresh
|
|
1429
|
+
`--expected-plan-id`. Formal preparation writes an immutable
|
|
1430
|
+
`multiple-preservation-cleanup-recovery/v1` record and returns its path and SHA-256. It retains
|
|
1431
|
+
the full original proof set, verifies every completed generation's official metadata, and freezes
|
|
1432
|
+
those metadata paths and hashes together with the allocation owner, CAS and history. Resume
|
|
1433
|
+
uses that exact new reference:
|
|
1434
|
+
|
|
1435
|
+
```bash
|
|
1436
|
+
worktree-controller resource-recover --stage resume --dry-run \
|
|
1437
|
+
--repo /absolute/canonical-repo \
|
|
1438
|
+
--recovery-receipt /absolute/recovery/record.json \
|
|
1439
|
+
--recovery-receipt-sha256 <trusted-recovery-record-sha256> --json
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
Run the same resume command without `--dry-run` only after a passing review. It uses the existing
|
|
1443
|
+
cleanup executor for the untouched suffix; it never replays the completed prefix or reduces the
|
|
1444
|
+
proof set. A started or partial recovery cannot replay deletion. If the successor cleanup already
|
|
1445
|
+
completed, resume may only finish its missing terminal evidence after verifying the unique exact
|
|
1446
|
+
successful receipt. The original journal, failed receipt and all generations remain unchanged.
|
|
1447
|
+
Every existing allocation, root, resource, topology, runtime, reference and transaction guard still
|
|
1448
|
+
applies. Historical terminal verification continues after an exact official release by checking
|
|
1449
|
+
the frozen authority, complete metadata set and release evidence; checkout absence is not proof.
|
|
1450
|
+
Unsupported interrupted shapes still block release and rollout. Legacy single-receipt recovery
|
|
1451
|
+
retains its existing flow, and no interrupted multi plan may be downgraded into it.
|
|
1452
|
+
|
|
1453
|
+
Capability `preservation_supersession_version` 1 recognizes an exact official frozen handoff,
|
|
1454
|
+
reopen and new handoff lineage. The checkout root and source parent/ancestor identities must
|
|
1455
|
+
remain unchanged. A rebuilt source leaf or directory inode is accepted only when the new exact
|
|
1456
|
+
verified proof and actual completed action cover that path under the same allocation and owner,
|
|
1457
|
+
with the recorded CAS/HEAD ancestry. Every older completed generation's official metadata remains
|
|
1458
|
+
verified and retained; neither a newer copy nor a missing source alone grants cleanup authority.
|
|
1459
|
+
|
|
1460
|
+
The plan revalidates receipt bytes, supported contract, allocation/HEAD/record/owner/authority,
|
|
1461
|
+
exact worktree/source and generation identities, manifest digest, PASS status, every preserved
|
|
1462
|
+
node and the complete current source tree and inode identities. Missing, changed or mismatched
|
|
1463
|
+
evidence blocks. Tracked/staged files and unknown ignored resources still block. Managed and
|
|
1464
|
+
observed references and managed visibility uncertainty still block; host visibility gaps remain
|
|
1465
|
+
explicit warnings and evidence, without claiming host-wide absence of references.
|
|
1466
|
+
Editable links inside the same verified legacy tree are preserved and removed with it; links
|
|
1467
|
+
from other managed environments remain release blockers.
|
|
1468
|
+
|
|
1469
|
+
An eligible plan reports `CLEANUP_READY_WITH_PRESERVATION`. Run the same command without
|
|
1470
|
+
`--dry-run`, adding `--expected-plan-id <fresh-plan-id>`, only after explicit cleanup authorization.
|
|
1471
|
+
In this root `.venv` example, the three typed resources are classified separately and removed
|
|
1472
|
+
first; exact-preserved `.venv` is last. Each action repeats the safety checks. Deletion uses the existing FD-bound primitive,
|
|
1473
|
+
validates directory and entry identities, unlinks symlinks themselves, and handles FIFOs only as
|
|
1474
|
+
nodes with `O_PATH` metadata pins. No FIFO endpoint or external symlink target is opened for I/O.
|
|
1475
|
+
The preservation generation is outside the deletion tree and is never deleted by cleanup.
|
|
1476
|
+
|
|
1477
|
+
The immutable cleanup receipt records the original plan, per-action progress, runtime warnings,
|
|
1478
|
+
and post-scan. Any failed/partial action retains its transaction and receipt, blocks allocation
|
|
1479
|
+
release, and requires review; there is no blind retry. Successful cleanup rescans Git, resources,
|
|
1480
|
+
runtime authority and release eligibility before clearing its transaction. A separate ordinary
|
|
1481
|
+
`release` still applies every existing gate and re-verifies the preserved destination from the
|
|
1482
|
+
cleanup receipt. Historical preservation and handoff receipts are never rewritten.
|
|
1483
|
+
|
|
1484
|
+
After cleanup or release, verification can still hash the preserved tree without requiring the
|
|
1485
|
+
removed source:
|
|
1486
|
+
|
|
1487
|
+
```bash
|
|
1488
|
+
python -m worktree_controller.preservation verify --plan-file /absolute/generation/plan.json \
|
|
1489
|
+
--destination-only --receipt-sha256 <trusted-receipt-sha256>
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
## Policy v2, v3, and v4
|
|
1493
|
+
|
|
1494
|
+
Policy v2 separates checkout preparation capacity from publication concurrency and reserves
|
|
1495
|
+
governance recovery independently from ordinary feature work:
|
|
1496
|
+
|
|
1497
|
+
```toml
|
|
1498
|
+
schema_version = 2
|
|
1499
|
+
|
|
1500
|
+
[limits]
|
|
1501
|
+
normal_limit = 6
|
|
1502
|
+
hard_limit = 8
|
|
1503
|
+
|
|
1504
|
+
[slots]
|
|
1505
|
+
integration_candidate_limit = 2
|
|
1506
|
+
feature_or_audit = 3
|
|
1507
|
+
emergency = 1
|
|
1508
|
+
recovery_reserve = 1
|
|
1509
|
+
|
|
1510
|
+
[publication]
|
|
1511
|
+
main_publish_limit = 1
|
|
1512
|
+
remote = "origin"
|
|
1513
|
+
branch = "main"
|
|
1514
|
+
```
|
|
1515
|
+
|
|
1516
|
+
The remaining `[canonical]`, `[review]`, and `[authority]` tables retain their v1 fields. Policy v1
|
|
1517
|
+
remains readable, but 1.1.0 uses resource admission in place of aggregate quantity limits.
|
|
1518
|
+
|
|
1519
|
+
Policy v3 retains every v2 capacity/publication field and adds only the automatic-release switch and
|
|
1520
|
+
short intent lifetime:
|
|
1521
|
+
|
|
1522
|
+
```toml
|
|
1523
|
+
schema_version = 3
|
|
1524
|
+
|
|
1525
|
+
[release]
|
|
1526
|
+
automatic_feature_audit = false
|
|
1527
|
+
intent_ttl_seconds = 3600
|
|
1528
|
+
```
|
|
1529
|
+
|
|
1530
|
+
When enabled, automatic release applies only to `feature`/`audit` allocations in `HANDOFF_READY`
|
|
1531
|
+
with exact clean committed handoff/Finalizer identity, released and remote-verified integration
|
|
1532
|
+
evidence (ancestry or explicit equivalent replay), no unintegrated result, no runtime/editable/
|
|
1533
|
+
external binding, classified disposable resources, a retained branch, passing official plan, and
|
|
1534
|
+
passing execution-time HEAD/record CAS. `PRESERVED`, canonical, emergency, and recovery allocations
|
|
1535
|
+
are excluded; integration candidates continue to use publication lease completion.
|
|
1536
|
+
|
|
1537
|
+
Policy v4 keeps total-capacity accounting unchanged, names preparation capacity explicitly, and
|
|
1538
|
+
adds the continuation boundary consumed by Agent Workspace:
|
|
1539
|
+
|
|
1540
|
+
```toml
|
|
1541
|
+
schema_version = 4
|
|
1542
|
+
|
|
1543
|
+
[slots]
|
|
1544
|
+
integration_prepare_limit = 3
|
|
1545
|
+
feature_or_audit = 11
|
|
1546
|
+
emergency = 1
|
|
1547
|
+
recovery_reserve = 1
|
|
1548
|
+
|
|
1549
|
+
[publication]
|
|
1550
|
+
main_publish_limit = 1
|
|
1551
|
+
remote = "origin"
|
|
1552
|
+
branch = "main"
|
|
1553
|
+
|
|
1554
|
+
[release]
|
|
1555
|
+
automatic_feature_audit = true
|
|
1556
|
+
automatic_integration_continuation = true
|
|
1557
|
+
preserved_auto_release = false
|
|
1558
|
+
intent_ttl_seconds = 3600
|
|
1559
|
+
```
|
|
1560
|
+
|
|
1561
|
+
Count observations no longer require the historical slot sums to equal `normal_limit` or
|
|
1562
|
+
`hard_limit`; their positive values and `normal_limit < hard_limit` remain validated.
|
|
1563
|
+
Integration preparation and the single main-publication lease remain enforced independently.
|
|
1564
|
+
`preserved_auto_release=true` is rejected. Schema v1-v3
|
|
1565
|
+
remain readable and do not enable automatic integration continuation.
|
|
1566
|
+
|
|
1567
|
+
## Integration and recovery gates
|
|
1568
|
+
|
|
1569
|
+
Repository-writing agents call `write-guard` at the task/write entry boundary after selecting an
|
|
1570
|
+
allocation. `feature-write` is never valid for an integration allocation. Same-task
|
|
1571
|
+
`integration-write`, explicit recovery, and finalization remain available under their lifecycle
|
|
1572
|
+
rules. A `HANDOFF_READY` integration whose HEAD no longer matches its evidence returns
|
|
1573
|
+
`BLOCK_RECOVERY_REQUIRED` before additional work. This is a trusted-agent workflow gate, not
|
|
1574
|
+
same-UID process isolation; `doctor`, handoff, lease, and release continue to revalidate durable
|
|
1575
|
+
state independently.
|
|
1576
|
+
|
|
1577
|
+
Feature handoff evidence binds a base commit, task commit, Finalizer commit, and repository-relative
|
|
1578
|
+
path scope. An integration allocation names one or more integration-ready sources by allocation ID,
|
|
1579
|
+
or resolves a retained handoff commit with `acquire --source-handoff <commit>`; its scope is exactly
|
|
1580
|
+
their union. `handoff`, `doctor`, and `lease-acquire` reject novel non-merge commits,
|
|
1581
|
+
out-of-scope paths, or task/handoff/Finalizer commits that no longer equal HEAD. These are delivery
|
|
1582
|
+
gates, not same-UID process isolation: direct Git writes can still occur, but cannot obtain Controller
|
|
1583
|
+
publication authority after semantic drift.
|
|
1584
|
+
|
|
1585
|
+
`HANDOFF_READY` is a logical write fence. `feature-write` and new active bindings are rejected until
|
|
1586
|
+
`reopen` moves the allocation back to `ACTIVE`; reopen clears old handoff/Finalizer identity and
|
|
1587
|
+
cancels pending intents. Out-of-band dirty, HEAD, record, or binding drift makes old intents/plans
|
|
1588
|
+
stale or blocked rather than granting cleanup authority.
|
|
1589
|
+
|
|
1590
|
+
Two explicit owner/CAS recovery commands cover published historical drift:
|
|
1591
|
+
|
|
1592
|
+
- `recover-handoff --path PATH --owner OWNER --expected-record-version N --expected-head OID`
|
|
1593
|
+
accepts an attached frozen source whose old handoff has an exact ancestry publication receipt
|
|
1594
|
+
and whose advanced HEAD is already on the configured publication ref. It preserves the entire
|
|
1595
|
+
old record and any proven dead writer lease in an immutable recovery preparation receipt,
|
|
1596
|
+
leaves modified/untracked files and the index untouched, and reopens at the observed base.
|
|
1597
|
+
The owner must then test, commit and record a new handoff before normal release.
|
|
1598
|
+
- `recover-published` takes the same identity arguments plus `--branch recovery/NAME`. It requires
|
|
1599
|
+
a clean detached `PUBLISHED` integration, an exact remote-verified receipt with its lease released,
|
|
1600
|
+
and ancestry on the publication ref. It creates a fresh retained branch at the same HEAD and
|
|
1601
|
+
attaches it without checking out files or running checkout hooks. Normal release remains separate.
|
|
1602
|
+
|
|
1603
|
+
Both reject active integration obligations, staged state, runtime references, live writers and
|
|
1604
|
+
uncertain visibility. Neither edits historical publication evidence or grants publication authority.
|
|
1605
|
+
Interrupted preparation can be retried with the same arguments; an existing branch is accepted
|
|
1606
|
+
only when the immutable preparation proves this exact creation attempt and its OID still matches.
|
|
1607
|
+
These receipts use the existing completion-receipts storage, without changing its layout.
|
|
1608
|
+
|
|
1609
|
+
An integration intent freezes task/authority, durable queue order, source allocation/branch,
|
|
1610
|
+
handoff/Finalizer/Snapshot, path-scope digest, base main, candidate attempt/OID, and exact prepared
|
|
1611
|
+
receipt. Queue entries consume no checkout. Non-overlapping intents may use up to the policy
|
|
1612
|
+
preparation limit; a later overlapping intent enters `WAITING_SCOPE`. An intent-bound integration
|
|
1613
|
+
checkout rejects feature writes. `READY_TO_PUBLISH` is write-fenced until explicit reopen;
|
|
1614
|
+
out-of-band HEAD, content, record, or binding drift makes old evidence unusable.
|
|
1615
|
+
|
|
1616
|
+
The bounded `integration-keyed-document-scope/v1` refinement applies only when the entire
|
|
1617
|
+
overlap is the exact `docs/handbook/business-progress.json` path. Every pair of frozen source
|
|
1618
|
+
commits must have one non-ancestor merge base and the same committed `.gitattributes` declaring
|
|
1619
|
+
`merge=qp-handbook-progress`. QP schema 2 records are compared by whole `tasks.authority_key`
|
|
1620
|
+
or `development_lines/modules/decisions.id`, including additions and deletions. All changed key
|
|
1621
|
+
sets must be disjoint. Metadata, schema, ordering, duplicate keys, missing/oversized/non-regular
|
|
1622
|
+
blobs, unknown evidence, broad paths and code overlaps keep the ordinary file fence. Explicit
|
|
1623
|
+
prerequisites and earlier publishing/recovery/attention states still block. The normal claim or
|
|
1624
|
+
reconciler can clear an existing `WAITING_SCOPE` without changing its frozen source or queue order.
|
|
1625
|
+
This proof grants preparation only; publishing still requires a fresh main base and normal validation.
|
|
1626
|
+
|
|
1627
|
+
At lease acquisition Controller compares live `origin/main` to the immutable prepared base. If main
|
|
1628
|
+
advanced, the candidate enters `NEEDS_REPREPARE`; `integration-refresh` then rebuilds from frozen
|
|
1629
|
+
handoffs without rewriting source branches and requires new tests/Snapshot evidence. A true semantic
|
|
1630
|
+
conflict enters `ATTENTION_REQUIRED`; other preparation slots and the publisher remain independent.
|
|
1631
|
+
|
|
1632
|
+
Capability `integration_retry_version` 1 supports an aborted, unpublished intent whose candidate
|
|
1633
|
+
has formally been released. Run `integration-retry --intent-id ID --owner OWNER --reason TEXT
|
|
1634
|
+
--dry-run`, then repeat the exact request with `--expected-plan-id PLAN_ID`. The plan pins frozen
|
|
1635
|
+
source/scope, original queue sequence, failure receipts and queue evidence. Retry increments the
|
|
1636
|
+
attempt and clears old candidate, preparation, tests, Snapshot and validation eligibility;
|
|
1637
|
+
`integration-claim`, validation and publication remain separate mandatory steps. Default retry
|
|
1638
|
+
branches have an attempt suffix, so the original failed branch and receipts remain recoverable.
|
|
1639
|
+
|
|
1640
|
+
Capability `integration_conflict_resolution_version` 1 also permits the same retry plan/CAS for an
|
|
1641
|
+
exact `INTEGRATION_CONFLICT_BLOCKED` failure that never allocated a candidate. It requires no invented
|
|
1642
|
+
abort or release receipt: the original failure event/base, frozen sources and queue evidence are
|
|
1643
|
+
retained. Unknown missing-allocation shapes still fail closed.
|
|
1644
|
+
|
|
1645
|
+
After retry, `integration-claim --intent-id ID --owner OWNER --resolve-conflicts` may claim an actual
|
|
1646
|
+
single-source conflict at the exact fetched main base. Normal scope order and resource gates apply;
|
|
1647
|
+
the decision names `pending_merge_source`. This is an unfinished candidate, never publication proof.
|
|
1648
|
+
Within that governed checkout, merge that exact source, explicitly resolve only the frozen scope,
|
|
1649
|
+
and commit through Git Finalizer's resolved-merge mode. Then run `integration-resolve --path PATH
|
|
1650
|
+
--expected-record-version N --owner OWNER --expected-head FULL_OID --finalizer-record-id RECEIPT`.
|
|
1651
|
+
It checks the unchanged source identity, clean attached HEAD, exact two parents (pinned main, frozen
|
|
1652
|
+
source), and normal commit/path scope before binding the new candidate revision. It does not merge,
|
|
1653
|
+
commit or choose conflict contents. Fresh tests, Snapshot, `integration-validate`, publication lease,
|
|
1654
|
+
remote verification and guarded release remain required. Default claims still refuse conflicts;
|
|
1655
|
+
multiple-source conflicts, arbitrary candidate replacements and scope widening are unsupported.
|
|
1656
|
+
|
|
1657
|
+
When a necessary dependency is already an ancestor of the frozen source but is outside its scope,
|
|
1658
|
+
enqueue its own formally evidenced handoff and use the same plan/apply entry with
|
|
1659
|
+
`--prerequisite-intent CHILD_ID`. The child must be unclaimed, later in the queue, owned by the same
|
|
1660
|
+
integration owner and contain only strict ancestors of the parent source. One append-only parent
|
|
1661
|
+
decision gives it the parent's scheduling position before later overlapping waits; earlier
|
|
1662
|
+
unrelated intents still fence it. Both stored queue sequences, source identities and scopes stay
|
|
1663
|
+
unchanged. The parent stays failed until the prerequisite is formally published, released and
|
|
1664
|
+
absorbed into main, then retry it without `--prerequisite-intent`. This entry never broadens scope,
|
|
1665
|
+
republishes an existing result or resolves a merge conflict by editing another owner's files.
|
|
1666
|
+
|
|
1667
|
+
`lease-acquire --run-id ...` binds repository ID, target ref, holder/run, expected main OID, exact
|
|
1668
|
+
candidate OID, validation evidence, and expiry. Git Finalizer consumes that same lease with
|
|
1669
|
+
`--publish-integration-candidate`; both it and Controller serialize on the existing repository lock.
|
|
1670
|
+
An expired or replaced executor cannot enter the mutation gate. On restart, Finalizer reads live
|
|
1671
|
+
remote first: exact candidate means receipt recovery without another push; unchanged expected main
|
|
1672
|
+
means re-entering the fenced gate.
|
|
1673
|
+
|
|
1674
|
+
`lease-complete --run-id ...` live-verifies the candidate, persists an immutable publication receipt
|
|
1675
|
+
and lease duration, releases the lease, then by default calls the unified guarded release. Once the
|
|
1676
|
+
integration intent is both remote verified and `RELEASED`, Controller creates source `ReleaseIntentV1`
|
|
1677
|
+
records and reconciles them under policy. Repository-control, Finalizer, and Agent Workspace provide
|
|
1678
|
+
events and bindings; only Controller removes worktrees. Historical receipt or stale-intent drift is
|
|
1679
|
+
represented by a new immutable reconciliation receipt: safe historical drift is `DEGRADED`, while
|
|
1680
|
+
identity ambiguity remains a blocking doctor failure.
|
|
1681
|
+
|
|
1682
|
+
With capability `source_release_summary_version` 1, `source_release_trigger.outcomes` reports each
|
|
1683
|
+
source separately while preserving `created_intent_ids`, `skipped`, and `reconciliation`.
|
|
1684
|
+
An already closed source reports `status: RELEASED`, `result: ALREADY_RELEASED`, and its immutable
|
|
1685
|
+
`release_receipt_id` only when unique, complete `lifecycle-history` evidence matches the frozen
|
|
1686
|
+
source branch, handoff, Finalizer identity, and publication association, with no live binding.
|
|
1687
|
+
Retiring its branch afterward does not invalidate that historical closure. Missing or conflicting
|
|
1688
|
+
proof remains blocked with a diagnostic and `recovery_commands`; candidate cleanup still pending
|
|
1689
|
+
reports `WAITING`, and live source reconciliation retains its original manual, retryable, or stale
|
|
1690
|
+
result. These observations do not create release authority or prove business completion. Older
|
|
1691
|
+
runtimes require querying `lifecycle-history` separately; path absence alone never proves closure.
|
|
1692
|
+
|
|
1693
|
+
Recovery is explicit: `quarantine` records the last legitimate commit and observed polluted HEAD;
|
|
1694
|
+
an `acquire --role recovery --source-allocation ... --scope ...` checkout starts at that observed
|
|
1695
|
+
HEAD and explicitly scopes the recovered feature rather than inheriting the integration scope;
|
|
1696
|
+
`externalize` binds its retained branch as lineage evidence. Only then may the polluted checkout be
|
|
1697
|
+
released. Both the original branch and recovery branch remain.
|
|
1698
|
+
|
|
1699
|
+
## Lifecycle dogfood evidence
|
|
1700
|
+
|
|
1701
|
+
A local lifecycle dogfood covers `plan`, `acquire`, owner review, a Finalizer-linked commit,
|
|
1702
|
+
`integration-ready` handoff, local control-plane integration, explicit owner release, and guarded
|
|
1703
|
+
checkout release. It must retain the feature branch and finish with a clean canonical checkout and
|
|
1704
|
+
passing `doctor` result. Local-only evidence does not prove remote publication or justify mandatory
|
|
1705
|
+
Controller enforcement; those require a separate publication-linked lifecycle run.
|
|
1706
|
+
|
|
1707
|
+
A publication-linked lifecycle acceptance additionally requires non-force control-plane
|
|
1708
|
+
integration, Git Finalizer publication with live remote verification, and release guards evaluated
|
|
1709
|
+
against the published main lineage. A successful acceptance removes only the linked checkout,
|
|
1710
|
+
retains its branch as a recoverable ref, and ends with a clean canonical checkout and passing
|
|
1711
|
+
`doctor` result.
|
|
1712
|
+
|
|
1713
|
+
## Development
|
|
1714
|
+
|
|
1715
|
+
### Explicit writer session contract (1.0.0)
|
|
1716
|
+
|
|
1717
|
+
The single `worktree-session` entry point accepts any executable. Its client identity is
|
|
1718
|
+
an opaque identifier, with no provider list, private session reader or adapter family:
|
|
1719
|
+
|
|
1720
|
+
```bash
|
|
1721
|
+
worktree-session --path /absolute/worktree --client-id terminal \
|
|
1722
|
+
--session-id task-session --run-id task-run -- bash
|
|
1723
|
+
worktree-controller writer-session acquire my-client /absolute/worktree 12345 \
|
|
1724
|
+
--session-id task-session --run-id task-run
|
|
1725
|
+
worktree-controller writer-session check my-client /absolute/worktree 12345 \
|
|
1726
|
+
--session-id task-session --run-id task-run
|
|
1727
|
+
```
|
|
1728
|
+
|
|
1729
|
+
Admission is `GRANTED`, `NOT_APPLICABLE`, or `REFUSED`. Applicability follows the declared
|
|
1730
|
+
role. A broken policy, corrupt metadata, a conflicting lease or a nested repository cannot
|
|
1731
|
+
turn a governed path into an ungoverned one. Any acquisition/check failure stops execution.
|
|
1732
|
+
The holder retains its exact Linux boot ID, PID and start ticks, plus the explicit client,
|
|
1733
|
+
session and run identities. The resolver also accepts an explicit `ancestor:<program>`;
|
|
1734
|
+
it never infers task identity from command arguments or an executor's session files.
|
|
1735
|
+
|
|
1736
|
+
The launcher acquires and checks once before starting its child. It exports the exact lease
|
|
1737
|
+
through `CONTROLLER_WRITER_MODE=host-managed-v2` and `CONTROLLER_WRITER_CONTEXT`. A host may
|
|
1738
|
+
supply the same context directly. The context contains `holder` (PID, boot ID, start ticks),
|
|
1739
|
+
`client_id`, `session_id`, explicit nullable `run_id`, `repository_path`, `worktree_path`,
|
|
1740
|
+
`allocation_id`, and `lease_id`. `writer-session host-check <actual-caller-path>` is read-only
|
|
1741
|
+
and requires the current allocation, lease, holder, lifecycle and ordinary write-guard to
|
|
1742
|
+
agree. Missing, duplicate, unknown or mismatched fields refuse the operation. Hosted child
|
|
1743
|
+
commands never acquire, release or reconcile their parent's allocation.
|
|
1744
|
+
|
|
1745
|
+
A per-tool host gate passes one `--access` class: `read`, `exec`, or `write` with exactly one
|
|
1746
|
+
absolute `--target`. While the allocation is `ACTIVE` the answer is the unchanged `GRANTED`.
|
|
1747
|
+
After this holder's own handoff (`HANDOFF_READY` or `PRESERVED`), the same holder receives
|
|
1748
|
+
`CONTINUATION_GRANTED` for reads, commands, writes outside governance, and writes into an
|
|
1749
|
+
`ACTIVE` integration candidate whose `source_allocations` include the frozen allocation and whose
|
|
1750
|
+
`integration-write` guard allows them. This is what lets a session that handed off its checkout
|
|
1751
|
+
continue with `integration-enqueue`, `integration-claim`, candidate work, `lease-acquire`,
|
|
1752
|
+
Finalizer publication, `lease-complete` and session end. Writes into the frozen checkout (symlinks
|
|
1753
|
+
resolved), other governed checkouts or repositories, and checks without `--access` keep the
|
|
1754
|
+
original refusal; commands keep their own Controller guards, which still detect drift. A missing
|
|
1755
|
+
or replaced lease, a foreign holder or runtime binding, HEAD drift, an unknown class or a relative
|
|
1756
|
+
target refuse every class. The check never mutates leases, bindings or lifecycle.
|
|
1757
|
+
|
|
1758
|
+
On ordinary child completion, the launcher preserves the command's exit code, releases only
|
|
1759
|
+
its exact lease, and invokes the existing guarded terminal reconciliation. A command that
|
|
1760
|
+
cannot start releases its new lease and leaves the task ACTIVE. Cleanup remains advisory;
|
|
1761
|
+
it never invents task completion, removes another holder or relaxes release evidence.
|
|
1762
|
+
|
|
1763
|
+
Public CLI contract 30, Writer Session 2, writer lease schema 2 and host context version 2
|
|
1764
|
+
replace the former executable contracts; branch retirement publishes contract
|
|
1765
|
+
`branch-retirement/v5` with execution authorization `branch-retirement-execution/v1` on plan schema 1. The package exposes `worktree-controller`,
|
|
1766
|
+
`worktree-session`, and `worktree-session-end` only. Historical releases remain unchanged.
|
|
1767
|
+
|
|
1768
|
+
### Governed deployment and rollback
|
|
1769
|
+
|
|
1770
|
+
An archived Codex thread can leave a stale agent binding on a shared, still-live
|
|
1771
|
+
app-server after its writer lease was released. Ordinary `binding-remove` still
|
|
1772
|
+
requires the holder to exit. The explicit `--recover-archived-codex-session`
|
|
1773
|
+
option instead verifies the original daemon through its same-user Unix socket,
|
|
1774
|
+
the exact archived rollout/session identity, native `notLoaded` status and loaded
|
|
1775
|
+
thread inventory, and absence of checkout references. It refuses active leases,
|
|
1776
|
+
active/unarchived threads, different peers, identity drift and uncertain scans.
|
|
1777
|
+
Use `--dry-run` first, then the same owner/binding/record CAS without dry-run.
|
|
1778
|
+
The immutable `native-session-recoveries` receipt supports interrupted completion
|
|
1779
|
+
and exact idempotent readback. This action never archives a thread, terminates a
|
|
1780
|
+
daemon, changes source/lifecycle/owner, or weakens the ordinary live-holder gate.
|
|
1781
|
+
Archiving the original thread is a separate authorized native operation; other
|
|
1782
|
+
loaded threads remain running. Native status/transport unavailability fails closed.
|
|
1783
|
+
|
|
1784
|
+
`scripts/controller-activate stage --version <commit>` installs a frozen, commit-pinned
|
|
1785
|
+
runtime beside the active one. Staging leaves live entry points unchanged. Activation and
|
|
1786
|
+
rollback both verify source bytes and the installation receipt, then run the frozen
|
|
1787
|
+
`scripts/check-writer-rollout` gate. Declare every external host using repeatable
|
|
1788
|
+
`--consumer /absolute/capability-command`; every host reports the same
|
|
1789
|
+
`--writer-session-capabilities` contract. There is no built-in application prerequisite.
|
|
1790
|
+
|
|
1791
|
+
For a first pinned installation, initialize the declared repositories with the staged Controller
|
|
1792
|
+
and deploy `scripts/controller-runtime` as the executable `~/bin/controller-runtime` and the three
|
|
1793
|
+
`~/.local/bin/{worktree-controller,worktree-session,worktree-session-end}` entry points. Then run
|
|
1794
|
+
`scripts/controller-activate bootstrap --version <full-commit-oid>` with the same governed repository
|
|
1795
|
+
and consumer declarations. Bootstrap verifies the staged package and runs the same frozen rollout
|
|
1796
|
+
gate, then creates the active pointer only if it is still absent under the activation lock. Existing
|
|
1797
|
+
or dangling pointers require normal activation/recovery. A failed read-only smoke removes only its
|
|
1798
|
+
own new pointer; concurrent pointer drift blocks restoration. Ordinary upgrades keep the verified
|
|
1799
|
+
previous-runtime and ancestry gates.
|
|
1800
|
+
|
|
1801
|
+
Sites using `tool-skill-sync` declare the reviewed owner catalog with
|
|
1802
|
+
`--toolchain-compatibility /absolute/canonical-owner/toolchain_compatibility.json`, or
|
|
1803
|
+
`CONTROLLER_ROLLOUT_TOOLCHAIN_COMPATIBILITY`. Deliver the catalog registration to its owner main
|
|
1804
|
+
before staging or activating a newly advertised CLI, capability or storage version. The deployment
|
|
1805
|
+
entry checks catalog schema 1 / toolchain contract 6 and the target's independent version bindings.
|
|
1806
|
+
The catalog must match its tracked bytes on the owner's canonical `main` or `master`, whose HEAD
|
|
1807
|
+
must equal live `origin`; dirty catalogs, candidate branches, missing/unreachable sources and
|
|
1808
|
+
unreviewed versions refuse acceptance before any runtime switch. Other owner files are not written.
|
|
1809
|
+
The check runs for staged acceptance, before rollout, and again under the switch lock, including
|
|
1810
|
+
bootstrap, rollback and automatic restoration. A changed catalog is rechecked rather than granting
|
|
1811
|
+
authority from a previous observation. Logs identify the owner commit and catalog SHA256; failures
|
|
1812
|
+
are permanent deployment refusals, not temporary rollout retry authority. Sites without a cross-tool
|
|
1813
|
+
workflow retain the independent deployment path when no catalog is declared. After activation,
|
|
1814
|
+
run the default installed `tool-skill-sync doctor --repo <actual-checkout> --summary`.
|
|
1815
|
+
|
|
1816
|
+
Forward activation requires a commit descended from the active runtime and a newer software
|
|
1817
|
+
version. The frozen public
|
|
1818
|
+
contract, installed package and `capabilities --json` must agree on the software, CLI,
|
|
1819
|
+
capability-set and storage-layout versions independently. Every previously advertised
|
|
1820
|
+
versioned capability must remain present without regression; rollback keeps its separate
|
|
1821
|
+
compatibility gate and accepts only an ancestor of the active runtime. The local v1.10.0
|
|
1822
|
+
tag is not an activation target from 1.9.4 because its contract omits the already active
|
|
1823
|
+
`archive-retained` capability.
|
|
1824
|
+
|
|
1825
|
+
The gate requires the declared repositories' governance state, pinned neutral entry points,
|
|
1826
|
+
a runnable staged version, compatible session identities, and quiescent publication and
|
|
1827
|
+
mutation state. Incompatible hosts and live sessions whose transition cannot be proved safe
|
|
1828
|
+
block activation. The exact re-preservation recovery exception below remains supported.
|
|
1829
|
+
|
|
1830
|
+
If the repository mutation lock is busy, the gate defers transaction inspection and reports only
|
|
1831
|
+
the temporary mutation blocker for that repository. A live command's intermediate journal is not
|
|
1832
|
+
classified as interrupted recovery evidence. The next complete gate still verifies the lock,
|
|
1833
|
+
pending transactions and publication lease before granting readiness.
|
|
1834
|
+
|
|
1835
|
+
From 1.24.6, completed feature/audit acquisition evidence also survives ordinary commits in an
|
|
1836
|
+
ACTIVE or RETAINED task. The staged check requires its original acquisition history, unchanged
|
|
1837
|
+
task/base identity, exact Git registration and allocation binding, an attached branch and base
|
|
1838
|
+
ancestry. It leaves the journal and unfinished task untouched; handoff, writer, publication and
|
|
1839
|
+
release continue to require their own authority.
|
|
1840
|
+
|
|
1841
|
+
From 1.24.7, the same acquisition proof survives formal archival and a later guarded release.
|
|
1842
|
+
An ARCHIVED task must match its immutable archive receipt, original acquisition identity and
|
|
1843
|
+
attached descendant branch. RELEASED progress additionally requires the exact release receipt,
|
|
1844
|
+
absence of checkout/registration/bindings/lease, and the matching retained ref or official retirement
|
|
1845
|
+
proof. Archive identity checks read metadata only; preserved payload verification remains in the
|
|
1846
|
+
normal archive-release guards. Journals and task-completion/publication claims remain unchanged.
|
|
1847
|
+
|
|
1848
|
+
When an unfinished transaction blocks activation, the staged read-only check reports the
|
|
1849
|
+
first refused transaction's ID, command, phase, concrete error and recovery entry under
|
|
1850
|
+
`decision.transaction_diagnostics`. The frozen gate retains these fields, adds the repository,
|
|
1851
|
+
and passes them to the activation log. Older staged errors retain their code and message;
|
|
1852
|
+
failed or malformed checks report an explicit diagnostic and remain blocked.
|
|
1853
|
+
An interrupted `release`, including `RELEASED_RECORDED` without its formal release receipt,
|
|
1854
|
+
requires the original guarded `release` request. With capability
|
|
1855
|
+
`release_recovery_inspection_version=1`, inspect that exact transaction through the producer:
|
|
1856
|
+
|
|
1857
|
+
```bash
|
|
1858
|
+
worktree-controller release-recovery-inspect --repo /absolute/repository \
|
|
1859
|
+
--transaction-id <original-uuid> --json
|
|
1860
|
+
```
|
|
1861
|
+
|
|
1862
|
+
The query verifies the original journal and ReleasePlan, their repository/allocation/path/branch/
|
|
1863
|
+
HEAD/CAS identity and any referenced publication or release intent. It returns the original
|
|
1864
|
+
owner and `request`, existing evidence paths with raw SHA-256 digests, and `release_argv` as
|
|
1865
|
+
individual arguments. The original CAS comes from the plan, even when the current `RELEASED`
|
|
1866
|
+
record has advanced and its owner is `unassigned`. Missing, malformed or inconsistent evidence
|
|
1867
|
+
is refused. Consumers pass the returned arguments to `worktree-controller` through an argument
|
|
1868
|
+
array, never shell evaluation. Publication recovery preserves `--publication-receipt-id`;
|
|
1869
|
+
intent recovery preserves `--intent-id`.
|
|
1870
|
+
Inspection is read-only and sets `release_authorized=false` and `execution_guards_checked=false`:
|
|
1871
|
+
the original task must still verify its authority and use normal guarded `release` recovery.
|
|
1872
|
+
It does not declare that live resource, binding, retained-ref or other execution guards pass,
|
|
1873
|
+
create a receipt, execute recovery, or clear the rollout gate. On older runtimes, inspect the
|
|
1874
|
+
persisted journal and release plan and verify the exact original identity before normal replay.
|
|
1875
|
+
Once the checkout is removed,
|
|
1876
|
+
`release --dry-run` refuses with `INTERRUPTED_TRANSACTION` and requires formal recovery;
|
|
1877
|
+
this diagnostic adds no dry-run recovery authority. Only supported legacy cleanup requires a new
|
|
1878
|
+
preservation baseline; unknown commands and cleanup contracts remain refused. Recovery entries
|
|
1879
|
+
are hints, require their original identity/evidence arguments, and grant no execution
|
|
1880
|
+
authority. The check never repairs metadata or relaxes activation and retry guards.
|
|
1881
|
+
|
|
1882
|
+
`controller-runtime` supplies the three stable entry points. Each invocation resolves one
|
|
1883
|
+
concrete active runtime and stays on it through completion. A broken active pointer fails
|
|
1884
|
+
closed. Switching uses the activation lock and compares the prior pointer with preflight;
|
|
1885
|
+
a failed post-activation check restores that exact prior runtime only if no later activation
|
|
1886
|
+
has changed the pointer. Rollback has the same gates and no skip-gate escape hatch.
|
|
1887
|
+
|
|
1888
|
+
For temporary concurrent activity, add `--wait-ready-seconds 300` to an ordinary `activate`
|
|
1889
|
+
or `rollback` command. The optional integer budget is 1..3600 seconds; omitting it preserves
|
|
1890
|
+
the immediate-refusal behavior. Only `CONTROLLER_MUTATION_IN_FLIGHT:<repository>`,
|
|
1891
|
+
`CONTROLLER_TRANSACTION_SET_CHANGED:<repository>` and the legacy
|
|
1892
|
+
`LIVE_CONTROLLER_INVOCATION:<pid>` shape are retryable, and every blocker must be one of
|
|
1893
|
+
those types. From 1.30.4 the frozen gate never blocks on a process count: live Controller
|
|
1894
|
+
processes are reported as the `live_controller_invocations` inventory, and
|
|
1895
|
+
`CONTROLLER_MUTATION_IN_FLIGHT` names an exclusive repository-lock holder, so shared-lock
|
|
1896
|
+
admission accounting no longer masquerades as a mutation. Compatibility, publication,
|
|
1897
|
+
unfinished-transaction, unobservable-process, unknown and malformed decisions stop
|
|
1898
|
+
immediately. Each attempt repeats the complete gate from the same frozen source
|
|
1899
|
+
with the same repository, consumer and recovery-proof declarations.
|
|
1900
|
+
|
|
1901
|
+
From 1.24.0, a host may release a writer lease after its runtime reaped the holder: the exact
|
|
1902
|
+
recorded identity plus `--lease-id` releases an exited holder's lease (`holder_exited: true`) when
|
|
1903
|
+
no member of its process group survives and no other process still uses the checkout; anything
|
|
1904
|
+
else still refuses. An intent that was claimed,
|
|
1905
|
+
aborted and retried can now resolve a later pre-allocation conflict: retry judges only the current
|
|
1906
|
+
attempt (`integration_conflict_resolution_version` 2). Renewal of a stale release intent for an
|
|
1907
|
+
unchanged record and HEAD backs off exponentially from the second repeat (TTL doubled per repeat,
|
|
1908
|
+
capped at a day; `deferred: RENEWAL_BACKOFF`). From 1.24.1 retention convergence applies the
|
|
1909
|
+
same backoff before it creates a release intent to classify a frozen handoff's `.venv`
|
|
1910
|
+
(`RENEWAL_BACKOFF` on the row). `scripts/check-shards [N]` runs the test
|
|
1911
|
+
suite in isolated parallel shards plus ruff; `.agents/integration.toml` points Agent Workspace's
|
|
1912
|
+
`just integrate` at it. CLI contract 56, storage layout 4.
|
|
1913
|
+
|
|
1914
|
+
From 1.23.0, `canonical-refresh [--dry-run]` fast-forwards an ACTIVE canonical checkout to the
|
|
1915
|
+
fetched publication branch after integrations publish from candidates. It acts only when the
|
|
1916
|
+
checkout is clean, on the publication branch, holds no writer lease, runtime binding or unfinished
|
|
1917
|
+
transaction, is used by no live process other than the caller's own ancestry, and is an ancestor
|
|
1918
|
+
of published main whose policy remains governable. It reports `CANONICAL_REFRESHED`,
|
|
1919
|
+
`CANONICAL_CURRENT` or `CANONICAL_REFRESH_READY`, and refuses otherwise (a frozen canonical keeps
|
|
1920
|
+
`canonical-transition`). Controller metadata is unchanged. Capability `canonical_refresh_version` 1;
|
|
1921
|
+
CLI contract 55, storage layout 4.
|
|
1922
|
+
|
|
1923
|
+
From 1.24.8, live-process `CANONICAL_IN_USE` refusals additionally expose
|
|
1924
|
+
`decision.process_diagnostics` and the same details in text output. At most five processes show
|
|
1925
|
+
kernel process names (64 characters), parent PID/name and three distinct matched paths (512
|
|
1926
|
+
characters each); omission counts and path-truncation flags mark the evidence limits. The process
|
|
1927
|
+
path scan is reused; identity metadata accepts at most 4096 characters, with one extra character
|
|
1928
|
+
read to detect oversize. Command arguments and
|
|
1929
|
+
environments are not read. Exited, unreadable or malformed identity observations become `unknown`;
|
|
1930
|
+
the original positive occupancy still blocks the attempt. Parent/name details are advisory
|
|
1931
|
+
observations, not verified task ownership or permission to terminate a process.
|
|
1932
|
+
|
|
1933
|
+
Use the owning daemon's native session inventory to identify the exact session and other active
|
|
1934
|
+
work. End only the completed task-owned session through its normal lifecycle; ending the current
|
|
1935
|
+
CLI alone may leave shared-daemon tools running. After the references clear, run the returned
|
|
1936
|
+
`retry_argv` (a guarded `canonical-refresh --dry-run --json`), then repeat without `--dry-run` once
|
|
1937
|
+
ready. These diagnostics do not archive sessions, stop processes or grant cleanup authority.
|
|
1938
|
+
The optional diagnostic field is additive; CLI contract 56 and storage layout 4 are unchanged.
|
|
1939
|
+
|
|
1940
|
+
From 1.22.5, the gate observes transaction membership before and after the staged check, including
|
|
1941
|
+
an initially empty set. Readiness requires the same membership at both observations and in the
|
|
1942
|
+
staged result. Only a successful, well-formed check matching either observed boundary, together
|
|
1943
|
+
with an actual before/after membership change, produces the retryable set-change blocker.
|
|
1944
|
+
Recovery errors, contradictory results, unreadable evidence and a reported set matching neither
|
|
1945
|
+
boundary retain the permanent refusal. These are bounded observations, not an atomic snapshot
|
|
1946
|
+
of the entire gate; the existing mutation, process and publication checks remain mandatory.
|
|
1947
|
+
|
|
1948
|
+
The monotonic budget includes gate execution and the pauses between attempts. Each gate runs in
|
|
1949
|
+
its own process group; expiration terminates that invocation and its children. The prior active
|
|
1950
|
+
pointer is checked before and after attempts; the existing 300-second per-gate command limit also
|
|
1951
|
+
remains fatal. The existing switch CAS remains mandatory.
|
|
1952
|
+
Timeout or pointer drift does not switch or restore a runtime. The option does not apply to
|
|
1953
|
+
bootstrap, layout migration/recovery, or the automatic rollback after a failed post-check.
|
|
1954
|
+
|
|
1955
|
+
For the one-time namespace/schema transition, `activate` also accepts
|
|
1956
|
+
`--migrate-namespace-from <previous-common-directory-name>`,
|
|
1957
|
+
`--migrate-policy-from <previous-repository-relative-policy-path>`, and repeatable
|
|
1958
|
+
`--previous-entrypoint /absolute/installed-entry=SHA256`. All three are required together.
|
|
1959
|
+
Supply the exact governed repository set through `CONTROLLER_ROLLOUT_GOVERNED`, the committed
|
|
1960
|
+
checkout through `CONTROLLER_ROLLOUT_REPO` (defaults to the script's checkout), and the source URL
|
|
1961
|
+
through `CONTROLLER_ROLLOUT_GIT_SOURCE`. Stage and activation refuse an unspecified source. The
|
|
1962
|
+
package index defaults to the normal `uv` configuration; `CONTROLLER_ROLLOUT_INDEX` overrides it.
|
|
1963
|
+
`--temp-helper /absolute/helper` remains available for sites with a shared temporary-directory
|
|
1964
|
+
helper; otherwise the script creates and removes its own temporary directory.
|
|
1965
|
+
|
|
1966
|
+
For a Skill installation that lags the active runtime, use the source-owned maintenance
|
|
1967
|
+
entry point without activating another version:
|
|
1968
|
+
|
|
1969
|
+
```bash
|
|
1970
|
+
scripts/controller-activate sync-skill --version <full-active-commit-oid> --dry-run
|
|
1971
|
+
scripts/controller-activate sync-skill --version <same-full-active-commit-oid>
|
|
1972
|
+
```
|
|
1973
|
+
|
|
1974
|
+
Set `CONTROLLER_ROLLOUT_REPO` and `CONTROLLER_ROLLOUT_GIT_SOURCE` to the governed source as
|
|
1975
|
+
above. The entry point verifies the active runtime against that exact committed source,
|
|
1976
|
+
then reads its committed `skills/worktree-lifecycle` payload. Only `SKILL.md` may be replaced;
|
|
1977
|
+
`agents/openai.yaml` must already match, and unknown paths, symlinks, or installed instructions
|
|
1978
|
+
that match no committed ancestor of the active version refuse synchronization. The active
|
|
1979
|
+
pin and installation are checked again under the activation lock. Failed replacement or
|
|
1980
|
+
verification restores the previous Skill bytes; unknown concurrent drift blocks restoration.
|
|
1981
|
+
Runtime pointers, sessions, lifecycle metadata and the shared Claude Skill link are preserved.
|
|
1982
|
+
Dry-run creates no synchronization receipt. Apply writes an identity-only receipt under
|
|
1983
|
+
`~/.local/state/worktree-controller-rollout/`, including when the Skill is already current;
|
|
1984
|
+
that no-change case does not replace the installed file.
|
|
1985
|
+
|
|
1986
|
+
The transition verifies the previous runtime against its own frozen public contract,
|
|
1987
|
+
holds the activation and repository locks, and temporarily refuses old entry points.
|
|
1988
|
+
It moves each unchanged policy to `.agents/worktree-policy.toml` and the existing metadata
|
|
1989
|
+
directory to `worktree-controller/v1`. Only current writer leases change schema:
|
|
1990
|
+
`holder.provider` becomes `holder.client_id`; all identities, ownership, timestamps and
|
|
1991
|
+
expiry remain intact. Directory inodes and historical receipts are preserved. Publication
|
|
1992
|
+
leases, unsupported unfinished transactions, malformed leases, concurrent mutations and
|
|
1993
|
+
conflicting layouts block the transition.
|
|
1994
|
+
|
|
1995
|
+
The layout gate uses the same terminal-evidence classifier as cleanup and doctor. It also
|
|
1996
|
+
admits an exact `tracked-source-no-action-recovery/v1` read-only proof; this neither reconciles
|
|
1997
|
+
the attempt nor authorizes a destructive action. The proof is checked again under the
|
|
1998
|
+
repository lock. Other live completion evidence tied to old paths still blocks migration.
|
|
1999
|
+
Only this explicit migration read view validates schema 1 leases through the existing
|
|
2000
|
+
lease converter in memory; normal runtime parsing still requires schema 2. An unrelated
|
|
2001
|
+
lease survives the transition, while a lease for the cleanup allocation still blocks it.
|
|
2002
|
+
`metadata-reference-relocation/v1` pins the old/new roots, unchanged directory device/inode
|
|
2003
|
+
and each immutable cleanup JSON file's relative path and SHA-256. Its exclusive record
|
|
2004
|
+
travels with the directory rename. Historical references resolve only through this exact
|
|
2005
|
+
inventory and digest, using no-follow reads; there is no second metadata authority, alias
|
|
2006
|
+
symlink, rewritten receipt or guessed legacy namespace. Historical attempt-chain identities
|
|
2007
|
+
remain unchanged. Missing/changed references or an overlapping old namespace fail closed.
|
|
2008
|
+
|
|
2009
|
+
The frozen consumer gate and post-activation checks still run. Failure restores the exact
|
|
2010
|
+
previous policy, state location, entry points and active pointer. The durable layout receipt
|
|
2011
|
+
also supports `rollback --layout-receipt <absolute-receipt> --layout-receipt-sha256 <digest>`;
|
|
2012
|
+
lease renewals are preserved during reverse conversion. Rollback removes only its own
|
|
2013
|
+
additive relocation record after moving original evidence back unchanged; an interrupted
|
|
2014
|
+
inverse move retains its reference map for retry. New reconciliation,
|
|
2015
|
+
action/completion evidence, drift or newly unfinished work blocks rollback without discarding
|
|
2016
|
+
it. After real client acceptance, call `finish-layout`
|
|
2017
|
+
with that same receipt and its latest digest to retire the fenced old entry points and empty
|
|
2018
|
+
old directories. Keep the recovery receipt; it records the retired layout as provenance.
|
|
2019
|
+
|
|
2020
|
+
### Started resume attempts (0.9.8)
|
|
2021
|
+
|
|
2022
|
+
`cleanup-resume-attempt/v1` records `ATTEMPT_STARTED` separately from immutable action
|
|
2023
|
+
evidence (`ACTION_STARTED`, `ACTION_COMPLETED`). An incomplete call records the underlying
|
|
2024
|
+
status, blocker/error codes, safe result/exception summaries and attempt identity before
|
|
2025
|
+
coordinator diagnostics. Cleanup is an in-process Python call: subprocess exit code is
|
|
2026
|
+
explicitly null, and no stdout/stderr stream is invented. The CLI returns nonzero for
|
|
2027
|
+
`RECOVERY_ATTEMPT_INCOMPLETE`. Missing completion never becomes a `None.json` lookup.
|
|
2028
|
+
|
|
2029
|
+
`resource-recover --stage reconcile-attempt --recovery-receipt <record.json>
|
|
2030
|
+
--recovery-receipt-sha256 <sha256> --dry-run` evaluates `started-no-action-recovery/v1`.
|
|
2031
|
+
Execution additionally requires that dry-run's `--expected-plan-id`. It verifies the exact
|
|
2032
|
+
full source/resource baseline, hardlink topology, both preservation generations, Git/runtime
|
|
2033
|
+
authority and immutable original evidence; new journals, completion receipts or destructive
|
|
2034
|
+
action evidence block this narrow recovery. Absence of a completion receipt alone is insufficient.
|
|
2035
|
+
|
|
2036
|
+
Successful reconciliation appends `STARTED_NO_ACTION_RECOVERABLE`, with the assertion
|
|
2037
|
+
"attempt started, no action proven, safe to supersede". It does not reconstruct a missing
|
|
2038
|
+
historical refusal. A subsequent explicit `--stage resume` creates a new UUID under
|
|
2039
|
+
`attempts/`, binds its predecessor reconciliation, revalidates the baseline, and processes
|
|
2040
|
+
only dist, node_modules and preserved legacy state. Legacy `started.json`, receipts and
|
|
2041
|
+
reconciliation records are retained unchanged. A failed attempt needs a separate new
|
|
2042
|
+
reconciliation; it cannot be blindly retried. Any possible partial action remains blocked.
|
|
2043
|
+
`ATTEMPT_FAILED_BEFORE_ACTION` requires fresh no-action proof; `PARTIAL_ACTION` also covers
|
|
2044
|
+
uncertainty when that proof fails. Host visibility gaps remain warnings, without a claim of
|
|
2045
|
+
host-wide absence of references. Automatic release and disposable types are unchanged.
|
|
2046
|
+
|
|
2047
|
+
### Exact tracked modified source preservation
|
|
2048
|
+
|
|
2049
|
+
`tracked-source-preservation/v1` preserves only unstaged ` M` regular files in an
|
|
2050
|
+
`ACTIVE` source allocation. The index must match the exact requested HEAD. Staged,
|
|
2051
|
+
untracked, deleted, renamed, symlink/type-changed, sparse or assume-unchanged source
|
|
2052
|
+
states are refused. Ignored resources remain outside this receipt's coverage.
|
|
2053
|
+
|
|
2054
|
+
The manifest binds allocation/owner/record version, worktree identity, base HEAD/tree,
|
|
2055
|
+
the complete dirty path set, Git status, original permission mode, byte size and SHA-256.
|
|
2056
|
+
An independently computed Git tree identifies the base tree plus the exact raw file
|
|
2057
|
+
overlay. This is not a normalized/staged tree: preservation never changes payload bytes,
|
|
2058
|
+
the index, HEAD or worktree. File and parent directory identities are checked before and
|
|
2059
|
+
after copying, including inode, ctime and link count. Runtime authority uses the existing
|
|
2060
|
+
managed/observed/uncertain blockers and records host visibility gaps without a host-wide
|
|
2061
|
+
absence claim.
|
|
2062
|
+
|
|
2063
|
+
The existing no-follow directory/regular-file primitives and immutable generation root
|
|
2064
|
+
are reused. Each generation contains `plan.json`, `manifest.json`, `receipt.json` and the
|
|
2065
|
+
exact file overlay under `tree/`. Files are sealed read-only; their original modes remain
|
|
2066
|
+
in the manifest for reconstruction. An existing generation is never overwritten, a failed
|
|
2067
|
+
copy has no success receipt, and verification checks both current source and preserved
|
|
2068
|
+
bytes against the pinned plan and receipt hash. Unmodified files come from the retained
|
|
2069
|
+
base HEAD. The contract bounds coverage to 256 modified files, 64 MiB per file and
|
|
2070
|
+
128 MiB total. Git content filters/encoding and CRLF-expanding checkout configurations
|
|
2071
|
+
are unsupported; `core.autocrlf=input` does not transform preserved raw bytes.
|
|
2072
|
+
|
|
2073
|
+
Credential paths are refused before opening payloads. Recognizable private-key/provider
|
|
2074
|
+
secret markers also fail closed before that file is copied; diagnostics never include
|
|
2075
|
+
payloads. This source-only check is not a general secret detector or provider-state
|
|
2076
|
+
preservation backend. Review the exact selected source paths before executing a plan.
|
|
2077
|
+
|
|
2078
|
+
The module CLI is `python -m worktree_controller.source_preservation`: `plan` requires
|
|
2079
|
+
`--repo`, `--path`, `--allocation-id`, `--owner`, `--expected-record-version`, the full
|
|
2080
|
+
`--expected-head` and an exclusive `--plan-file` outside the checkout. `copy --plan-file`
|
|
2081
|
+
creates the generation; `verify --plan-file --receipt-sha256` independently verifies it.
|
|
2082
|
+
The receipt explicitly records `cleanup_authorized=false`: neither preservation nor its
|
|
2083
|
+
verification changes lifecycle or relaxes the ordinary dirty/release gates.
|
|
2084
|
+
|
|
2085
|
+
`tracked-source-cleanup/v1` is a separate, explicit operation. Its module CLI is
|
|
2086
|
+
`python -m worktree_controller.source_cleanup plan --plan-file <preservation-plan>
|
|
2087
|
+
--receipt-sha256 <trusted-sha256>`. Execution requires `execute` and the fresh
|
|
2088
|
+
`--expected-plan-id`. It restores exactly those tracked files to their bound HEAD blob
|
|
2089
|
+
bytes and executable mode. The index remains byte- and identity-identical; there is no
|
|
2090
|
+
reset, stash, staging, commit or implicit release. A modified source inode with multiple
|
|
2091
|
+
hardlinks is refused, so restoration cannot change an out-of-scope linked file.
|
|
2092
|
+
|
|
2093
|
+
Restoration pins each regular inode using `O_PATH | O_NOFOLLOW`, verifies the complete
|
|
2094
|
+
source identity and digest, and reopens only that descriptor for writing. Every write,
|
|
2095
|
+
truncate and mode restoration operates on the verified FD. Parent/leaf changes fail
|
|
2096
|
+
closed; external symlink targets are never opened for writing. Interrupted or short writes
|
|
2097
|
+
may leave an already-preserved source file partial. The Controller journal and independent
|
|
2098
|
+
immutable before/after/failure evidence then remain, block release and forbid blind replay.
|
|
2099
|
+
No automatic rollback or partial-source recovery is introduced by this increment.
|
|
2100
|
+
|
|
2101
|
+
After a verified source cleanup completes, a later Git index stat-cache rewrite does not
|
|
2102
|
+
invalidate archival evidence. Controller 1.2.1 rechecks exact HEAD/tree, every staged mode/blob,
|
|
2103
|
+
index flags, clean source and the preserved/restored bytes at closeout and release. The current
|
|
2104
|
+
index identity and bytes must stay stable throughout that verification; the historical action
|
|
2105
|
+
still proves that restoration itself never changed the index. Old receipts are not rewritten.
|
|
2106
|
+
|
|
2107
|
+
An interrupted typed cleanup with every action still PENDING and no action evidence
|
|
2108
|
+
can use `resource-recover --stage reconcile-typed --transaction-id ... --transaction-sha256 ...`
|
|
2109
|
+
with a fresh dry-run plan ID. It rechecks the complete in-scope inode/member/content baseline,
|
|
2110
|
+
resource classification, owner, Git and runtime. Only changes to an already external link-count
|
|
2111
|
+
remainder and its ctime may be observed; their historical cause remains NOT_PROVEN. Started or
|
|
2112
|
+
partial actions, unknown receipts, content and inode changes remain blocked. The immutable
|
|
2113
|
+
`typed-cleanup-no-action/v1` record supersedes the original pending attempt without changing its
|
|
2114
|
+
journal. A subsequent fresh ordinary cleanup gets a new transaction. Existing approved automatic
|
|
2115
|
+
release intents use the same reconciliation; a clean directory alone grants no such authority.
|
|
2116
|
+
A missing completion receipt or an exact immutable pre-action refusal receipt is supported.
|
|
2117
|
+
The latter must match the original journal/plan/actions, contain no action/runtime checkpoint,
|
|
2118
|
+
and is pinned by SHA-256 in the recovery record. Its historical result remains unchanged.
|
|
2119
|
+
Cleanup preflight refusals now retain the underlying blocker code for diagnosis.
|
|
2120
|
+
|
|
2121
|
+
Every action checks HEAD, dirty path set/digest, mode, index, parent/file identities,
|
|
2122
|
+
runtime authority and the immutable preservation generation. Successful source cleanup
|
|
2123
|
+
records separate completion evidence and formally removes its journal. Completion and
|
|
2124
|
+
release reverify that generation and the restored source. A task with no task-produced
|
|
2125
|
+
commit may then use the existing `complete-no-change` lifecycle; its current completion
|
|
2126
|
+
receipt references the archived source cleanup. This records no historical handoff,
|
|
2127
|
+
successful validation or publication. Typed cleanup also accepts this narrowly evidenced
|
|
2128
|
+
`NO_CHANGE_COMPLETED` state; its resource identity, unknown ignored content, runtime and
|
|
2129
|
+
ordinary release gates remain in force. Other no-change allocations gain no new typed
|
|
2130
|
+
cleanup permission from this contract.
|
|
2131
|
+
|
|
2132
|
+
`controller-transaction-conflict/v1` keeps the repository mutation lock while checking
|
|
2133
|
+
nonterminal conflicts by exact allocation and overlapping resource scope. The
|
|
2134
|
+
coordinator identifies its repository/allocation/transaction, worktree, operation
|
|
2135
|
+
and resource contract. Independent nonterminal resource scopes do not conflict;
|
|
2136
|
+
unknown identity/lifecycle fails closed. Journal counts are never a conflict predicate.
|
|
2137
|
+
Every journal identity is validated on each check, but terminal evidence is verified only for
|
|
2138
|
+
journals in the caller's scope (overlapping resource scope, or the retiring allocation), so large
|
|
2139
|
+
unrelated historical evidence does not slow an unrelated operation.
|
|
2140
|
+
Only verified immutable terminal evidence permits a retained journal to coexist with a
|
|
2141
|
+
new operation, including operations in the same allocation. Neither retention nor
|
|
2142
|
+
absence alone establishes terminal status. Doctor reports historical terminal attempts
|
|
2143
|
+
separately from unresolved transactions.
|
|
2144
|
+
|
|
2145
|
+
An interrupted `acquire` or `integration-refresh` journal closes only on positive
|
|
2146
|
+
current-state evidence. The completion shapes require the checkout directory, its
|
|
2147
|
+
administrative worktree registration, the binding derived from that entry, the record's
|
|
2148
|
+
task commit and the branch ref to agree exactly with the journal; the no-effect shapes
|
|
2149
|
+
require every one of those facts to be provably absent, including the target itself.
|
|
2150
|
+
Half-shapes — an orphan worktree, a dangling detached HEAD, a moved branch, a record
|
|
2151
|
+
that disagrees, or a residual checkout directory — keep the fence until the operator
|
|
2152
|
+
restores one of the two complete shapes. The classifier never rewrites or deletes a
|
|
2153
|
+
journal, reads refs only from the files backend (reftable disables inference), and runs
|
|
2154
|
+
only while the repository flock is held. Branch retirement's `TRANSACTION_ACTIVE` gate
|
|
2155
|
+
uses this same pending/terminal semantics as `doctor` and mutation admission.
|
|
2156
|
+
Dot-prefixed `.tmp` siblings inside `transactions/` are interrupted atomic writes that
|
|
2157
|
+
never became records: conflict checks, doctor, rollout and activation layout scans
|
|
2158
|
+
ignore them, and doctor reports them as a non-blocking residue count.
|
|
2159
|
+
|
|
2160
|
+
`repo.lock` arbitrates every writer. A mutation against a missing lock stops with
|
|
2161
|
+
`MISSING_REPOSITORY_LOCK`, and a symlinked, non-regular or unopenable lock stops with
|
|
2162
|
+
`REPOSITORY_LOCK_INVALID` for readers and writers alike; the lock is opened with
|
|
2163
|
+
`O_NOFOLLOW` and must be a regular readable-writable file, which is also the contract
|
|
2164
|
+
the public publication session enforces on behalf of its consumer. `adopt`, `acquire` and `enqueue-integration`
|
|
2165
|
+
recreate a missing lock as their bootstrap step. Doctor turns both states into
|
|
2166
|
+
blocking findings carrying the exact path, so recovery is `remove or recreate the
|
|
2167
|
+
reported path`, after which any writer re-establishes the arbiter.
|
|
2168
|
+
|
|
2169
|
+
`tracked-source-no-action-recovery/v1` accepts only a recorded transaction-scope refusal
|
|
2170
|
+
with empty actions/checks, no before/action evidence, and an exact fresh source, parent,
|
|
2171
|
+
HEAD, index, preservation-generation and runtime verification. Its module CLI is
|
|
2172
|
+
`python -m worktree_controller.source_recovery plan --repo <repo> --transaction-id <id>
|
|
2173
|
+
--receipt-sha256 <trusted-failed-receipt-sha256>`; `reconcile` additionally requires the
|
|
2174
|
+
fresh `--expected-plan-id`. Reconciliation adds an exclusive immutable record beside the
|
|
2175
|
+
original plan. The old receipt and journal remain byte-identical. The record proves
|
|
2176
|
+
`STARTED_NO_ACTION_RECOVERABLE` and binds one new, single-use successor attempt identity;
|
|
2177
|
+
the original attempt is `SOURCE_ATTEMPT_SUPERSEDED`, not a completed source cleanup.
|
|
2178
|
+
Fresh installed cleanup planning and execution must reverify the full baseline. Any
|
|
2179
|
+
possible partial action remains blocked. Structured underlying refusal and conflict
|
|
2180
|
+
identities survive coordinator/evidence-write errors. Rollout may inspect this state
|
|
2181
|
+
read-only to admit an installed recovery-capable runtime; it never performs reconciliation.
|
|
2182
|
+
|
|
2183
|
+
|
|
2184
|
+
`tracked-source-progress-recovery/v1` (1.13.2) is a separate append-only closeout for
|
|
2185
|
+
an interrupted attempt whose recorded actions form a proper restore prefix with a nonempty preserved remainder. Use
|
|
2186
|
+
`python -m worktree_controller.source_recovery plan-progress` with the same repository,
|
|
2187
|
+
transaction and failed-receipt SHA options; `reconcile-progress` additionally requires
|
|
2188
|
+
its fresh `--expected-plan-id`. All completed actions require their exact before/after
|
|
2189
|
+
chain. An optional last STARTED action must now match its bound HEAD bytes, size and
|
|
2190
|
+
mode on the original single-link inode; it is recorded as a current observation,
|
|
2191
|
+
never retroactively completed. Remaining dirty files must exactly match preservation.
|
|
2192
|
+
Both read-only planning and the locked append reverify authority, runtime, HEAD, index,
|
|
2193
|
+
parents and the immutable generation. Unknown evidence or partial bytes fail closed.
|
|
2194
|
+
|
|
2195
|
+
The exclusive `progress-reconciliation.json` retains the old receipt, journal and
|
|
2196
|
+
before/after records byte-identical. Shared transaction classification reports
|
|
2197
|
+
`SOURCE_ATTEMPT_PRESERVED` only after validating this frozen proof. This closes the
|
|
2198
|
+
historical attempt without source writes, cleanup completion, lifecycle transition,
|
|
2199
|
+
successor grant or release authority. Future legitimate task edits do not invalidate
|
|
2200
|
+
historical terminal evidence; current dirty-source release gates remain unchanged.
|
|
2201
|
+
An unresolved partial attempt still blocks activation: stage the exact recovery-capable
|
|
2202
|
+
commit, execute its supported module, then run the normal rollout/activation gates.
|
|
2203
|
+
Older runtimes that cannot validate this marker remain blocked by rollback preflight;
|
|
2204
|
+
never delete the marker or journal to force rollback compatibility.
|
|
2205
|
+
|
|
2206
|
+
### Cleanup hardlinks and re-preservation recovery (0.9.7)
|
|
2207
|
+
|
|
2208
|
+
`cleanup-hardlink-topology/v1` records every non-directory inode, all cleanup-scope names,
|
|
2209
|
+
node identities and opaque content digests when the observed name count equals `st_nlink`.
|
|
2210
|
+
Same-resource and cross-resource links retain explicit membership. `cleanup-induced-hardlink-mutation/v1`
|
|
2211
|
+
accepts only the exact link-count decrement for the preceding completed action and its ctime
|
|
2212
|
+
effect; surviving paths, inode, type, mode, size, mtime, ownership, bytes and symlink literals
|
|
2213
|
+
remain unchanged. Every action saves exclusive before/after records under `cleanup-actions`.
|
|
2214
|
+
Deletion continues through verified directory FDs and checks each pinned inode's full identity.
|
|
2215
|
+
|
|
2216
|
+
Controller 1.2.0 additionally writes `cleanup-hardlink-topology/v2` when an inode has external
|
|
2217
|
+
links. It records their exact link-count remainder without searching for, opening or modifying
|
|
2218
|
+
external names. Only already-authorized in-scope names are unlinked. Every unlink pins the inode
|
|
2219
|
+
with `O_PATH|O_NOFOLLOW`, verifies its bytes (or symlink literal) before and after the action,
|
|
2220
|
+
and accepts exactly one link-count decrement plus its ctime effect. Regular bytes are read through
|
|
2221
|
+
that same FD with `O_NOATIME`; FIFO endpoints remain unopened. The final pinned observation is
|
|
2222
|
+
retained in `cleanup-induced-hardlink-mutation/v2`, including when no in-scope name survives.
|
|
2223
|
+
Detected content, identity, ownership, mode, size, mtime or unexpected link-count changes block
|
|
2224
|
+
further actions and retain partial evidence. This proves the observed unlink boundary; it does
|
|
2225
|
+
not exclude external writers globally or claim host-wide absence of references. Unknown content
|
|
2226
|
+
and existing runtime/reference checks retain their own gates.
|
|
2227
|
+
|
|
2228
|
+
The v1 encoding and historical evidence remain unchanged, and the current reader supports both
|
|
2229
|
+
versions. `capabilities.cleanup_hardlink_topology_version=2` is independent of software, metadata
|
|
2230
|
+
and writer-session versions. Activation and rollback use the target installed runtime's existing
|
|
2231
|
+
read-only recovery probe; a target unable to validate retained transactions/terminal evidence is
|
|
2232
|
+
refused. A binary downgrade alone is not a recovery operation.
|
|
2233
|
+
|
|
2234
|
+
`re-preservation-recovery/v1` supports exactly a failed legacy cleanup with `backend/.venv`
|
|
2235
|
+
COMPLETED and `frontend/dist`, `frontend/node_modules`, `.venv` PENDING. It makes no historical
|
|
2236
|
+
causal assertion: `historical_hardlink_cause=NOT_PROVEN`. A new preservation plan binds the
|
|
2237
|
+
original journal/receipt hashes, the current complete source tree and all remaining hardlinks.
|
|
2238
|
+
Current contents and stable identities must still match the earlier preserved state; only the
|
|
2239
|
+
unattributed nlink/ctime differences may exist before this new baseline is frozen. Subsequent
|
|
2240
|
+
source or topology changes invalidate the plan. FIFO nodes are preserved without opening them;
|
|
2241
|
+
symlink targets are never followed and provider files are copied/hashed only as opaque bytes.
|
|
2242
|
+
|
|
2243
|
+
Use the installed runtime's `python -I -m worktree_controller.preservation plan` with the existing
|
|
2244
|
+
allocation/source arguments plus `--recovery-transaction-id`, `--transaction-sha256`, and
|
|
2245
|
+
`--cleanup-receipt-sha256`; then use its `copy --plan-file` command. The new generation never
|
|
2246
|
+
overwrites the old one. `worktree-controller resource-recover --stage prepare --dry-run` requires the
|
|
2247
|
+
same original hashes and the new `--preservation-receipt`/`--preservation-receipt-sha256`.
|
|
2248
|
+
Executing prepare requires its exact `--expected-plan-id` and writes an immutable recovery
|
|
2249
|
+
record. `--stage resume --recovery-receipt ... --recovery-receipt-sha256 ...` revalidates it and
|
|
2250
|
+
uses the ordinary cleanup engine for only the three pending resources. A started execution
|
|
2251
|
+
cannot blindly replay after interruption. Partial cleanup continues to block allocation release.
|
|
2252
|
+
|
|
2253
|
+
Successful recovery writes an independent completion linked to every action and both preserved
|
|
2254
|
+
generations. The historical transaction file remains unchanged; doctor/release recognize its
|
|
2255
|
+
formal terminal evidence instead of deleting or rewriting its history. Allocation release is a
|
|
2256
|
+
separate guarded command. Managed references, observed references and managed uncertainty block;
|
|
2257
|
+
HOST_VISIBILITY_GAP remains warning/evidence and makes no host-wide absence claim. Unknown ignored
|
|
2258
|
+
resources remain blocked. This adds no ordinary disposable type, use lease or automatic release.
|
|
2259
|
+
Older runtimes do not interpret the terminal supplement and continue to block on the retained
|
|
2260
|
+
historical journal; recovery-aware lifecycle operations require Controller 0.9.7 or later.
|
|
2261
|
+
|
|
2262
|
+
To activate that first recovery-aware version, publish and stage it normally, then use the
|
|
2263
|
+
staged installed runtime to create and verify the new preservation generation. Pass its exact
|
|
2264
|
+
`--recovery-preservation-receipt` and `--recovery-preservation-receipt-sha256` to
|
|
2265
|
+
`scripts/controller-activate activate`. The frozen rollout gate asks that staged runtime for
|
|
2266
|
+
`resource-recover --stage rollout-check --dry-run`: it fully validates the current recovery
|
|
2267
|
+
baseline without writing recovery metadata. Only this exact recoverable transaction, or a
|
|
2268
|
+
verified append-only terminal recovery, can remain during activation. All other unfinished
|
|
2269
|
+
transactions, live mutations, publication leases and incompatible callers still block. Formal
|
|
2270
|
+
prepare/resume happens through the active installed runtime after activation.
|
|
2271
|
+
|
|
2272
|
+
### Checks
|
|
2273
|
+
|
|
2274
|
+
Linux, Python 3.12 and an unprivileged user are the supported acceptance environment. Local checks and
|
|
2275
|
+
`.gitea/workflows/checks.yml` use the same entry point:
|
|
2276
|
+
|
|
2277
|
+
```bash
|
|
2278
|
+
uv run --locked --python 3.12 --no-env-file python scripts/check-project \
|
|
2279
|
+
--work-dir /absolute/path/to/a/new/check-output-directory
|
|
2280
|
+
```
|
|
2281
|
+
|
|
2282
|
+
The output directory must not exist. The command retains wheel/sdist artifacts,
|
|
2283
|
+
isolated installation environments, test fixtures and `pytest.xml` for inspection;
|
|
2284
|
+
choose a disposable parent and clean it through your environment's normal cleanup
|
|
2285
|
+
workflow after reviewing the result. Add `--offline` once the locked dependencies,
|
|
2286
|
+
Python interpreter and build backend are cached.
|
|
2287
|
+
|
|
2288
|
+
For an isolated PID namespace, resolve Python 3.12 first and run the check entry point
|
|
2289
|
+
directly as namespace PID 1:
|
|
2290
|
+
|
|
2291
|
+
```bash
|
|
2292
|
+
wc_checks_python="$(uv python find 3.12)"
|
|
2293
|
+
unshare --user --map-current-user --pid --fork --mount-proc --kill-child=KILL \
|
|
2294
|
+
"$wc_checks_python" scripts/check-project \
|
|
2295
|
+
--work-dir /absolute/path/to/a/new/check-output-directory --offline
|
|
2296
|
+
```
|
|
2297
|
+
|
|
2298
|
+
This optional mode requires unprivileged Linux namespaces. As PID 1, the entry point
|
|
2299
|
+
runs the checks in a child and continuously reaps adopted orphan fixtures. It forwards
|
|
2300
|
+
SIGINT and SIGTERM to that worker's process group and returns its exit status, using
|
|
2301
|
+
128 plus the signal number for a signal exit. Command timeouts remain failures; when
|
|
2302
|
+
namespace init exits, Linux terminates the remaining namespace processes. The interpreter
|
|
2303
|
+
flags and check arguments are preserved. Outside PID 1 the ordinary checks run directly.
|
|
2304
|
+
|
|
2305
|
+
If your local environment provides the optional `tool-temp-dir` companion, create
|
|
2306
|
+
one owned acceptance root with a separate `tests` or `fixture` token in its prefix:
|
|
2307
|
+
|
|
2308
|
+
```bash
|
|
2309
|
+
set -euo pipefail
|
|
2310
|
+
wc_checks_root="$(tool-temp-dir create worktree-controller-tests | \
|
|
2311
|
+
python3 -c 'import json, sys; print(json.load(sys.stdin)["path"])')"
|
|
2312
|
+
uv run --locked --python 3.12 --no-env-file python scripts/check-project \
|
|
2313
|
+
--work-dir "$wc_checks_root/project"
|
|
2314
|
+
```
|
|
2315
|
+
|
|
2316
|
+
Use a new child such as `"$wc_checks_root/cross-tool"` for optional cross-tool output.
|
|
2317
|
+
After all checks have exited, review the retained results and preserve any evidence
|
|
2318
|
+
you need before cleaning the root:
|
|
2319
|
+
|
|
2320
|
+
```bash
|
|
2321
|
+
tool-temp-dir validate-cleanup "$wc_checks_root" --allow-owned-fixture-fifo
|
|
2322
|
+
tool-temp-dir cleanup "$wc_checks_root" --allow-owned-fixture-fifo
|
|
2323
|
+
```
|
|
2324
|
+
|
|
2325
|
+
The fixture prefix and explicit FIFO option allow native cleanup of the suite's
|
|
2326
|
+
owned FIFO fixtures under the unchanged nested output layout. Do not move fixture
|
|
2327
|
+
directories to satisfy cleanup or delete special files by hand. This companion is
|
|
2328
|
+
optional; ordinary acceptance and CI retain the generic output-directory workflow.
|
|
2329
|
+
|
|
2330
|
+
Checks cover Ruff, matching source/package CLI contracts and versions, wheel/sdist
|
|
2331
|
+
builds, rebuilding a wheel from the sdist, installed console commands, distribution
|
|
2332
|
+
README/license metadata and the complete self-contained pytest suite. Package smoke
|
|
2333
|
+
checks run outside the source checkout with isolated Python imports. Tests use fixture
|
|
2334
|
+
state and do not inherit caller credentials, proxy settings or host integration opt-ins.
|
|
2335
|
+
Mandatory version, contract, package metadata and tool entry-point stability checks remain
|
|
2336
|
+
active with Python `-O` or `PYTHONOPTIMIZE=1`.
|
|
2337
|
+
Git Finalizer consumer acceptance tests remain separately opt-in; their skips are shown
|
|
2338
|
+
in pytest results. CI runs on the existing `ubuntu-latest` runner with pinned actions
|
|
2339
|
+
and uv, without publishing packages. The job pins its Ubuntu image, enables Docker's
|
|
2340
|
+
init process to reap orphan fixtures, and installs GNU Time in that disposable container
|
|
2341
|
+
when `/usr/bin/time` is missing. No test or recovery guard is skipped. Manual workflow
|
|
2342
|
+
dispatch can select an existing `source_ref` (commit or tag); the workflow revision and
|
|
2343
|
+
checked-out source identity remain separate. When the disposable CI container starts as root,
|
|
2344
|
+
the workflow gives its checkout and job-local Python/cache/output directories to
|
|
2345
|
+
`nobody:nogroup` and runs acceptance as that user so filesystem permission tests remain effective.
|
|
2346
|
+
|
|
2347
|
+
### Optional cross-tool acceptance
|
|
2348
|
+
|
|
2349
|
+
After the self-contained checks, run the forty-five acceptance cases against an explicitly
|
|
2350
|
+
selected installed Controller/Finalizer pair. Both tools and the Finalizer retirement companion
|
|
2351
|
+
are optional external prerequisites; the ordinary checks and CI do not require them.
|
|
2352
|
+
|
|
2353
|
+
Alongside retirement and publication recovery, the installed Controller cases cover direct and
|
|
2354
|
+
hosted handoff continuation, writes into other checkouts leased by the same process (including
|
|
2355
|
+
another repository), and refusal of writes into the frozen source or its symlink aliases. They
|
|
2356
|
+
check CLI exit status and unchanged fixture metadata, using subprocesses whose imports cannot
|
|
2357
|
+
fall back to this checkout. Unclassified requests, foreign sessions and unleased targets remain
|
|
2358
|
+
refused. The gate checks `host_handoff_continuation_version` independently of CLI and storage.
|
|
2359
|
+
|
|
2360
|
+
Eight installed-runtime cases also exercise real SIGINT while reporting a wait or re-polling
|
|
2361
|
+
admission, and preserve an already acquired and bound provider for ordinary exit reconciliation.
|
|
2362
|
+
They observe real procfs thread-count changes during five pool expansions and five contractions,
|
|
2363
|
+
retaining live file references. An exiting leader with a live thread produces actual vanished
|
|
2364
|
+
cwd/exe and denied fd observations: completion clears them on a bounded rescan, a persistent live
|
|
2365
|
+
thread stays uncertain, and permission denial alone stays uncertain without a rescan. Only
|
|
2366
|
+
scheduling is instrumented; process identities, errors and admission decisions come from the
|
|
2367
|
+
installed runtime and kernel. The load fixtures require Linux x86_64 or aarch64 and a commit-pinned
|
|
2368
|
+
Controller with the 1.24.2 behavior. Each worker uses the installed interpreter with isolated imports.
|
|
2369
|
+
|
|
2370
|
+
Five installed-CLI cases verify lease release after the host has reaped the holder. The exact
|
|
2371
|
+
recorded holder and lease ID release only writer entitlement, leaving the allocation and work
|
|
2372
|
+
unchanged. A missing or wrong lease ID, any changed holder field, a surviving member of the
|
|
2373
|
+
holder's process group, or another process still using the checkout must refuse without changing
|
|
2374
|
+
the recorded lease or allocation. Real fixture processes exercise these boundaries and close
|
|
2375
|
+
through ordinary no-change completion and guarded release once occupancy clears. The gate checks
|
|
2376
|
+
`exited_holder_release_version` independently of CLI and storage contracts.
|
|
2377
|
+
|
|
2378
|
+
Eight installed-tool cases exercise conflict recovery on the original integration intent. A
|
|
2379
|
+
prepared candidate is formally aborted and released, an intervening fixture integration changes
|
|
2380
|
+
the same file, and the original intent retries, observes a real merge conflict, retries again and
|
|
2381
|
+
publishes an exact two-parent resolution through the installed Git Finalizer. Queue position,
|
|
2382
|
+
frozen source and scope stay unchanged; old validation is cleared and explicit fixture content
|
|
2383
|
+
and Git review is bound again before publication. Public CLI refusals cover a candidate that was
|
|
2384
|
+
never aborted and an aborted candidate that was never released, without editing authority files.
|
|
2385
|
+
Resolution additionally refuses a wrong expected HEAD, an uncommitted candidate edit, a merge
|
|
2386
|
+
outside frozen scope and a commit with the wrong parent topology. These negative fixtures compare
|
|
2387
|
+
the complete public intent, candidate and source observations, publication lease and live remote
|
|
2388
|
+
main before and after refusal. The scope and parent cases deliberately create malformed Git input
|
|
2389
|
+
inside disposable fixtures; that input is not a Finalizer delivery. A resolved candidate also
|
|
2390
|
+
refuses a publication lease until fresh content/Git review is prepared, then publishes normally.
|
|
2391
|
+
All fixture checkouts close through guarded verbs. The gate independently checks
|
|
2392
|
+
`integration_conflict_resolution_version` and requires version 2 from the matching contract.
|
|
2393
|
+
|
|
2394
|
+
Three installed-CLI cases cover release-intent renewal with a real ten-second fixture TTL.
|
|
2395
|
+
The first expiry renews immediately without changing the frozen allocation. Repeated retention
|
|
2396
|
+
passes refuse an unrecognizable fixture environment, then report `RENEWAL_BACKOFF` after a second
|
|
2397
|
+
expiry without creating another active intent, changing the handoff or deleting its payload.
|
|
2398
|
+
Removing an explicit fixture workspace binding changes the record CAS and renews immediately;
|
|
2399
|
+
the registered environment and checkout release only through the existing verified publication
|
|
2400
|
+
and guarded cleanup flow. The fixtures wait for the normal one-minute retention grace and use
|
|
2401
|
+
public CLI responses throughout, without editing private metadata or patching installed code.
|
|
2402
|
+
The gate also checks `retention_convergence_version` independently of CLI and storage contracts.
|
|
2403
|
+
|
|
2404
|
+
Three installed-tool cases cover main advancing after two independent candidates are prepared.
|
|
2405
|
+
One publishes through the normal Controller lease and installed Finalizer; the other's lease
|
|
2406
|
+
request enters `NEEDS_REPREPARE`, clears its old validation and prepared receipt, and leaves its
|
|
2407
|
+
original intent, queue position, source and scope fixed without granting publication authority.
|
|
2408
|
+
An `integration-refresh` request with the old record CAS refuses without changing the public
|
|
2409
|
+
intent, candidate, source, lease or remote main. A current refresh rebuilds that same candidate
|
|
2410
|
+
on the published main; publication still refuses until fresh fixture content/Git review is bound.
|
|
2411
|
+
The new prepared receipt then permits normal Finalizer publication, preserving both changes and
|
|
2412
|
+
closing all published sources and candidates with complete formal release proof. No authority
|
|
2413
|
+
files are edited. The gate independently checks `integration_publication_execution_version`.
|
|
2414
|
+
|
|
2415
|
+
Three installed-tool cases cover the repository's single main publication lease and its real TTL.
|
|
2416
|
+
Two independent prepared candidates compete: an occupied lease refuses the second candidate
|
|
2417
|
+
without changing the public allocations, intents, lease or live main. After a two-second lease
|
|
2418
|
+
expires, its Finalizer request refuses before push; a replacement lease can publish while the
|
|
2419
|
+
old identity remains fenced without disturbing it. A separate fifteen-second lease uses the
|
|
2420
|
+
installed Finalizer's existing crash-after-push hook. Once that lease really expires, an already
|
|
2421
|
+
published candidate refuses replacement and recovers twice with the exact original lease/run/
|
|
2422
|
+
candidate identity, verified remote main and no repeated push. Normal completion closes the
|
|
2423
|
+
published source and candidate with complete formal proof. Fixture cleanup settles only its own
|
|
2424
|
+
remaining lease before guarded abort/release; no clock or private authority files are patched.
|
|
2425
|
+
|
|
2426
|
+
Three more installed-tool cases refuse a wrong run on the current publication lease, a dirty
|
|
2427
|
+
candidate after leasing, and separate fetch/push endpoints on two disposable local bare remotes.
|
|
2428
|
+
The real Finalizer returns `LEASE_MISMATCH`, `PUBLICATION_EVIDENCE_MISMATCH` and `INVALID_REMOTE`
|
|
2429
|
+
respectively. Refusals preserve the complete public allocations, intents and lease, all local refs
|
|
2430
|
+
and remote refs; neither endpoint changes. Each fixture restores only its own file or remote
|
|
2431
|
+
configuration fault before normal completion and cleanup. The original lease, run, candidate and
|
|
2432
|
+
validation then publish normally, with complete source and candidate release proof.
|
|
2433
|
+
|
|
2434
|
+
Two installed-tool cases recheck remote facts after publication leasing. An independent disposable
|
|
2435
|
+
clone advances main through the installed Finalizer's normal branch publication path; the old
|
|
2436
|
+
lease refuses `PUBLICATION_REMOTE_DRIFT` without changing public authority or any refs. Public
|
|
2437
|
+
lease abort, stale-base detection, refresh and fresh validation then publish both changes on the
|
|
2438
|
+
original intent with a new lease, without rewinding main. Another case temporarily relocates only
|
|
2439
|
+
its owned local bare remote. Finalizer refuses `PUBLICATION_REMOTE_UNAVAILABLE`; actual refs at
|
|
2440
|
+
the relocated fixture and public authority remain unchanged. Restoring the endpoint permits normal
|
|
2441
|
+
publication with the original lease. Both cases verify complete source/candidate release proof;
|
|
2442
|
+
fixture failure cleanup restores the endpoint and settles only its own exact lease.
|
|
2443
|
+
|
|
2444
|
+
Three installed public-session cases refuse completion before authorization and an authorized
|
|
2445
|
+
consumer's success claim without a remote push, and disconnect before authorization while an
|
|
2446
|
+
installed status reader waits for the repository lock. The blocked sessions release the lock
|
|
2447
|
+
without changing public authority or Git refs. Each case then publishes through the real Finalizer
|
|
2448
|
+
using its original lease, run and candidate, retaining the intent identity and complete release proof.
|
|
2449
|
+
|
|
2450
|
+
```bash
|
|
2451
|
+
wc_controller_version=$(uv run --locked --python 3.12 --no-env-file python -c \
|
|
2452
|
+
'import tomllib; from pathlib import Path; print(tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"])')
|
|
2453
|
+
|
|
2454
|
+
uv run --locked --python 3.12 --no-env-file python scripts/check-cross-tool \
|
|
2455
|
+
--work-dir /absolute/path/to/a/new/cross-tool-output-directory \
|
|
2456
|
+
--controller /absolute/path/to/versioned/installed/worktree-controller \
|
|
2457
|
+
--controller-version "$wc_controller_version" \
|
|
2458
|
+
--git-finalizer /absolute/path/to/git-finalize --finalizer-version 1.8.18 \
|
|
2459
|
+
--retirement-companion /absolute/path/to/git-finalize-retirement-plan.py
|
|
2460
|
+
```
|
|
2461
|
+
|
|
2462
|
+
Run the example from this checkout's root. It reads the expected Controller version from
|
|
2463
|
+
`pyproject.toml`; the selected installed Controller must match. The command verifies both installed
|
|
2464
|
+
versions and the Controller CLI/storage contracts and selected capabilities before running tests.
|
|
2465
|
+
|
|
2466
|
+
To add the twenty real-process rollout cases (seven activation, eight rollback and five automatic restoration), supply
|
|
2467
|
+
`--rollout-previous-controller /absolute/path/to/previous/installed/worktree-controller`.
|
|
2468
|
+
Both Controller entries must come from original commit-pinned installations; the activation
|
|
2469
|
+
fixture verifies their package bytes, receipts and release ancestry against frozen Git commits.
|
|
2470
|
+
The extended selection requires all sixty-five cases to pass without skips and records both pins.
|
|
2471
|
+
|
|
2472
|
+
Run either selection in a PID namespace to isolate process inventory from unrelated host
|
|
2473
|
+
activity. Prefix the command above with
|
|
2474
|
+
`unshare --user --map-current-user --mount --pid --fork --mount-proc`; the script uses the existing
|
|
2475
|
+
project reaper as PID 1. The current UID remains unprivileged. Each case has an independent HOME,
|
|
2476
|
+
temporary directory and active pointer. The frozen gate and installed CLI responses run normally;
|
|
2477
|
+
process barriers control transaction creation and completion before or after the staged check.
|
|
2478
|
+
Fixtures generate journals through the installed CLI and finish interrupted release through its
|
|
2479
|
+
original request. Cases verify a complete repeated gate, consumer rechecks, permanent recovery
|
|
2480
|
+
refusal, pointer drift, churn timeout and termination of only the attempt's process group.
|
|
2481
|
+
Rollback cases use the older target's own frozen gate and installed CLI. They cover successful
|
|
2482
|
+
rollback, a real mutation-lock refusal followed by a complete gate retry, the older gate's permanent
|
|
2483
|
+
transaction-set refusal, recovery errors, current consumer minimums, ancestor checks, pointer drift
|
|
2484
|
+
and timeout cleanup. A rollback acceptance pair must include an older gate that reports transaction
|
|
2485
|
+
set mismatches as `CONTROLLER_TRANSACTION_UNFINISHED`; it preserves that version's refusal contract.
|
|
2486
|
+
No host active pointer is changed by these fixtures.
|
|
2487
|
+
|
|
2488
|
+
Automatic restoration cases hide only the disposable fixture's Git directory during the new
|
|
2489
|
+
runtime's post-check, observing the installed CLI's real failure before restoring the directory.
|
|
2490
|
+
They then verify the previous runtime's exact frozen gate, successful restoration and its post-check,
|
|
2491
|
+
refusal after a new release-recovery error or consumer capability minimum, fresh review of a changed
|
|
2492
|
+
published compatibility catalog, and refusal to overwrite another activator's pointer. The previous
|
|
2493
|
+
runtime must lack `canonical_refresh_version` while the new runtime advertises version 1.
|
|
2494
|
+
|
|
2495
|
+
The Controller 1.22.4 / Git Finalizer 1.8.18 wheel pair was verified on 2026-10-13: all seven cases
|
|
2496
|
+
passed with zero skips, using CLI contract 54, storage layout 4 and continuation capability 2.
|
|
2497
|
+
The Controller 1.22.3 / Git Finalizer 1.8.18 wheel pair was verified on 2026-10-13: all seven cases
|
|
2498
|
+
passed with zero skips, using CLI contract 54, storage layout 4 and continuation capability 2.
|
|
2499
|
+
The Controller 1.22.1 / Git Finalizer 1.8.18 baseline was verified on 2026-10-12: all seven cases
|
|
2500
|
+
passed with zero skips, using CLI contract 54, storage layout 4 and continuation capability 2.
|
|
2501
|
+
The historical Controller 1.21.0 / Git Finalizer 1.8.18 baseline was verified on 2026-10-12: all three cases
|
|
2502
|
+
passed with zero skips, using CLI contract 53 and storage layout 4.
|
|
2503
|
+
The historical Controller 1.20.4 / Git Finalizer 1.8.18 baseline was verified on 2026-10-12: all three
|
|
2504
|
+
cases passed with zero skips, using CLI contract 52 and storage layout 4. The Controller 1.20.3 /
|
|
2505
|
+
Git Finalizer 1.8.18 baseline was verified on 2026-10-11 with the same three passes and zero skips.
|
|
2506
|
+
Use a versioned installed Controller entry point rather than a HOME-dependent
|
|
2507
|
+
launcher: fixtures have an isolated HOME and do not inherit caller credentials or proxy settings.
|
|
2508
|
+
Add `--offline` when dependencies are cached. The output directory must be new.
|
|
2509
|
+
|
|
2510
|
+
The cases cover publication interrupted after push, recovery and repeated execution, retirement
|
|
2511
|
+
proof drift rejection, occupied retirement authorization, and real canonical-transition-backed
|
|
2512
|
+
local deletion with idempotent completion. They use disposable repositories
|
|
2513
|
+
and local bare remotes, with the selected installed Controller serving the consumer's public RPCs
|
|
2514
|
+
and this matching-version checkout creating fixtures. Success requires all forty-five cases to pass
|
|
2515
|
+
with zero skips. `pytest.xml` and `cross-tool.json` retain the selected versions, contracts, tool
|
|
2516
|
+
entry-point hashes and test result; entry-point drift during the run also fails the gate.
|
|
2517
|
+
|
|
2518
|
+
### CLI performance baseline
|
|
2519
|
+
|
|
2520
|
+
Run `uv run --locked python scripts/benchmark-cli --work-dir /absolute/new-output
|
|
2521
|
+
--controller before=/absolute/pinned/worktree-controller
|
|
2522
|
+
--controller after=/absolute/pinned/worktree-controller --repeats 7` as one command.
|
|
2523
|
+
The entry points should belong to isolated installed wheels. Resolve a runtime's versioned
|
|
2524
|
+
entry point before passing it; home-dependent launchers do not work with the fixture HOME.
|
|
2525
|
+
The output directory must be new. The script creates only synthetic fixture repositories,
|
|
2526
|
+
uses canonical metadata serializers and release-plan hashing, and rejects diagnostic findings
|
|
2527
|
+
or different JSON outputs (apart from `tool_version`). It does not contact remotes.
|
|
2528
|
+
|
|
2529
|
+
Each invocation is a fresh process. `first_call` is the first measured call after fixture setup
|
|
2530
|
+
and version inspection; it is not a storage-cold measurement. Repeated calls use warm OS caches
|
|
2531
|
+
and bytecode caches isolated under the output directory, avoiding differences from precompiled
|
|
2532
|
+
installed packages. The script records elapsed wall time, GNU Time peak RSS and stdout bytes
|
|
2533
|
+
in `results.json`, alongside all samples. Setup and output comparison are outside the timing.
|
|
2534
|
+
It requires Linux, Python 3.12 and GNU `/usr/bin/time`. Fixture generation measures no writer
|
|
2535
|
+
durability, and no host cache is flushed.
|
|
2536
|
+
|
|
2537
|
+
The 2026-10-01 baseline used Python 3.12.13 on Linux x86_64 with an AMD Ryzen 9 9950X,
|
|
2538
|
+
seven paired repeats and one live checkout. Each synthetic closed release has an allocation
|
|
2539
|
+
record, publication receipt, prepared receipt, release plan and release receipt. Publication
|
|
2540
|
+
receipts remain `REMOTE_VERIFIED` after closure, exercising immutable release-proof joins.
|
|
2541
|
+
History queries select the shared authority with `--limit 10`. Values below are medians in
|
|
2542
|
+
milliseconds, shown as **1.18.2 / 1.18.3**:
|
|
2543
|
+
|
|
2544
|
+
| Closed releases | status | doctor | lifecycle-history |
|
|
2545
|
+
| ---: | ---: | ---: | ---: |
|
|
2546
|
+
| 0 | 61.8 / 62.2 | 60.2 / 62.1 | 62.4 / 62.5 |
|
|
2547
|
+
| 100 | 74.3 / 73.6 | 72.0 / 74.1 | 77.3 / 75.5 |
|
|
2548
|
+
| 1,000 | 175.9 / 175.6 | 194.4 / 174.6 | 203.0 / 200.1 |
|
|
2549
|
+
| 5,000 | 681.6 / 658.0 | 952.0 / 658.3 | 698.5 / 670.6 |
|
|
2550
|
+
|
|
2551
|
+
At 5,000 releases, doctor improves by about 31%; its median peak RSS changes from 62.5 to
|
|
2552
|
+
63.2 MiB. Status and history code is unchanged; their small timing differences are run variation.
|
|
2553
|
+
All three JSON outputs match across versions after excluding `tool_version`. The 10-entry
|
|
2554
|
+
history page is about 55 KiB at both 1,000 and 5,000 releases. These are local synthetic
|
|
2555
|
+
measurements, not portable latency guarantees; all commands still load and validate the full
|
|
2556
|
+
metadata snapshot, so time and memory continue to grow with history.
|
|
2557
|
+
|
|
2558
|
+
The 2026-10-06 comparison of installed 1.19.8, 1.19.9 and the 1.19.10 wheel used the same
|
|
2559
|
+
fixture, Python 3.12.13 and seven paired repeats. Values below are median milliseconds,
|
|
2560
|
+
shown as **1.19.8 / 1.19.9 / 1.19.10**:
|
|
2561
|
+
|
|
2562
|
+
| Closed releases | status | doctor | lifecycle-history |
|
|
2563
|
+
| ---: | ---: | ---: | ---: |
|
|
2564
|
+
| 0 | 100.7 / 103.4 / 108.6 | 152.8 / 172.5 / 136.6 | 118.3 / 112.9 / 110.7 |
|
|
2565
|
+
| 100 | 150.4 / 151.4 / 128.2 | 99.3 / 110.6 / 103.2 | 101.1 / 102.0 / 108.9 |
|
|
2566
|
+
| 1,000 | 272.0 / 345.6 / 282.3 | 264.0 / 281.0 / 248.0 | 271.5 / 296.0 / 259.4 |
|
|
2567
|
+
| 5,000 | 1049.6 / 1227.0 / 909.7 | 849.8 / 1083.9 / 908.7 | 958.2 / 1231.9 / 896.3 |
|
|
2568
|
+
|
|
2569
|
+
At 5,000 releases, median peak RSS in MiB was:
|
|
2570
|
+
|
|
2571
|
+
| Version | status | doctor | lifecycle-history |
|
|
2572
|
+
| --- | ---: | ---: | ---: |
|
|
2573
|
+
| 1.19.8 | 64.7 | 64.7 | 65.3 |
|
|
2574
|
+
| 1.19.9 | 103.0 | 103.8 | 104.6 |
|
|
2575
|
+
| 1.19.10 | 64.2 | 65.0 | 65.2 |
|
|
2576
|
+
|
|
2577
|
+
The normalized JSON outputs match for all sizes, commands and versions. Profiling confirms
|
|
2578
|
+
that each query loads one repository snapshot. The 1.19.9 command-scoped decode cache retained
|
|
2579
|
+
25,001 decoded files at 5,000 releases without reusing them; 1.19.10 runs these three queries
|
|
2580
|
+
on the uncached read path and preserves full validation. Other commands keep identity-aware
|
|
2581
|
+
decode reuse within their invocation. Compared with 1.19.9, this run reduces large-query
|
|
2582
|
+
median latency by 16–27% and peak RSS by about 38%. Small timings and the differences from
|
|
2583
|
+
1.19.8 vary on the shared host; these measurements do not establish portable latency bounds.
|
|
2584
|
+
|
|
2585
|
+
Add `--mode execution-admit --sizes 0 100 1000 --repeats 7` to measure execution admission
|
|
2586
|
+
with the same versioned installed entry points. Each sample uses a new isolated repository,
|
|
2587
|
+
HOME and resource ledger, containing the requested number of synthetic lost-exit host
|
|
2588
|
+
reservations and one actual live writer. The three cases cover immediately available admission,
|
|
2589
|
+
memory waiting that recovers a finished reservation, and an old run's identity recovery.
|
|
2590
|
+
The memory case includes one extra blocking reservation. Identity recovery deliberately measures
|
|
2591
|
+
the expected `EXECUTION_AUTHORITY_CHANGED` refusal after retaining the unfinished task; an
|
|
2592
|
+
untimed formal resume and admission must then succeed. No task completion is inferred.
|
|
2593
|
+
|
|
2594
|
+
This mode starts the selected entry point's Python interpreter and installed `CLI.main` in a
|
|
2595
|
+
fresh process with fixed memory and PSI probes; Git, metadata, locking and durable writes are
|
|
2596
|
+
real. Timing includes the fixture worker's bootstrap, but excludes fixture setup and acceptance
|
|
2597
|
+
postchecks. Bytecode caches are shared only within the output directory, and host caches are
|
|
2598
|
+
not flushed. Fixtures never use the caller's resource ledger, remotes, credentials or proxies.
|
|
2599
|
+
Bulk synthetic history is setup, not evidence of writer durability. Results retain entry-point
|
|
2600
|
+
and package hashes, measured CLI responses, wall time, peak RSS and postchecks for live-writer,
|
|
2601
|
+
scope, publication, request-idempotency and memory-budget invariants. `status=PASS` is written
|
|
2602
|
+
only after every sample passes and the installed identities are unchanged.
|
|
2603
|
+
|
|
2604
|
+
Unlike query mode, this mode does not require identical JSON across versions: 1.19.2 intentionally
|
|
2605
|
+
keeps unrelated finished reservations on its available path, while 1.19.1 reconciles them first.
|
|
2606
|
+
Both states are reported and checked; a lower latency does not count as successful recovery.
|
|
2607
|
+
From 1.19.6, automatic recovery batches proven bounded host exits into one atomic ledger write.
|
|
2608
|
+
Each row retains the same target, runtime-chain and process-group checks; repository recovery
|
|
2609
|
+
still runs separately, outside the host ledger lock. Explicit recovery interfaces are unchanged.
|
|
2610
|
+
From 1.19.7, `admission-queue --all-repositories --apply` also uses that batch while preserving
|
|
2611
|
+
the ordered per-request outcomes. A failed batch or an unproven row still uses ordinary diagnostics;
|
|
2612
|
+
repository-selected and dry-run passes leave host reservations unchanged. Normal host clear and
|
|
2613
|
+
typed dead-holder recovery retain their separate proof requirements.
|
|
2614
|
+
|
|
2615
|
+
Unrelated host process churn can cause a real procfs visibility refusal during old-run recovery.
|
|
2616
|
+
Such a sample stops the run and retains `recovery-refusals.json` and `refusal-inspect.json`; it
|
|
2617
|
+
must not be counted as a successful timing. Where unprivileged namespaces are supported, prefix
|
|
2618
|
+
the benchmark command with `unshare --user --map-current-user --pid --fork --mount-proc` to
|
|
2619
|
+
isolate the fixture processes while retaining real procfs checks. This uses the existing Linux
|
|
2620
|
+
`unshare` command, changes only that test process's namespaces, and grants no host privileges.
|
|
2621
|
+
Results record the PID, mount and user namespace identities.
|
|
2622
|
+
Use `--admission-cases available` (or either recovery case) to bound a focused experiment.
|
|
2623
|
+
`--timeout` limits each invocation; a timeout stops the run, terminates its process group and leaves
|
|
2624
|
+
the output at `RUNNING`, never `PASS`. Preserve that censored observation separately from
|
|
2625
|
+
completed paired samples before choosing a smaller scale or a longer explicit time budget.
|
|
2626
|
+
|
|
2627
|
+
The 2026-10-06 isolated PID-namespace baseline used Python 3.12.13 on Linux x86_64 and
|
|
2628
|
+
the installed 1.19.1 (`827c71a7`) / 1.19.2 (`094891c2`) packages. Seven paired repeats,
|
|
2629
|
+
with fixture setup and acceptance outside timing, produced these medians in milliseconds:
|
|
2630
|
+
|
|
2631
|
+
| Lost-exit history | Available 1.19.1 / 1.19.2 | Memory recovery 1.19.1 / 1.19.2 | Identity recovery 1.19.1 / 1.19.2 |
|
|
2632
|
+
| ---: | ---: | ---: | ---: |
|
|
2633
|
+
| 0 | 95.7 / 101.9 | 108.3 / 164.3 | 183.7 / 170.9 |
|
|
2634
|
+
| 100 | 1026.7 / 121.4 | 939.4 / 1202.4 | 982.3 / 997.8 |
|
|
2635
|
+
|
|
2636
|
+
At 100 entries, immediately available admission improves by about 88%. Waiting pays for an
|
|
2637
|
+
additional admission check; recovery is not generally faster. All completed samples preserve
|
|
2638
|
+
the live writer, scope, publication state, request identity and exact memory budget.
|
|
2639
|
+
|
|
2640
|
+
The 1,000-entry paired experiment stopped at its 60-second limit: one 1.19.1 available call
|
|
2641
|
+
completed in 56.4 seconds and the next timed out. There is no completed 1.19.1 paired median
|
|
2642
|
+
at that scale. A separate 1.19.2 run with a 180-second limit, one repeat plus a first call,
|
|
2643
|
+
passed all three cases: available 166.5 ms, memory recovery 54,922.9 ms, identity recovery
|
|
2644
|
+
49,620.3 ms (the repeat samples, not seven-repeat medians). Large recovery backlogs therefore
|
|
2645
|
+
remain expensive. Host-procfs refusals and the timed-out comparison are retained separately
|
|
2646
|
+
and are excluded from successful timings. These are synthetic local results, not portable
|
|
2647
|
+
latency guarantees or evidence that real host procfs visibility will always be clear.
|
|
2648
|
+
|
|
2649
|
+
The 2026-10-06 isolated PID-namespace run compared installed 1.19.8, 1.19.9 and the 1.19.10
|
|
2650
|
+
wheel with seven paired repeats and a 30-second per-invocation limit. All three cases completed
|
|
2651
|
+
at every size. Median milliseconds are shown as **1.19.8 / 1.19.9 / 1.19.10**:
|
|
2652
|
+
|
|
2653
|
+
| Lost-exit history | Available | Memory recovery | Identity recovery |
|
|
2654
|
+
| ---: | ---: | ---: | ---: |
|
|
2655
|
+
| 0 | 97.7 / 96.1 / 96.3 | 117.0 / 113.6 / 136.9 | 188.6 / 178.2 / 223.2 |
|
|
2656
|
+
| 100 | 100.6 / 107.5 / 105.0 | 139.1 / 132.6 / 135.8 | 216.7 / 215.7 / 248.1 |
|
|
2657
|
+
| 1,000 | 146.3 / 147.8 / 146.7 | 472.4 / 345.0 / 378.4 | 359.8 / 400.6 / 392.8 |
|
|
2658
|
+
|
|
2659
|
+
Every measured sample passed the live-writer, scope, publication, request-idempotency and exact
|
|
2660
|
+
memory-budget checks, including guarded resume after identity recovery. The query-cache change
|
|
2661
|
+
does not change these paths; their timings vary across the shared-host run and show no consistent
|
|
2662
|
+
latency improvement. These fixtures contain lost-exit host reservations and a small repository;
|
|
2663
|
+
they do not measure admission against a large repository metadata history.
|
|
2664
|
+
|
|
2665
|
+
For either admission mode, add `--repo-sizes 0 1000 5000` to seed closed repository releases
|
|
2666
|
+
independently of `--sizes 0 1000`, which still controls lost-exit host reservations. The
|
|
2667
|
+
Cartesian product measures each axis and their interaction. Release history is seeded after
|
|
2668
|
+
writer setup and before timing, using the same serializers and plan hashing as query mode.
|
|
2669
|
+
Every sample also verifies that all closed records and publication/prepared/release proofs
|
|
2670
|
+
remain unchanged.
|
|
2671
|
+
|
|
2672
|
+
Use `--mode session-admit` to measure the complete new-session CLI path: canonical plan,
|
|
2673
|
+
durable checkout acquisition and execution admission. Its default cases are `available` and
|
|
2674
|
+
`memory-recovery`; checkout planning must succeed before the memory case reaches execution
|
|
2675
|
+
waiting. Existing allocations and identity recovery use the existing fallback/step-by-step
|
|
2676
|
+
path, so `identity-recovery` is accepted only in `execution-admit` mode. Session samples verify
|
|
2677
|
+
that a repeated start returns fallback with the same allocation and leaves the snapshot and
|
|
2678
|
+
ledger unchanged. Untimed checks then replay execution admission and bind an actual live
|
|
2679
|
+
fixture process, requiring its exact holder, writer lease, runtime binding and `WRITE_ALLOWED`
|
|
2680
|
+
guard. The existing scoped writer and exact memory budget remain acceptance gates.
|
|
2681
|
+
|
|
2682
|
+
Add `--profile` for one extra fresh-fixture diagnostic call per version/configuration. These
|
|
2683
|
+
calls use `cProfile` around installed `CLI.main` and record snapshot-load counts, metadata
|
|
2684
|
+
decoder counts and the largest cumulative costs in `profile_diagnostic`. They are excluded
|
|
2685
|
+
from normal latency and RSS medians. Profiles cover the CLI main thread; inventory worker
|
|
2686
|
+
threads and subprocess internals are excluded. All admission runs persist `status=RUNNING` before the
|
|
2687
|
+
first sample; a timeout or failed invariant never becomes a passing timing, and fixture
|
|
2688
|
+
holders are stopped on both success and failure. These remain local synthetic measurements,
|
|
2689
|
+
with fixed memory/PSI probes, warm storage caches and no caller ledger or credentials.
|
|
2690
|
+
The tracked fixture tree contains only its policy file, so these measurements cover metadata
|
|
2691
|
+
and host-history growth, not the checkout cost of a large source tree or executor startup.
|
|
2692
|
+
|
|
2693
|
+
The 2026-10-07 complete new-session comparison used installed 1.19.8 (`bc619da`) and
|
|
2694
|
+
1.19.10 (`7da723c`), Python 3.12.13, isolated PID namespaces, alternating version order,
|
|
2695
|
+
seven paired repeats and a separate first call. All 192 timed calls passed history, live
|
|
2696
|
+
writer, scope, publication, fallback, holder/lease, request-identity and memory-budget checks.
|
|
2697
|
+
Median milliseconds are shown as **1.19.8 / 1.19.10**:
|
|
2698
|
+
|
|
2699
|
+
| Closed repository releases | Lost-exit host reservations | Session available | Session memory recovery |
|
|
2700
|
+
| ---: | ---: | ---: | ---: |
|
|
2701
|
+
| 0 | 0 | 293 / 384 | 427 / 363 |
|
|
2702
|
+
| 0 | 1,000 | 472 / 466 | 752 / 697 |
|
|
2703
|
+
| 1,000 | 0 | 1,341 / 718 | 1,506 / 1,081 |
|
|
2704
|
+
| 1,000 | 1,000 | 1,502 / 928 | 1,953 / 1,508 |
|
|
2705
|
+
| 5,000 | 0 | 5,070 / 3,051 | 7,652 / 4,837 |
|
|
2706
|
+
| 5,000 | 1,000 | 6,406 / 4,554 | 7,623 / 5,400 |
|
|
2707
|
+
|
|
2708
|
+
At 5,000 releases, session medians improve by 29–40%; with 1,000 host reservations,
|
|
2709
|
+
peak RSS medians are 135.7 / 120.1 MiB (available) and 135.5 / 122.9 MiB (memory
|
|
2710
|
+
recovery). A separate one-repeat profiling pilot at 1,000 releases sees five snapshot loads
|
|
2711
|
+
in either version, but each immutable prepared/publication/release receipt type is decoded
|
|
2712
|
+
5,000 / 1,000 times.
|
|
2713
|
+
Its diagnostic timings are excluded from the seven-repeat table. Empty-history results vary
|
|
2714
|
+
on this shared host and show no consistent speedup.
|
|
2715
|
+
|
|
2716
|
+
A separate seven-repeat `execution-admit` comparison at 5,000 releases and 1,000 host
|
|
2717
|
+
reservations exposes the single-snapshot cost: available admission takes 1,048 / 1,448 ms
|
|
2718
|
+
and 72.3 / 118.2 MiB. Memory recovery takes 3,104 / 2,978 ms; identity recovery takes
|
|
2719
|
+
7,809 / 5,358 ms, including its expected refusal and untimed guarded resume. All 48 timed
|
|
2720
|
+
calls pass their invariants. The initial execution profiling output was overwritten by an
|
|
2721
|
+
untimed replay; those diagnostics are excluded. The harness now captures the initial profile
|
|
2722
|
+
and disables profiling for subsequent acceptance calls.
|
|
2723
|
+
|
|
2724
|
+
The subsequent paired 1.19.10 / 1.19.11 wheel comparison uses the same 5,000-release,
|
|
2725
|
+
1,000-reservation configuration, seven repeats and one separate first call per case. All 48
|
|
2726
|
+
timed calls and six separate profile calls pass their acceptance checks:
|
|
2727
|
+
|
|
2728
|
+
| Standalone execution case | Median milliseconds 1.19.10 / 1.19.11 | Median peak RSS MiB 1.19.10 / 1.19.11 |
|
|
2729
|
+
| --- | ---: | ---: |
|
|
2730
|
+
| Available | 1,473 / 976 | 118.7 / 71.5 |
|
|
2731
|
+
| Memory recovery | 3,274 / 2,646 | 123.2 / 121.8 |
|
|
2732
|
+
| Identity recovery | 6,561 / 6,445 | 124.4 / 124.4 |
|
|
2733
|
+
|
|
2734
|
+
Available admission reduces median latency by 34% and RSS by 40% in this run. Recovery RSS
|
|
2735
|
+
is similar. Recovery samples vary substantially on the shared host; their median reductions
|
|
2736
|
+
are not evidence of a reliable recovery speedup. Profiling confirms one snapshot for available
|
|
2737
|
+
admission, three for memory recovery and eight for identity recovery. The recovery candidate
|
|
2738
|
+
decodes each unchanged receipt type 10,000 times instead of 5,000 because caching starts after
|
|
2739
|
+
the first attempt. That extra decoding is the cost of avoiding cache retention on the ordinary
|
|
2740
|
+
path; profiles are diagnostic and their instrumented timings are excluded from these medians.
|
|
2741
|
+
|
|
2742
|
+
The 2026-10-07 complete `session-admit` comparison of installed 1.19.13 (`c59e0cd`) and the
|
|
2743
|
+
1.19.14 wheel uses Python 3.12.13, isolated PID namespaces, alternating version order, seven
|
|
2744
|
+
paired repeats and a separate first call. All 192 timed calls and 24 separate profile calls
|
|
2745
|
+
pass history, live-writer, scope, publication, fallback, holder/lease, request-identity and exact
|
|
2746
|
+
memory-budget checks. Median milliseconds are shown as **1.19.13 / 1.19.14**:
|
|
2747
|
+
|
|
2748
|
+
| Closed repository releases | Lost-exit host reservations | Session available | Session memory recovery |
|
|
2749
|
+
| ---: | ---: | ---: | ---: |
|
|
2750
|
+
| 0 | 0 | 233 / 254 | 218 / 215 |
|
|
2751
|
+
| 0 | 1,000 | 434 / 413 | 678 / 601 |
|
|
2752
|
+
| 1,000 | 0 | 664 / 494 | 1,065 / 685 |
|
|
2753
|
+
| 1,000 | 1,000 | 875 / 961 | 2,278 / 1,080 |
|
|
2754
|
+
| 5,000 | 0 | 3,193 / 1,863 | 4,861 / 3,222 |
|
|
2755
|
+
| 5,000 | 1,000 | 4,734 / 2,188 | 6,460 / 3,262 |
|
|
2756
|
+
|
|
2757
|
+
At 5,000 releases, median latency falls by 34–54% in this run. With 1,000 host reservations,
|
|
2758
|
+
median peak RSS changes from 120.4 / 99.3 MiB (available) and 122.9 / 102.6 MiB (memory recovery).
|
|
2759
|
+
Profiling confirms five / three snapshot loads on the available path and seven / five on
|
|
2760
|
+
memory recovery. Planning reuses the session's initial read-only observation; acquisition still
|
|
2761
|
+
rechecks current metadata and authority under its exclusive lock, then uses that validated
|
|
2762
|
+
repository identity. Flat historical metadata files retain the same filename order and validation
|
|
2763
|
+
while sorting by filename instead of comparing complete `Path` objects.
|
|
2764
|
+
The unchanged receipt types are still decoded once per command. These are warm-cache synthetic
|
|
2765
|
+
shared-host measurements; empty and some 1,000-release combinations fluctuate and do not show
|
|
2766
|
+
a consistent speedup. Profile timings are excluded from the medians, and the fixture still does
|
|
2767
|
+
not measure a large tracked checkout or executor startup.
|
|
2768
|
+
|
|
2769
|
+
The subsequent seven-repeat installed 1.19.14 (`cfd8f3a`) / 1.19.15 wheel comparison focuses
|
|
2770
|
+
on 5,000 closed repository releases, with zero or 1,000 lost-exit host reservations. It uses
|
|
2771
|
+
the same Python 3.12.13, isolated PID namespaces, alternating version order and separate first
|
|
2772
|
+
calls. All 64 timed calls and eight separate profile calls pass the existing history,
|
|
2773
|
+
live-writer, scope, publication, fallback, holder/lease, request-identity and memory-budget
|
|
2774
|
+
checks. Medians are shown as **1.19.14 / 1.19.15**:
|
|
2775
|
+
|
|
2776
|
+
| Lost-exit host reservations | Session case | Median milliseconds | Median peak RSS MiB |
|
|
2777
|
+
| ---: | --- | ---: | ---: |
|
|
2778
|
+
| 0 | Available | 1,572 / 1,133 | 93.9 / 82.6 |
|
|
2779
|
+
| 0 | Memory recovery | 2,012 / 1,293 | 93.6 / 83.5 |
|
|
2780
|
+
| 1,000 | Available | 1,674 / 1,307 | 99.3 / 87.6 |
|
|
2781
|
+
| 1,000 | Memory recovery | 2,258 / 1,531 | 102.4 / 90.7 |
|
|
2782
|
+
|
|
2783
|
+
Median latency falls by 22–36% and peak RSS by about 11–12% in this run. Snapshot counts stay
|
|
2784
|
+
at three (available) and five (memory recovery), and unchanged receipt types are still decoded
|
|
2785
|
+
once per command. Fresh `os.scandir` enumeration of five flat historical metadata directories
|
|
2786
|
+
avoids repeated `Path` construction and hashing while preserving filename order and all
|
|
2787
|
+
schema/identity checks. Directory entries never survive a snapshot load; cached decodes retain
|
|
2788
|
+
their existing file-identity checks and command-local lifetime.
|
|
2789
|
+
A separate three-repeat query probe makes 48 calls across empty and 5,000-release histories:
|
|
2790
|
+
`status`, `doctor` and `lifecycle-history` return matching business payloads in both versions;
|
|
2791
|
+
small timing differences do not establish a query speedup. The one-repeat admission pilot and
|
|
2792
|
+
profile diagnostics are excluded from the seven-repeat medians. These remain warm-cache,
|
|
2793
|
+
synthetic shared-host measurements with fixed pressure/memory probes, rather than latency
|
|
2794
|
+
bounds or measurements of large tracked checkouts or executor startup.
|
|
2795
|
+
|
|
2796
|
+
An installed 1.19.15 (`f309ea5`) / 1.19.16 wheel comparison measures the subsequent ASCII hex
|
|
2797
|
+
validation change on Python 3.12.13. A compiled full-match pattern replaces Python character
|
|
2798
|
+
loops for OIDs and SHA-256 digests; exact lengths, lowercase acceptance, whitespace normalization,
|
|
2799
|
+
optional values and errors remain unchanged. Every snapshot still validates all metadata.
|
|
2800
|
+
The existing benchmark uses alternating version order, separate first calls and isolated
|
|
2801
|
+
fixtures. There are 64 timed session calls (seven repeats), 16 immediately available standalone
|
|
2802
|
+
admission calls (three repeats), 96 query calls (seven repeats), and 16 additional session memory
|
|
2803
|
+
recovery calls (seven repeats) to recheck a possible regression. All admission safety checks pass;
|
|
2804
|
+
query business payloads match across versions. Medians below are **1.19.15 / 1.19.16** with
|
|
2805
|
+
5,000 closed repository releases:
|
|
2806
|
+
|
|
2807
|
+
| Command / case | Lost-exit host reservations | Median milliseconds | Median peak RSS MiB |
|
|
2808
|
+
| --- | ---: | ---: | ---: |
|
|
2809
|
+
| Session / available | 0 | 1,071 / 1,043 | 82.8 / 82.4 |
|
|
2810
|
+
| Session / memory recovery | 0 | 1,313 / 1,324 | 82.9 / 83.1 |
|
|
2811
|
+
| Session / available | 1,000 | 1,210 / 1,217 | 87.6 / 87.2 |
|
|
2812
|
+
| Session / memory recovery | 1,000 | 1,608 / 1,704 | 90.1 / 90.6 |
|
|
2813
|
+
| Standalone admission / available | 0 | 696 / 635 | 62.6 / 61.3 |
|
|
2814
|
+
| Standalone admission / available | 1,000 | 742 / 643 | 71.1 / 71.2 |
|
|
2815
|
+
| Status | 0 | 681 / 645 | 59.0 / 59.2 |
|
|
2816
|
+
| Doctor | 0 | 648 / 627 | 60.0 / 60.4 |
|
|
2817
|
+
| Lifecycle history | 0 | 704 / 656 | 60.4 / 60.7 |
|
|
2818
|
+
|
|
2819
|
+
Standalone admission improves by 9–13% in this run, with the candidate faster in all six paired
|
|
2820
|
+
repeat samples. The three large-history queries improve by 3–7%, with the candidate faster in
|
|
2821
|
+
20 of 21 paired samples. Empty-history query changes are under 10 ms and show no consistent gain.
|
|
2822
|
+
The first session matrix ranges from 3% faster to 6% slower; the separately repeated 1,000-host
|
|
2823
|
+
memory case gives 1,769 / 1,652 ms and 91.0 / 90.7 MiB. The initial regression does not repeat,
|
|
2824
|
+
but these results do not establish a general session speedup. RSS is broadly unchanged.
|
|
2825
|
+
The one-repeat pilot and eight profile diagnostics are excluded from these medians; later pilot
|
|
2826
|
+
cases overlap regression tests and are not acceptance timings. Their snapshot counts remain
|
|
2827
|
+
three / five, with unchanged receipt types decoded once per command. These are synthetic,
|
|
2828
|
+
warm-cache measurements on a shared host, with fixed memory/pressure probes for admission.
|
|
2829
|
+
They do not establish portable latency bounds or measure large tracked checkouts/executor startup.
|
|
2830
|
+
|
|
2831
|
+
The subsequent installed 1.19.16 (`aea5ee9`) / 1.19.17 wheel comparison isolates compact host
|
|
2832
|
+
ledger serialization. Python 3.12.13 and the unchanged benchmark use isolated PID namespaces,
|
|
2833
|
+
alternating version order and separate first calls. All 128 timed calls pass the existing
|
|
2834
|
+
history, live-writer, scope, publication, holder/lease, request-identity and memory-budget checks:
|
|
2835
|
+
64 session calls (seven paired repeats), 32 standalone admission calls (three paired repeats),
|
|
2836
|
+
and 32 zero-host session rechecks (seven paired repeats). With 5,000 closed repository releases,
|
|
2837
|
+
initial medians are shown as **1.19.16 / 1.19.17**:
|
|
2838
|
+
|
|
2839
|
+
| Command / case | Lost-exit host reservations | Median milliseconds | Median peak RSS MiB |
|
|
2840
|
+
| --- | ---: | ---: | ---: |
|
|
2841
|
+
| Session / available | 0 | 1,652 / 1,791 | 84.1 / 83.8 |
|
|
2842
|
+
| Session / memory recovery | 0 | 1,848 / 1,771 | 83.0 / 83.1 |
|
|
2843
|
+
| Session / available | 1,000 | 1,167 / 1,100 | 87.5 / 85.2 |
|
|
2844
|
+
| Session / memory recovery | 1,000 | 1,546 / 1,436 | 90.9 / 87.6 |
|
|
2845
|
+
| Standalone admission / available | 0 | 639 / 633 | 61.6 / 61.4 |
|
|
2846
|
+
| Standalone admission / memory recovery | 0 | 1,257 / 1,262 | 80.6 / 82.1 |
|
|
2847
|
+
| Standalone admission / available | 1,000 | 700 / 631 | 70.6 / 66.1 |
|
|
2848
|
+
| Standalone admission / memory recovery | 1,000 | 1,503 / 1,526 | 87.6 / 83.7 |
|
|
2849
|
+
|
|
2850
|
+
With 1,000 host reservations, session latency falls by 6–7%, with the candidate faster in six
|
|
2851
|
+
of seven pairs for each case. Immediately available standalone admission improves by 10%,
|
|
2852
|
+
with the candidate faster in all three pairs. Standalone memory recovery shows no latency gain;
|
|
2853
|
+
large-host median peak RSS falls by 2–4.5 MiB across these paths.
|
|
2854
|
+
The first session matrix includes a 13-second zero-host memory sample. A separate zero-host
|
|
2855
|
+
recheck gives 989 / 970 ms (available) and 1,310 / 1,280 ms (memory recovery), with peak RSS
|
|
2856
|
+
82.5 / 82.6 and 83.1 / 83.2 MiB. The initial available-path regression and extreme memory
|
|
2857
|
+
sample do not repeat; these data do not establish a general zero-host speedup. Keep both trials
|
|
2858
|
+
separate rather than pooling them.
|
|
2859
|
+
The 16-call pilot and eight additional profile calls are excluded from formal medians.
|
|
2860
|
+
The pilot also contains an eight-second memory sample; elevated shared-host I/O and memory
|
|
2861
|
+
pressure was observed around these anomalies, without establishing their cause. Profile
|
|
2862
|
+
snapshot counts stay at three / five; the 1,000-host available profile still saves the ledger
|
|
2863
|
+
four times. Only its JSON encoding changes, preserving all durable save boundaries.
|
|
2864
|
+
Measurements remain synthetic and warm-cache, with fixed memory/pressure probes and a tiny
|
|
2865
|
+
tracked checkout. They do not measure executor startup or establish portable latency bounds.
|
|
2866
|
+
|
|
2867
|
+
An installed 1.19.17 (`ab50d05`) / 1.19.18 wheel comparison isolates the optional UUID
|
|
2868
|
+
validation fast path on Python 3.12.13. The unchanged benchmark alternates versions in isolated
|
|
2869
|
+
PID namespaces and separates first calls from seven-repeat medians. All 192 timed calls pass:
|
|
2870
|
+
64 session calls, 96 query calls across empty and 5,000-release histories, and 32 session
|
|
2871
|
+
available-path rechecks. Admission checks preserve history, live writer, scope, publication,
|
|
2872
|
+
fallback, holder/lease, request identity and memory budgets; query business payloads match.
|
|
2873
|
+
With 5,000 closed repository releases, initial medians are **1.19.17 / 1.19.18**:
|
|
2874
|
+
|
|
2875
|
+
| Command / case | Lost-exit host reservations | Median milliseconds | Median peak RSS MiB |
|
|
2876
|
+
| --- | ---: | ---: | ---: |
|
|
2877
|
+
| Session / available | 0 | 1,011 / 1,039 | 82.3 / 82.2 |
|
|
2878
|
+
| Session / memory recovery | 0 | 1,244 / 1,113 | 82.8 / 82.8 |
|
|
2879
|
+
| Session / available | 1,000 | 1,039 / 1,074 | 85.0 / 85.2 |
|
|
2880
|
+
| Session / memory recovery | 1,000 | 1,606 / 1,499 | 87.3 / 87.2 |
|
|
2881
|
+
| Status | 0 | 600 / 550 | 59.4 / 59.4 |
|
|
2882
|
+
| Doctor | 0 | 583 / 558 | 60.0 / 60.1 |
|
|
2883
|
+
| Lifecycle history | 0 | 624 / 595 | 60.0 / 59.6 |
|
|
2884
|
+
|
|
2885
|
+
Memory-recovery session medians improve by 7–11%, with the candidate faster in seven of seven
|
|
2886
|
+
zero-host pairs and five of seven 1,000-host pairs. Large-history query medians improve by 4–8%,
|
|
2887
|
+
with the candidate faster in 18 of 21 pairs. Empty-history queries differ by 1–3 ms without a
|
|
2888
|
+
consistent gain, and RSS is broadly unchanged.
|
|
2889
|
+
The initial available sessions are 3% slower. A separate seven-repeat recheck gives
|
|
2890
|
+
1,042 / 1,033 ms and 82.4 / 81.7 MiB for zero host reservations (three of seven pairs faster),
|
|
2891
|
+
and 1,121 / 1,077 ms and 84.7 / 84.9 MiB for 1,000 reservations (five of seven pairs faster).
|
|
2892
|
+
The initial median regression does not repeat; these results do not establish a general
|
|
2893
|
+
available-session speedup. Keep the initial and recheck trials separate. Outliers include a
|
|
2894
|
+
6.2-second baseline and 2.1-second candidate in the initial zero-host available trial, and a
|
|
2895
|
+
1.8-second candidate in the 1,000-host recheck; their cause is not established.
|
|
2896
|
+
Eight additional profile calls are excluded from medians. Snapshot counts stay at three / five,
|
|
2897
|
+
and historical decoder counts match across versions. In the zero-host available profile,
|
|
2898
|
+
85,011 optional UUID checks take 0.193 / 0.091 cumulative seconds; these diagnostic timings
|
|
2899
|
+
are not ordinary CLI latency. Every identity still receives validation, and noncanonical UUID
|
|
2900
|
+
forms keep the original validator. Measurements are synthetic and warm-cache on a shared host,
|
|
2901
|
+
with fixed memory/pressure probes and a tiny tracked checkout. They do not measure executor
|
|
2902
|
+
startup or establish portable latency bounds.
|
|
2903
|
+
|
|
2904
|
+
The 2026-10-10 installed 1.19.22 (`a74ebcb`) / 1.19.23 wheel comparison measures reuse of the
|
|
2905
|
+
first nonempty-string normalization result on Python 3.12.13. The unchanged benchmark alternates
|
|
2906
|
+
versions in isolated PID namespaces, with separate first calls and seven paired repeats. All
|
|
2907
|
+
64 timed session calls and eight separate profile calls pass their existing history, writer,
|
|
2908
|
+
scope, publication, fallback, holder/lease, request-identity and memory-budget checks. With
|
|
2909
|
+
5,000 closed repository releases, initial medians are **1.19.22 / 1.19.23**:
|
|
2910
|
+
|
|
2911
|
+
| Session case | Lost-exit host reservations | Median milliseconds | Median peak RSS MiB | Candidate faster pairs |
|
|
2912
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
2913
|
+
| Available | 0 | 935.5 / 911.6 | 82.6 / 82.6 | 3/7 |
|
|
2914
|
+
| Memory recovery | 0 | 1,139.5 / 1,139.5 | 82.9 / 82.5 | 4/7 |
|
|
2915
|
+
| Available | 1,000 | 999.1 / 993.8 | 85.4 / 85.2 | 4/7 |
|
|
2916
|
+
| Memory recovery | 1,000 | 1,381.0 / 1,435.7 | 87.2 / 87.6 | 2/7 |
|
|
2917
|
+
|
|
2918
|
+
The initial 1,000-host memory-recovery median is 4% slower. A separate seven-pair recheck
|
|
2919
|
+
reversing the first-version order gives 1,386.5 / 1,395.7 ms and 87.1 / 87.8 MiB, with the
|
|
2920
|
+
candidate faster in five of seven pairs. All 16 timed calls and two separate profile calls
|
|
2921
|
+
pass. Keep the initial and recheck trials separate; these results do not establish a general
|
|
2922
|
+
session-latency improvement or a repeatable 4% regression. Snapshot counts remain three / five,
|
|
2923
|
+
and unchanged receipt decoder counts match across versions.
|
|
2924
|
+
|
|
2925
|
+
A separate unprofiled function experiment uses eight fixed valid strings, 800,000 calls per
|
|
2926
|
+
sample and eleven alternating pairs. Median time is 46.0 / 40.9 ms, with the candidate faster
|
|
2927
|
+
in all eleven pairs (11% lower); 21 absence, type, Unicode-whitespace and NUL cases return the
|
|
2928
|
+
same text or error. The four initial profiles show 26–30% lower cumulative string-validation
|
|
2929
|
+
cost at 325,099 / 325,131 calls per command. Profiles are instrumented diagnostics excluded
|
|
2930
|
+
from CLI medians; the isolated function result is not an end-to-end latency claim.
|
|
2931
|
+
|
|
2932
|
+
Two seven-pair query trials each pass all 96 timed calls, with identical business payloads
|
|
2933
|
+
at zero and 5,000 releases. At 5,000 releases, medians in milliseconds are:
|
|
2934
|
+
|
|
2935
|
+
| Command | Initial 1.19.22 / 1.19.23 | Recheck 1.19.22 / 1.19.23 |
|
|
2936
|
+
| --- | ---: | ---: |
|
|
2937
|
+
| Status | 492.5 / 482.5 | 477.1 / 479.2 |
|
|
2938
|
+
| Doctor | 503.6 / 499.6 | 503.3 / 513.4 |
|
|
2939
|
+
| Lifecycle history | 512.5 / 519.0 | 529.1 / 533.9 |
|
|
2940
|
+
|
|
2941
|
+
Initial empty-history doctor and lifecycle-history medians differ by 4–5 ms; the reversed-order
|
|
2942
|
+
recheck is faster for the candidate in all three empty-history queries. Query gains are not
|
|
2943
|
+
consistent across trials. RSS differences stay below 1 MiB in these configurations. The measured
|
|
2944
|
+
benefit is reduced duplicate normalization work; schema validation, errors, snapshot freshness,
|
|
2945
|
+
decode-cache lifetime and durable writes are unchanged. Measurements remain synthetic and
|
|
2946
|
+
warm-cache on a shared host, with fixed pressure probes and a tiny tracked checkout, and do not
|
|
2947
|
+
establish portable latency bounds. The initial profiling pilot is excluded from formal medians.
|
|
2948
|
+
|
|
2949
|
+
The 2026-10-10 installed 1.19.23 (`bb0b90f`) / pre-integration 1.19.24 candidate wheel comparison measures command-local
|
|
2950
|
+
reuse of validated historical mappings. The unchanged benchmark uses Python 3.12.13, isolated
|
|
2951
|
+
PID namespaces, alternating version order, separate first calls and seven paired repeats.
|
|
2952
|
+
All 64 initial timed session calls and eight separate profile calls pass the existing history,
|
|
2953
|
+
writer, scope, publication, fallback, holder/lease, request-identity and budget checks. With
|
|
2954
|
+
5,000 closed repository releases, medians are **1.19.23 / 1.19.24**:
|
|
2955
|
+
|
|
2956
|
+
| Session case | Lost-exit host reservations | Median milliseconds | Median peak RSS MiB | Candidate faster pairs |
|
|
2957
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
2958
|
+
| Available | 0 | 904.6 / 904.6 | 81.9 / 85.6 | 3/7 |
|
|
2959
|
+
| Memory recovery | 0 | 1,119.0 / 1,042.9 | 82.4 / 86.0 | 6/7 |
|
|
2960
|
+
| Available | 1,000 | 969.4 / 1,016.8 | 85.4 / 87.5 | 2/7 |
|
|
2961
|
+
| Memory recovery | 1,000 | 1,355.8 / 1,281.3 | 86.9 / 90.0 | 6/7 |
|
|
2962
|
+
|
|
2963
|
+
The initial 1,000-host available median is 4.9% slower. A separate seven-pair recheck reverses
|
|
2964
|
+
the first-version order and passes all 32 timed calls and four separate profile calls:
|
|
2965
|
+
|
|
2966
|
+
| Session case | Lost-exit host reservations | Median milliseconds | Median peak RSS MiB | Candidate faster pairs |
|
|
2967
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
2968
|
+
| Available | 1,000 | 996.4 / 951.7 | 84.1 / 86.9 | 5/7 |
|
|
2969
|
+
| Memory recovery | 1,000 | 1,379.1 / 1,331.7 | 87.1 / 91.0 | 5/7 |
|
|
2970
|
+
|
|
2971
|
+
Keep the trials separate: ordinary admission has no consistent gain or repeated 4.9%
|
|
2972
|
+
regression. The 1,000-host memory-recovery median is 5.5% / 3.4% lower across the two trials,
|
|
2973
|
+
with the candidate faster in 11 of their 14 pairs. The measured tradeoff is roughly 3–4 MiB
|
|
2974
|
+
of additional command-local peak RSS. Snapshot counts remain three / five, and metadata
|
|
2975
|
+
decoder counts match. The internal file-cache gateway `_decoded` is called 30,007 times
|
|
2976
|
+
instead of 75,010 / 125,018; unchanged collections reuse validated mappings after fresh
|
|
2977
|
+
directory enumeration and file-identity checks. This reduces repeated dispatch, filename
|
|
2978
|
+
validation and mapping construction, without skipping first-read JSON or schema validation.
|
|
2979
|
+
|
|
2980
|
+
Query checks pass all 96 initial calls at zero / 5,000 releases and 48 reversed-order recheck
|
|
2981
|
+
calls at 5,000 releases, with identical business payloads. At 5,000 releases, medians in
|
|
2982
|
+
milliseconds are:
|
|
2983
|
+
|
|
2984
|
+
| Command | Initial 1.19.23 / 1.19.24 | Recheck 1.19.23 / 1.19.24 |
|
|
2985
|
+
| --- | ---: | ---: |
|
|
2986
|
+
| Status | 474.1 / 464.2 | 495.4 / 507.2 |
|
|
2987
|
+
| Doctor | 474.9 / 480.7 | 510.9 / 512.7 |
|
|
2988
|
+
| Lifecycle history | 491.0 / 498.9 | 530.6 / 512.8 |
|
|
2989
|
+
|
|
2990
|
+
These query measurements do not establish a general latency improvement; peak RSS differences
|
|
2991
|
+
stay below 1 MiB. Results remain synthetic and warm-cache on a shared host, with fixed pressure
|
|
2992
|
+
probes and a tiny tracked checkout. They do not establish portable latency bounds. The initial
|
|
2993
|
+
three-pair profiling pilot is excluded from formal medians. Cache lifetime, mutable snapshot
|
|
2994
|
+
isolation, metadata freshness, authority rechecks, locking and durable writes keep their gates.
|
|
2995
|
+
|
|
2996
|
+
The final 2026-10-10 comparison uses installed 1.20.0 (`88094e0`) and the tested 1.20.1 wheel
|
|
2997
|
+
subsequently published as `db43f68`. It retains the unchanged benchmark, Python 3.12.13,
|
|
2998
|
+
isolated PID namespaces, alternating version order, separate first calls and seven paired
|
|
2999
|
+
repeats. With 5,000 closed repository releases and 1,000 lost-exit host reservations, all
|
|
3000
|
+
32 timed session calls and four separate profile calls pass the existing history, writer,
|
|
3001
|
+
scope, publication, fallback, holder/lease, request-identity and budget checks. Initial medians
|
|
3002
|
+
are **1.20.0 / 1.20.1**:
|
|
3003
|
+
|
|
3004
|
+
| Session case | Median milliseconds | Median peak RSS MiB | Candidate faster pairs |
|
|
3005
|
+
| --- | ---: | ---: | ---: |
|
|
3006
|
+
| Available | 980.5 / 966.1 | 85.3 / 87.9 | 6/7 |
|
|
3007
|
+
| Memory recovery | 1,413.5 / 1,343.0 | 88.0 / 92.3 | 3/7 |
|
|
3008
|
+
|
|
3009
|
+
The initial memory-recovery samples include candidate outliers at 1.676 and 3.002 seconds.
|
|
3010
|
+
A separate memory-recovery recheck reverses the first-version order; all 16 timed calls
|
|
3011
|
+
and two separate profile calls pass:
|
|
3012
|
+
|
|
3013
|
+
| Session case | Median milliseconds | Median peak RSS MiB | Candidate faster pairs |
|
|
3014
|
+
| --- | ---: | ---: | ---: |
|
|
3015
|
+
| Memory recovery | 1,393.3 / 1,373.3 | 88.3 / 92.4 | 5/7 |
|
|
3016
|
+
|
|
3017
|
+
Keep the initial and recheck trials separate. Their memory-recovery medians are 5.0% / 1.4%
|
|
3018
|
+
lower, but the candidate is faster in only eight of the combined 14 pairs. The measured
|
|
3019
|
+
tradeoff is roughly 4 MiB of additional command-local peak RSS. These noisy samples do not
|
|
3020
|
+
establish a general session-latency improvement. Profile snapshot counts remain three / five,
|
|
3021
|
+
historical decoder counts match, and `_decoded` calls fall from 75,010 / 125,018 to 30,007.
|
|
3022
|
+
Instrumented profile timings are excluded from the latency medians.
|
|
3023
|
+
|
|
3024
|
+
Final query checks pass all 48 timed calls at 5,000 releases, with identical business payloads:
|
|
3025
|
+
|
|
3026
|
+
| Command | Median milliseconds, 1.20.0 / 1.20.1 | Median peak RSS MiB, 1.20.0 / 1.20.1 |
|
|
3027
|
+
| --- | ---: | ---: |
|
|
3028
|
+
| Status | 494.2 / 488.1 | 59.4 / 59.6 |
|
|
3029
|
+
| Doctor | 497.1 / 476.4 | 61.0 / 60.4 |
|
|
3030
|
+
| Lifecycle history | 499.2 / 514.5 | 60.5 / 60.4 |
|
|
3031
|
+
|
|
3032
|
+
Query results likewise do not establish a general latency improvement. Measurements remain
|
|
3033
|
+
synthetic and warm-cache on a shared host, with fixed memory/pressure probes and a tiny tracked
|
|
3034
|
+
checkout; they do not establish portable latency bounds. The earlier 1.19.24 tables describe
|
|
3035
|
+
the pre-integration candidate and do not replace this final 1.20.1 comparison.
|
|
3036
|
+
|
|
3037
|
+
### Writer permission checks
|
|
3038
|
+
|
|
3039
|
+
`scripts/benchmark-cli --mode writer-check` measures both installed `writer-session check`
|
|
3040
|
+
and `writer-session host-check`. The four cases are an active feature write, a read after its
|
|
3041
|
+
holder's handoff, a refused write to the frozen checkout, and a refused foreign session. The
|
|
3042
|
+
selected runtime must advertise `host_handoff_continuation_version >= 2`; its CLI/storage
|
|
3043
|
+
versions, package/entry-point hashes and the benchmark source hash are retained.
|
|
3044
|
+
|
|
3045
|
+
For a baseline, run this from the development environment, substituting a versioned installed
|
|
3046
|
+
Controller entry point and a new output directory under a task-owned temporary root:
|
|
3047
|
+
|
|
3048
|
+
```bash
|
|
3049
|
+
unshare --user --map-current-user --pid --fork --mount-proc --kill-child=KILL \
|
|
3050
|
+
.venv/bin/python scripts/benchmark-cli --mode writer-check \
|
|
3051
|
+
--work-dir /absolute/new-output \
|
|
3052
|
+
--controller current=/absolute/versioned/worktree-controller \
|
|
3053
|
+
--sizes 0 1000 5000 --repeats 7
|
|
3054
|
+
```
|
|
3055
|
+
|
|
3056
|
+
`--sizes` counts synthetic closed repository releases (five metadata documents per release).
|
|
3057
|
+
The default sizes are 0, 1,000 and 5,000. Each call is a fresh installed CLI process descending
|
|
3058
|
+
from the fixture's actual live lease holder. Setup creates empty feature tasks and pins one
|
|
3059
|
+
handoff at its unchanged HEAD; setup memory/pressure probes are fixed, while the measured CLI
|
|
3060
|
+
uses its ordinary process and authority checks. No real repository, host ledger or remote is
|
|
3061
|
+
modified. `--repo-sizes` remains specific to admission modes. Add `--profile` for one extra
|
|
3062
|
+
diagnostic process per runtime/case/action/history size, using the selected installed interpreter
|
|
3063
|
+
and CLI with real locks and holder checks. `profile_diagnostic` records snapshot load counts,
|
|
3064
|
+
their cumulative time, historical decoder counts and the largest cumulative costs. Its instrumented
|
|
3065
|
+
latency and RSS are excluded from `first_call`, `samples` and medians.
|
|
3066
|
+
|
|
3067
|
+
First calls follow setup and identity probes; repeated calls use warm OS and task-local
|
|
3068
|
+
bytecode caches. Metadata hashing also warms caches outside the timing. These are synthetic
|
|
3069
|
+
shared-host measurements, not storage-cold timings or portable latency bounds. Multiple
|
|
3070
|
+
selected runtimes alternate order within each case. `results.json` retains every sample,
|
|
3071
|
+
median wall time and peak RSS; `calls/*.json` in each `writer-<size>` fixture retains exact
|
|
3072
|
+
arguments, exit status, decision and stderr.
|
|
3073
|
+
|
|
3074
|
+
Every call verifies the expected grant/refusal, allocation, scope, holder and lease evidence,
|
|
3075
|
+
unchanged repository/host JSON bytes, and an absent write target. Each timed and diagnostic response
|
|
3076
|
+
must also match across selected runtimes after excluding only `tool_version`. Cleanup releases the exact
|
|
3077
|
+
leases, reopens only the empty frozen fixture, and uses Controller `complete-no-change` and
|
|
3078
|
+
guarded `release` for both checkouts. `fixture-cleanup.json` retains their completion/release
|
|
3079
|
+
receipts and verifies zero fixture leases or linked checkouts. Check failures/timeouts retain
|
|
3080
|
+
partial output and still run this cleanup; the overall result becomes `PASS` only after all
|
|
3081
|
+
samples, cleanup and installed-runtime identity rechecks succeed. The containing fixture
|
|
3082
|
+
repositories and retained refs remain disposable evidence until their task-owned output root
|
|
3083
|
+
is cleaned.
|
|
3084
|
+
|
|
3085
|
+
The 2026-10-12 baseline uses installed Controller 1.22.2 (`a3111f9`), CLI contract 54,
|
|
3086
|
+
storage layout 4 and continuation version 2, with Python 3.12.13 on Linux x86_64 in an
|
|
3087
|
+
isolated PID namespace. All 192 timed calls passed their expected authority and metadata
|
|
3088
|
+
checks; all six fixture checkouts were released. Seven repeated samples per row give these
|
|
3089
|
+
direct / hosted median pairs:
|
|
3090
|
+
|
|
3091
|
+
| Case | 0 releases, ms | 1,000 releases, ms | 5,000 releases, ms | 5,000 releases, peak RSS MiB |
|
|
3092
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
3093
|
+
| Active write | 76.6 / 77.7 | 163.0 / 162.1 | 554.0 / 510.1 | 76.82 / 77.06 |
|
|
3094
|
+
| Handoff read | 75.1 / 77.8 | 190.1 / 164.0 | 577.3 / 529.3 | 82.41 / 77.00 |
|
|
3095
|
+
| Frozen write refusal | 90.1 / 90.6 | 187.0 / 165.1 | 616.3 / 564.1 | 82.68 / 76.88 |
|
|
3096
|
+
| Foreign session refusal | 90.1 / 93.9 | 169.4 / 159.9 | 639.2 / 547.3 | 82.24 / 76.40 |
|
|
3097
|
+
|
|
3098
|
+
This is a baseline of one runtime on synthetic warm-cache history. Direct and hosted actions
|
|
3099
|
+
run in separate blocks; these figures do not establish a paired cross-path performance gain
|
|
3100
|
+
or an improvement over an earlier release.
|
|
3101
|
+
|
|
3102
|
+
The 2026-10-13 comparison uses the installed 1.22.2 runtime and an isolated 1.22.3 wheel with
|
|
3103
|
+
Python 3.12.13 in a PID namespace. All 384 timed calls and 48 separate profile calls pass the
|
|
3104
|
+
authority, unchanged-metadata and identical-business-JSON checks; all six fixture checkouts are
|
|
3105
|
+
released. Runtime order alternates within each of seven pairs, with a separate first call.
|
|
3106
|
+
Direct `check` medians are shown as **1.22.2 / 1.22.3**:
|
|
3107
|
+
|
|
3108
|
+
| Case | 0 releases, ms | 1,000 releases, ms | 5,000 releases, ms | 5,000 releases, peak RSS MiB |
|
|
3109
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
3110
|
+
| Active write | 81.0 / 80.3 | 164.8 / 166.8 | 573.9 / 558.3 | 76.85 / 75.89 |
|
|
3111
|
+
| Read after handoff | 84.1 / 84.7 | 185.4 / 172.2 | 700.1 / 588.7 | 82.22 / 75.59 |
|
|
3112
|
+
| Frozen write refusal | 83.3 / 83.0 | 197.7 / 185.4 | 712.7 / 624.0 | 82.33 / 76.24 |
|
|
3113
|
+
| Foreign session refusal | 75.5 / 78.1 | 194.6 / 181.0 | 789.9 / 648.7 | 82.65 / 76.33 |
|
|
3114
|
+
|
|
3115
|
+
At 5,000 releases, each of the three continuation/refusal cases is faster in all seven pairs,
|
|
3116
|
+
with 12–18% lower median latency and about 6 MiB lower peak RSS. Their snapshot loads fall from
|
|
3117
|
+
two to one and live writer-lease decodes from four to two. Historical decoder counts match;
|
|
3118
|
+
the redundant history scan/validation is removed rather than cached across calls. The shared
|
|
3119
|
+
lock covers both the guard and continuation, so Controller authority mutations cannot split
|
|
3120
|
+
their snapshot. Active checks and every hosted case retain one load. Hosted 5,000-release
|
|
3121
|
+
medians vary from 0.6% slower to 5.1% faster; empty-history cases and these unchanged controls
|
|
3122
|
+
do not show a consistent gain. Profile timings are excluded from the table. These remain
|
|
3123
|
+
warm-cache synthetic measurements on a shared host, not portable latency guarantees or a
|
|
3124
|
+
full-suite performance claim.
|
|
3125
|
+
|
|
3126
|
+
The 2026-10-13 cache comparison uses installed 1.22.3 (`7f6db88`) and an isolated 1.22.4 wheel,
|
|
3127
|
+
Python 3.12.13, isolated PID namespaces, alternating runtime order and seven paired repeats.
|
|
3128
|
+
All 384 timed calls and 48 separate profile calls pass the authority, unchanged-metadata and
|
|
3129
|
+
identical-business-JSON checks; all six fixture checkouts are released. At 5,000 synthetic
|
|
3130
|
+
closed releases, medians are shown as **1.22.3 / 1.22.4**:
|
|
3131
|
+
|
|
3132
|
+
| Case | Command | Median ms | Peak RSS MiB | Faster pairs |
|
|
3133
|
+
| --- | --- | ---: | ---: | ---: |
|
|
3134
|
+
| Active write | `check` | 610.0 / 521.3 | 76.27 / 59.00 | 6/7 |
|
|
3135
|
+
| Active write | `host-check` | 536.0 / 508.2 | 76.32 / 58.67 | 7/7 |
|
|
3136
|
+
| Read after handoff | `check` | 559.5 / 510.8 | 76.36 / 59.13 | 7/7 |
|
|
3137
|
+
| Read after handoff | `host-check` | 598.5 / 534.3 | 76.48 / 59.00 | 6/7 |
|
|
3138
|
+
| Frozen write refusal | `check` | 582.7 / 522.8 | 75.81 / 58.56 | 6/7 |
|
|
3139
|
+
| Frozen write refusal | `host-check` | 620.8 / 598.3 | 76.27 / 58.65 | 7/7 |
|
|
3140
|
+
| Foreign session refusal | `check` | 594.6 / 555.2 | 76.12 / 58.57 | 7/7 |
|
|
3141
|
+
| Foreign session refusal | `host-check` | 658.5 / 562.6 | 76.28 / 58.54 | 6/7 |
|
|
3142
|
+
|
|
3143
|
+
The 5,000-release cases use about 17 MiB less peak RSS and have 3.6–14.6% lower median latency.
|
|
3144
|
+
Every profile retains one snapshot load and identical metadata decoder counts. Single-snapshot
|
|
3145
|
+
checks stop retaining decoded values, file-identity dictionaries and copies of historical mappings;
|
|
3146
|
+
the byte reader, full validation and shared-lock decision remain unchanged. At 1,000 releases,
|
|
3147
|
+
peak RSS falls by roughly 3 MiB, while latency gains vary by case. Empty-history medians range
|
|
3148
|
+
from 6.7% faster to 5.7% slower, with no consistent RSS change. These are warm-cache synthetic
|
|
3149
|
+
results on a shared host; they do not establish portable latency guarantees or full-suite performance.
|