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,131 @@
|
|
|
1
|
+
"""Exact immutable evidence references across the explicit one-time layout move.
|
|
2
|
+
|
|
3
|
+
This is not metadata discovery: only an explicit layout or namespace migration's
|
|
4
|
+
hash-bound inventory can relocate a historical reference. Current metadata authority
|
|
5
|
+
has one namespace.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import stat
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from . import preservation as p
|
|
15
|
+
from .disposable import _inode, _open_directory
|
|
16
|
+
from .models import ControllerError
|
|
17
|
+
from .preserved_cleanup import _document
|
|
18
|
+
from .store import ROOT_NAME, VERSION_NAME
|
|
19
|
+
|
|
20
|
+
CONTRACT = "metadata-reference-relocation/v1"
|
|
21
|
+
RECORD = "namespace-relocation.json"
|
|
22
|
+
DIRECTORIES = (
|
|
23
|
+
"transactions",
|
|
24
|
+
"completion-receipts",
|
|
25
|
+
"source-cleanup",
|
|
26
|
+
"cleanup-actions",
|
|
27
|
+
"cleanup-recoveries",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _blocked() -> ControllerError:
|
|
32
|
+
return ControllerError("METADATA_RELOCATION_BLOCKED", "exact metadata relocation guard refused")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def inventory(root: Path) -> dict[str, str]:
|
|
36
|
+
"""Only immutable cleanup JSON evidence; no provider files or mutable records."""
|
|
37
|
+
result = {}
|
|
38
|
+
|
|
39
|
+
def walk(directory: p._Directory, prefix: str) -> None:
|
|
40
|
+
directory.check()
|
|
41
|
+
names = sorted(os.listdir(directory.fd))
|
|
42
|
+
for name in names:
|
|
43
|
+
info = os.stat(name, dir_fd=directory.fd, follow_symlinks=False)
|
|
44
|
+
relative = f"{prefix}/{name}"
|
|
45
|
+
if stat.S_ISDIR(info.st_mode):
|
|
46
|
+
with _open_directory(directory.fd, name, _inode(info)) as child:
|
|
47
|
+
walk(child, relative)
|
|
48
|
+
elif stat.S_ISREG(info.st_mode) and name.endswith(".json"):
|
|
49
|
+
_, result[relative] = _document(directory, name)
|
|
50
|
+
else:
|
|
51
|
+
raise _blocked()
|
|
52
|
+
directory.check()
|
|
53
|
+
if sorted(os.listdir(directory.fd)) != names:
|
|
54
|
+
raise _blocked()
|
|
55
|
+
|
|
56
|
+
with p._absolute_directory(root) as chain:
|
|
57
|
+
parent = chain[-1]
|
|
58
|
+
for name in DIRECTORIES:
|
|
59
|
+
if name in os.listdir(parent.fd):
|
|
60
|
+
info = os.stat(name, dir_fd=parent.fd, follow_symlinks=False)
|
|
61
|
+
with _open_directory(parent.fd, name, _inode(info)) as child:
|
|
62
|
+
walk(child, name)
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def prepare(old: Path, new: Path) -> dict:
|
|
67
|
+
with p._absolute_directory(old) as chain:
|
|
68
|
+
info = os.fstat(chain[-1].fd)
|
|
69
|
+
value = {
|
|
70
|
+
"contract": CONTRACT,
|
|
71
|
+
"previous_root": str(old),
|
|
72
|
+
"current_root": str(new),
|
|
73
|
+
"root_identity": [info.st_dev, info.st_ino],
|
|
74
|
+
"files": inventory(old),
|
|
75
|
+
}
|
|
76
|
+
return {**value, "sha256": p._sha(value)}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _record(root: Path) -> dict:
|
|
80
|
+
with p._absolute_directory(root) as chain:
|
|
81
|
+
value, _ = _document(chain[-1], RECORD)
|
|
82
|
+
info = os.fstat(chain[-1].fd)
|
|
83
|
+
old = Path(value["previous_root"])
|
|
84
|
+
if (
|
|
85
|
+
value.get("contract") != CONTRACT
|
|
86
|
+
or value.get("sha256") != p._sha({k: v for k, v in value.items() if k != "sha256"})
|
|
87
|
+
or value.get("current_root") != str(root)
|
|
88
|
+
or value.get("root_identity") != [info.st_dev, info.st_ino]
|
|
89
|
+
or not old.is_absolute()
|
|
90
|
+
or old.parent.parent != root.parent.parent
|
|
91
|
+
or old.name != VERSION_NAME
|
|
92
|
+
or old == root
|
|
93
|
+
or os.path.lexists(old)
|
|
94
|
+
):
|
|
95
|
+
raise _blocked()
|
|
96
|
+
return value
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def relocated(path: Path, digest: str | None) -> Path:
|
|
100
|
+
"""Resolve a missing old reference only through an exact recorded file digest."""
|
|
101
|
+
if not digest or not path.is_absolute() or ".." in path.parts:
|
|
102
|
+
return path
|
|
103
|
+
for old in path.parents:
|
|
104
|
+
if old.name != VERSION_NAME:
|
|
105
|
+
continue
|
|
106
|
+
root = old.parent.parent / ROOT_NAME / VERSION_NAME
|
|
107
|
+
if root == old or not os.path.lexists(root / RECORD):
|
|
108
|
+
continue
|
|
109
|
+
value = _record(root)
|
|
110
|
+
if value["previous_root"] != str(old):
|
|
111
|
+
continue
|
|
112
|
+
relative = path.relative_to(old).as_posix()
|
|
113
|
+
if value["files"].get(relative) != digest:
|
|
114
|
+
raise _blocked()
|
|
115
|
+
return root / relative
|
|
116
|
+
return path
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def historical_reference(path: Path, digest: str) -> dict[str, str]:
|
|
120
|
+
"""Preserve reference identities used inside already immutable attempt chains."""
|
|
121
|
+
for root in path.parents:
|
|
122
|
+
if root.name != VERSION_NAME or root.parent.name != ROOT_NAME:
|
|
123
|
+
continue
|
|
124
|
+
if not os.path.lexists(root / RECORD):
|
|
125
|
+
break
|
|
126
|
+
value = _record(root)
|
|
127
|
+
relative = path.relative_to(root).as_posix()
|
|
128
|
+
if value["files"].get(relative) == digest:
|
|
129
|
+
path = Path(value["previous_root"]) / relative
|
|
130
|
+
break
|
|
131
|
+
return {"path": str(path), "sha256": digest}
|
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
"""Public model constants and small immutable data structures."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
SCHEMA_VERSION = 1
|
|
10
|
+
METADATA_SCHEMA_VERSION = 4
|
|
11
|
+
SUPPORTED_METADATA_SCHEMA_VERSIONS = frozenset({1, 2, 3, 4})
|
|
12
|
+
TERMINAL_INTEGRATION_STATES = frozenset({"REMOTE_VERIFIED", "RELEASED", "ABANDONED"})
|
|
13
|
+
|
|
14
|
+
LIFECYCLE_STATES = frozenset(
|
|
15
|
+
{
|
|
16
|
+
"PLANNED",
|
|
17
|
+
"ACTIVE",
|
|
18
|
+
"RETAINED",
|
|
19
|
+
"RELEASE_READY",
|
|
20
|
+
"ARCHIVED",
|
|
21
|
+
"NO_CHANGE_COMPLETED",
|
|
22
|
+
"INTEGRATION_ABORTED",
|
|
23
|
+
"HANDOFF_READY",
|
|
24
|
+
"PRESERVED",
|
|
25
|
+
"INTEGRATING",
|
|
26
|
+
"PUBLISHED",
|
|
27
|
+
"RELEASED",
|
|
28
|
+
"BLOCKED",
|
|
29
|
+
"QUARANTINED",
|
|
30
|
+
"RECOVERY_REQUIRED",
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
INTEGRATION_INTENT_STATES = frozenset(
|
|
34
|
+
{
|
|
35
|
+
"QUEUED",
|
|
36
|
+
"WAITING_SCOPE",
|
|
37
|
+
"PREPARING_INTEGRATION",
|
|
38
|
+
"READY_TO_PUBLISH",
|
|
39
|
+
"NEEDS_REPREPARE",
|
|
40
|
+
"PUBLISHING",
|
|
41
|
+
"RECOVERY_REQUIRED",
|
|
42
|
+
"ATTENTION_REQUIRED",
|
|
43
|
+
"REMOTE_VERIFIED",
|
|
44
|
+
"RELEASED",
|
|
45
|
+
# Accepted only while reading pre-0.6 intent records.
|
|
46
|
+
"CLAIMED",
|
|
47
|
+
"VALIDATED",
|
|
48
|
+
"BLOCKED",
|
|
49
|
+
"PUBLISHED",
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
INTEGRATION_EVENT_TYPES = frozenset(
|
|
53
|
+
{
|
|
54
|
+
"INTENT_QUEUED",
|
|
55
|
+
"PREPARE_SLOT_GRANTED",
|
|
56
|
+
"PREPARED",
|
|
57
|
+
"NEEDS_REPREPARE",
|
|
58
|
+
"ATTENTION_REQUIRED",
|
|
59
|
+
"PUBLICATION_LEASE_GRANTED",
|
|
60
|
+
"REMOTE_VERIFIED",
|
|
61
|
+
"RECOVERY_REQUIRED",
|
|
62
|
+
"RELEASED",
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
RELEASE_INTENT_STATES = frozenset(
|
|
66
|
+
{
|
|
67
|
+
"PENDING",
|
|
68
|
+
"BLOCKED_RETRYABLE",
|
|
69
|
+
"STALE",
|
|
70
|
+
"CANCELLED",
|
|
71
|
+
"RELEASED",
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
ACTIVE_AUTHORITY_STATES = frozenset(
|
|
75
|
+
{
|
|
76
|
+
"PLANNED",
|
|
77
|
+
"ACTIVE",
|
|
78
|
+
"ARCHIVED",
|
|
79
|
+
"HANDOFF_READY",
|
|
80
|
+
"INTEGRATING",
|
|
81
|
+
"BLOCKED",
|
|
82
|
+
"QUARANTINED",
|
|
83
|
+
"RECOVERY_REQUIRED",
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
CANONICAL_ROLES = frozenset(
|
|
87
|
+
{"canonical", "control-plane", "integration", "publication", "verification"}
|
|
88
|
+
)
|
|
89
|
+
#: Roles that share one bounded physical pool of ordinary worktrees.
|
|
90
|
+
ORDINARY_ROLES = frozenset({"feature", "audit"})
|
|
91
|
+
EMERGENCY_ROLES = frozenset({"hotfix", "emergency"})
|
|
92
|
+
RECOVERY_ROLES = frozenset({"preservation", "recovery"})
|
|
93
|
+
FEATURE_AUTHORITY_ROLES = frozenset({"feature", "audit", "hotfix", "emergency"})
|
|
94
|
+
WORKTREE_ROLES = frozenset(
|
|
95
|
+
{
|
|
96
|
+
"canonical",
|
|
97
|
+
"control-plane",
|
|
98
|
+
"feature",
|
|
99
|
+
"integration",
|
|
100
|
+
"audit",
|
|
101
|
+
"preservation",
|
|
102
|
+
"recovery",
|
|
103
|
+
"hotfix",
|
|
104
|
+
"emergency",
|
|
105
|
+
"publication",
|
|
106
|
+
"verification",
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
PLAN_DECISIONS = frozenset(
|
|
111
|
+
{
|
|
112
|
+
"REUSE",
|
|
113
|
+
"CREATE_ALLOWED",
|
|
114
|
+
"CREATE_EMERGENCY_ALLOWED",
|
|
115
|
+
"CREATE_RECOVERY_ALLOWED",
|
|
116
|
+
"BLOCK_BUDGET",
|
|
117
|
+
"BLOCK_CANONICAL",
|
|
118
|
+
"BLOCK_INTEGRATION_SLOT",
|
|
119
|
+
"BLOCK_FEATURE_SLOT",
|
|
120
|
+
"BLOCK_ORDINARY_POOL",
|
|
121
|
+
"BLOCK_EMERGENCY_SLOT",
|
|
122
|
+
"BLOCK_RECOVERY_RESERVE",
|
|
123
|
+
"BLOCK_DUPLICATE_AUTHORITY",
|
|
124
|
+
"NEEDS_ADOPTION",
|
|
125
|
+
"BLOCK_DRIFT",
|
|
126
|
+
"UNKNOWN",
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ControllerError(RuntimeError):
|
|
132
|
+
"""Controlled failure with a stable machine-readable code."""
|
|
133
|
+
|
|
134
|
+
def __init__(self, code: str, message: str) -> None:
|
|
135
|
+
super().__init__(message)
|
|
136
|
+
self.code = code
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass(frozen=True)
|
|
140
|
+
class WorktreeGitState:
|
|
141
|
+
path: Path
|
|
142
|
+
git_dir: Path
|
|
143
|
+
key: str
|
|
144
|
+
canonical: bool
|
|
145
|
+
head: str
|
|
146
|
+
branch: str | None
|
|
147
|
+
detached: bool
|
|
148
|
+
locked: bool
|
|
149
|
+
lock_reason: str | None
|
|
150
|
+
prunable: bool
|
|
151
|
+
prunable_reason: str | None
|
|
152
|
+
clean: bool
|
|
153
|
+
staged: int
|
|
154
|
+
modified: int
|
|
155
|
+
untracked: int
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass(frozen=True)
|
|
159
|
+
class RepositoryInventory:
|
|
160
|
+
requested_path: Path
|
|
161
|
+
canonical_path: Path
|
|
162
|
+
common_dir: Path
|
|
163
|
+
worktrees: tuple[WorktreeGitState, ...]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass(frozen=True)
|
|
167
|
+
class ConvergencePolicy:
|
|
168
|
+
"""Delegated retention convergence: how long an unused checkout may stay on disk.
|
|
169
|
+
|
|
170
|
+
``standing_reserve`` is the number of idle checkouts tolerated beside the live
|
|
171
|
+
writers before idle thresholds start to shrink; it is a pressure reference, never
|
|
172
|
+
an admission limit.
|
|
173
|
+
|
|
174
|
+
``release_unintegrated_handoffs`` lets the reconciler release the checkout of a
|
|
175
|
+
frozen, integration-ready handoff whose exact HEAD is retained by both the local
|
|
176
|
+
and the remote-tracking branch before it is integrated; integration then replays
|
|
177
|
+
from that retained branch.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
enabled: bool
|
|
181
|
+
grace_minutes: int
|
|
182
|
+
idle_hours_max: int
|
|
183
|
+
idle_hours_min: int
|
|
184
|
+
standing_reserve: int
|
|
185
|
+
release_unintegrated_handoffs: bool = False
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass(frozen=True)
|
|
189
|
+
class WorktreePolicy:
|
|
190
|
+
path: Path
|
|
191
|
+
present: bool
|
|
192
|
+
policy_schema_version: int
|
|
193
|
+
normal_limit: int
|
|
194
|
+
hard_limit: int
|
|
195
|
+
canonical_protected: bool
|
|
196
|
+
canonical_allowed_roles: frozenset[str]
|
|
197
|
+
integration_candidate_limit: int
|
|
198
|
+
integration_prepare_limit: int
|
|
199
|
+
feature_audit_slots: int
|
|
200
|
+
emergency_slots: int
|
|
201
|
+
recovery_reserve: int
|
|
202
|
+
retained_slots: int
|
|
203
|
+
reconciler_enabled: bool
|
|
204
|
+
reconciler_actor: str
|
|
205
|
+
reconciler_legacy_adoption: bool
|
|
206
|
+
main_publish_limit: int
|
|
207
|
+
publication_remote: str
|
|
208
|
+
publication_branch: str
|
|
209
|
+
automatic_feature_audit_release: bool
|
|
210
|
+
automatic_integration_continuation: bool
|
|
211
|
+
preserved_auto_release: bool
|
|
212
|
+
release_intent_ttl_seconds: int
|
|
213
|
+
stale_warning_days: int
|
|
214
|
+
require_adoption: bool
|
|
215
|
+
require_task_key: bool
|
|
216
|
+
require_authority_key: bool
|
|
217
|
+
unique_active_feature_authority: bool
|
|
218
|
+
resource_admission: bool = True
|
|
219
|
+
checkout_growth_bytes: int = 2 * 1024**3
|
|
220
|
+
checkout_growth_inodes: int = 100_000
|
|
221
|
+
convergence: ConvergencePolicy | None = None
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@dataclass(frozen=True)
|
|
225
|
+
class MetadataRecord:
|
|
226
|
+
schema_version: int
|
|
227
|
+
record_version: int
|
|
228
|
+
allocation_id: str
|
|
229
|
+
task_key: str
|
|
230
|
+
authority_key: str
|
|
231
|
+
module: str
|
|
232
|
+
role: str
|
|
233
|
+
owner: str
|
|
234
|
+
lifecycle: str
|
|
235
|
+
created_at: str
|
|
236
|
+
last_reviewed_at: str
|
|
237
|
+
handoff_commit: str | None
|
|
238
|
+
finalizer_record_id: str | None
|
|
239
|
+
base_commit: str | None
|
|
240
|
+
task_commit: str | None
|
|
241
|
+
finalizer_commit: str | None
|
|
242
|
+
scope_paths: tuple[str, ...]
|
|
243
|
+
source_allocations: tuple[str, ...]
|
|
244
|
+
quarantine_expected_commit: str | None
|
|
245
|
+
quarantine_observed_commit: str | None
|
|
246
|
+
quarantine_reason: str | None
|
|
247
|
+
recovery_allocation_id: str | None
|
|
248
|
+
recovery_ref: str | None
|
|
249
|
+
disposable_resources: tuple[dict[str, Any], ...]
|
|
250
|
+
external_bindings: tuple[str, ...]
|
|
251
|
+
history: tuple[dict[str, Any], ...]
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
@dataclass(frozen=True)
|
|
255
|
+
class IntegrationIntent:
|
|
256
|
+
schema_version: int
|
|
257
|
+
contract_version: int
|
|
258
|
+
intent_id: str
|
|
259
|
+
task_key: str
|
|
260
|
+
authority_key: str
|
|
261
|
+
module: str
|
|
262
|
+
owner: str
|
|
263
|
+
source_allocations: tuple[str, ...]
|
|
264
|
+
source_handoffs: tuple[str, ...]
|
|
265
|
+
source_branches: tuple[str, ...]
|
|
266
|
+
source_finalizer_record_ids: tuple[str, ...]
|
|
267
|
+
source_snapshot_ids: tuple[str, ...]
|
|
268
|
+
scope_paths: tuple[str, ...]
|
|
269
|
+
scope_digest: str
|
|
270
|
+
queue_sequence: int
|
|
271
|
+
state: str
|
|
272
|
+
allocation_id: str | None
|
|
273
|
+
base_main_oid: str | None
|
|
274
|
+
candidate_oid: str | None
|
|
275
|
+
revision: int
|
|
276
|
+
attempt: int
|
|
277
|
+
prepare_identity: str | None
|
|
278
|
+
publication_identity: str | None
|
|
279
|
+
recovery_identity: str | None
|
|
280
|
+
prepared_receipt_id: str | None
|
|
281
|
+
prepared_base_origin_main_oid: str | None
|
|
282
|
+
prepared_commit_oid: str | None
|
|
283
|
+
prepared_tree_oid: str | None
|
|
284
|
+
prepared_scope_digest: str | None
|
|
285
|
+
tests_receipt_id: str | None
|
|
286
|
+
snapshot_receipt_id: str | None
|
|
287
|
+
validation_evidence_id: str | None
|
|
288
|
+
validated_oid: str | None
|
|
289
|
+
blocked_reason: str | None
|
|
290
|
+
enqueued_at: str
|
|
291
|
+
claimed_at: str | None
|
|
292
|
+
validated_at: str | None
|
|
293
|
+
published_at: str | None
|
|
294
|
+
released_at: str | None
|
|
295
|
+
updated_at: str
|
|
296
|
+
equivalent_replays: tuple[dict[str, str], ...]
|
|
297
|
+
history: tuple[dict[str, Any], ...]
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@dataclass(frozen=True)
|
|
301
|
+
class PreparedCandidateReceipt:
|
|
302
|
+
schema_version: int
|
|
303
|
+
receipt_id: str
|
|
304
|
+
repository_id: str
|
|
305
|
+
intent_id: str
|
|
306
|
+
allocation_id: str
|
|
307
|
+
attempt: int
|
|
308
|
+
base_origin_main_oid: str
|
|
309
|
+
candidate_oid: str
|
|
310
|
+
tree_oid: str
|
|
311
|
+
scope_digest: str
|
|
312
|
+
prepare_identity: str
|
|
313
|
+
validation_evidence_id: str
|
|
314
|
+
tests_receipt_id: str
|
|
315
|
+
snapshot_receipt_id: str
|
|
316
|
+
created_at: str
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@dataclass(frozen=True)
|
|
320
|
+
class LifecycleEvent:
|
|
321
|
+
schema_version: int
|
|
322
|
+
event_id: str
|
|
323
|
+
repository_id: str
|
|
324
|
+
intent_id: str
|
|
325
|
+
allocation_id: str | None
|
|
326
|
+
event_type: str
|
|
327
|
+
state: str
|
|
328
|
+
sequence: int
|
|
329
|
+
identity: str
|
|
330
|
+
created_at: str
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@dataclass(frozen=True)
|
|
334
|
+
class IntegrationReconciliationReceipt:
|
|
335
|
+
schema_version: int
|
|
336
|
+
receipt_id: str
|
|
337
|
+
repository_id: str
|
|
338
|
+
intent_id: str
|
|
339
|
+
observed_intent_schema_version: int
|
|
340
|
+
observed_intent_state: str
|
|
341
|
+
observed_allocation_id: str | None
|
|
342
|
+
observed_record_version: int | None
|
|
343
|
+
observed_record_lifecycle: str | None
|
|
344
|
+
effective_terminal_state: str
|
|
345
|
+
result: str
|
|
346
|
+
actor: str
|
|
347
|
+
reconciled_at: str
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@dataclass(frozen=True)
|
|
351
|
+
class PublicationLease:
|
|
352
|
+
schema_version: int
|
|
353
|
+
lease_id: str
|
|
354
|
+
repository_id: str
|
|
355
|
+
allocation_id: str
|
|
356
|
+
holder: str
|
|
357
|
+
run_id: str
|
|
358
|
+
remote: str
|
|
359
|
+
branch: str
|
|
360
|
+
target_ref: str
|
|
361
|
+
candidate_oid: str
|
|
362
|
+
expected_main_oid: str
|
|
363
|
+
validation_evidence_id: str
|
|
364
|
+
acquired_at: str
|
|
365
|
+
expires_at: str
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@dataclass(frozen=True)
|
|
369
|
+
class WriterHolder:
|
|
370
|
+
"""Identity of the live process that currently owns write permission.
|
|
371
|
+
|
|
372
|
+
``pid`` alone cannot identify a process: Linux reuses pids, and a stale lease
|
|
373
|
+
naming a recycled pid would read as live. ``boot_id`` plus the kernel's
|
|
374
|
+
``starttime`` for that pid make the triple unique for the life of the boot, so
|
|
375
|
+
a recycled pid never satisfies a lease written for its predecessor.
|
|
376
|
+
"""
|
|
377
|
+
|
|
378
|
+
client_id: str
|
|
379
|
+
pid: int
|
|
380
|
+
boot_id: str
|
|
381
|
+
start_ticks: int
|
|
382
|
+
session_id: str | None
|
|
383
|
+
run_id: str | None
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
@dataclass(frozen=True)
|
|
387
|
+
class WriterLease:
|
|
388
|
+
schema_version: int
|
|
389
|
+
lease_id: str
|
|
390
|
+
repository_id: str
|
|
391
|
+
allocation_id: str
|
|
392
|
+
worktree_path: str
|
|
393
|
+
owner: str
|
|
394
|
+
holder: WriterHolder
|
|
395
|
+
acquired_at: str
|
|
396
|
+
renewed_at: str
|
|
397
|
+
expires_at: str | None
|
|
398
|
+
record_version: int
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@dataclass(frozen=True)
|
|
402
|
+
class WriterReconciliationReceipt:
|
|
403
|
+
schema_version: int
|
|
404
|
+
receipt_id: str
|
|
405
|
+
repository_id: str
|
|
406
|
+
allocation_id: str
|
|
407
|
+
actor: str
|
|
408
|
+
owner: str
|
|
409
|
+
trigger: str
|
|
410
|
+
outcome: str
|
|
411
|
+
reason: str
|
|
412
|
+
evidence: tuple[str, ...]
|
|
413
|
+
previous_lifecycle: str
|
|
414
|
+
new_lifecycle: str
|
|
415
|
+
reconciled_at: str
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
@dataclass(frozen=True)
|
|
419
|
+
class PublicationReceipt:
|
|
420
|
+
schema_version: int
|
|
421
|
+
receipt_id: str
|
|
422
|
+
repository_id: str
|
|
423
|
+
allocation_id: str
|
|
424
|
+
intent_id: str | None
|
|
425
|
+
target_ref: str
|
|
426
|
+
holder: str
|
|
427
|
+
run_id: str
|
|
428
|
+
expected_main_oid: str
|
|
429
|
+
candidate_oid: str
|
|
430
|
+
validation_evidence_id: str
|
|
431
|
+
finalizer_record_id: str
|
|
432
|
+
status: str
|
|
433
|
+
acquired_at: str
|
|
434
|
+
remote_verified_at: str
|
|
435
|
+
lease_released_at: str | None
|
|
436
|
+
released_at: str | None
|
|
437
|
+
lease_duration_ms: int
|
|
438
|
+
remote_verified_to_released_ms: int | None
|
|
439
|
+
source_results: tuple[dict[str, str], ...]
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
@dataclass(frozen=True)
|
|
443
|
+
class ReleaseIntent:
|
|
444
|
+
schema_version: int
|
|
445
|
+
intent_id: str
|
|
446
|
+
repository_id: str
|
|
447
|
+
allocation_id: str
|
|
448
|
+
task_key: str
|
|
449
|
+
authority_key: str
|
|
450
|
+
owner: str
|
|
451
|
+
role: str
|
|
452
|
+
expected_head: str
|
|
453
|
+
expected_record_version: int
|
|
454
|
+
task_commit: str
|
|
455
|
+
handoff_commit: str
|
|
456
|
+
finalizer_commit: str
|
|
457
|
+
finalizer_record_id: str
|
|
458
|
+
integration_intent_id: str | None
|
|
459
|
+
integration_allocation_id: str | None
|
|
460
|
+
publication_receipt_id: str | None
|
|
461
|
+
integration_candidate_oid: str | None
|
|
462
|
+
integration_mode: str | None
|
|
463
|
+
integration_evidence_id: str | None
|
|
464
|
+
trigger: str
|
|
465
|
+
status: str
|
|
466
|
+
blocker: str | None
|
|
467
|
+
created_at: str
|
|
468
|
+
updated_at: str
|
|
469
|
+
expires_at: str
|
|
470
|
+
history: tuple[dict[str, Any], ...]
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
@dataclass(frozen=True)
|
|
474
|
+
class ReleasePlan:
|
|
475
|
+
schema_version: int
|
|
476
|
+
plan_id: str
|
|
477
|
+
repository_id: str
|
|
478
|
+
allocation_id: str
|
|
479
|
+
release_intent_id: str | None
|
|
480
|
+
target_path: str
|
|
481
|
+
branch: str
|
|
482
|
+
expected_head: str
|
|
483
|
+
expected_record_version: int
|
|
484
|
+
owner: str
|
|
485
|
+
authority_key: str
|
|
486
|
+
role: str
|
|
487
|
+
lifecycle: str
|
|
488
|
+
trigger: str
|
|
489
|
+
publication_receipt_id: str | None
|
|
490
|
+
resource_actions: tuple[dict[str, Any], ...]
|
|
491
|
+
created_at: str
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
@dataclass(frozen=True)
|
|
495
|
+
class ReleaseReceipt:
|
|
496
|
+
schema_version: int
|
|
497
|
+
receipt_id: str
|
|
498
|
+
plan_id: str
|
|
499
|
+
repository_id: str
|
|
500
|
+
allocation_id: str
|
|
501
|
+
release_intent_id: str | None
|
|
502
|
+
target_path: str
|
|
503
|
+
branch: str
|
|
504
|
+
released_head: str
|
|
505
|
+
expected_record_version: int
|
|
506
|
+
released_record_version: int
|
|
507
|
+
trigger: str
|
|
508
|
+
resource_receipt_ids: tuple[str, ...]
|
|
509
|
+
result: str
|
|
510
|
+
released_at: str
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
@dataclass(frozen=True)
|
|
514
|
+
class ReconciliationReceipt:
|
|
515
|
+
schema_version: int
|
|
516
|
+
receipt_id: str
|
|
517
|
+
repository_id: str
|
|
518
|
+
source_receipt_id: str
|
|
519
|
+
allocation_id: str
|
|
520
|
+
observed_record_version: int
|
|
521
|
+
observed_record_lifecycle: str
|
|
522
|
+
observed_receipt_status: str
|
|
523
|
+
differences: tuple[str, ...]
|
|
524
|
+
recovered_intent_id: str | None
|
|
525
|
+
recovered_intent_state: str | None
|
|
526
|
+
result: str
|
|
527
|
+
actor: str
|
|
528
|
+
reconciled_at: str
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
@dataclass(frozen=True)
|
|
532
|
+
class MetadataSnapshot:
|
|
533
|
+
repository_id: str | None
|
|
534
|
+
records: dict[str, MetadataRecord]
|
|
535
|
+
bindings: dict[str, str]
|
|
536
|
+
integration_intents: dict[str, IntegrationIntent]
|
|
537
|
+
prepared_candidate_receipts: dict[str, PreparedCandidateReceipt]
|
|
538
|
+
integration_reconciliation_receipts: dict[str, IntegrationReconciliationReceipt]
|
|
539
|
+
release_intents: dict[str, ReleaseIntent]
|
|
540
|
+
release_plans: dict[str, ReleasePlan]
|
|
541
|
+
publication_lease: PublicationLease | None
|
|
542
|
+
publication_receipts: dict[str, PublicationReceipt]
|
|
543
|
+
release_receipts: dict[str, ReleaseReceipt]
|
|
544
|
+
reconciliation_receipts: dict[str, ReconciliationReceipt]
|
|
545
|
+
writer_leases: dict[str, WriterLease]
|
|
546
|
+
warnings: tuple[dict[str, str], ...]
|