oc_chat
Loading...
Searching...
No Matches
errors.py
Go to the documentation of this file.
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
# oc_chat/registry/errors.py
4
5
# OCT-LINT: disable=tests_exist reason="Covered by oc_chat/tests/registry/test_seam_and_locks.py"
6
"""
7
Typed registry errors.
8
9
Purpose
10
-------
11
Expose precise exception types for the M11 registry mutating seam and
12
lock primitives.
13
14
Responsibilities
15
----------------
16
- Define typed pre-commit validation failures.
17
- Define lock contention and ownership failures with useful attributes.
18
- Keep error classes independent from SQLite implementation details.
19
20
Diagnostics
21
-----------
22
Domain: OC_CHAT.MAIN
23
Levels:
24
L2 - lifecycle events
25
L3 - semantic details
26
L4 - deep tracing
27
28
Contracts
29
---------
30
- Pre-commit failures are raised before the backend commits.
31
- Lock errors carry enough data for callers to render operator messages.
32
33
Dependencies
34
------------
35
- oc_diagnostics (structured debug logging via _dbg)
36
- dataclasses (dataclass decorator for the error types' attribute fields)
37
"""
38
from
__future__
import
annotations
39
40
from
dataclasses
import
dataclass
41
42
from
oc_diagnostics
import
_dbg
43
44
45
_dbg(
"OC_CHAT.MAIN"
,
"module"
,
"loaded"
, 4)
46
47
48
class
RegistryError
(RuntimeError):
49
"""Base class for registry API errors."""
50
51
52
@dataclass
53
class
ActorTypeMismatchError
(
RegistryError
):
54
"""Raised when supplied actor_type disagrees with agents.slot_kind."""
55
56
actor_slot: str
57
expected_actor_type: str
58
supplied_actor_type: str
59
60
def
__post_init__
(self) -> None:
61
super().__init__(
62
"actor_type mismatch for "
63
f
"{self.actor_slot!r}: expected {self.expected_actor_type!r}, "
64
f
"got {self.supplied_actor_type!r}"
65
)
66
67
68
@dataclass
69
class
ActorRoleMismatchError
(
RegistryError
):
70
"""Raised when a slot kind is not allowed to author an operation."""
71
72
operation_type: str
73
actor_slot: str
74
slot_kind: str
75
allowed_slot_kinds: tuple[str, ...]
76
77
def
__post_init__
(self) -> None:
78
super().__init__(
79
f
"{self.actor_slot!r} ({self.slot_kind}) cannot author "
80
f
"{self.operation_type!r}; allowed slot kinds: "
81
f
"{', '.join(self.allowed_slot_kinds) or 'none'}"
82
)
83
84
85
@dataclass
86
class
HmacVerifyError
(
RegistryError
):
87
"""Raised when HMAC verification fails before the main write transaction."""
88
89
attempted_slot: str |
None
90
hmac_key_id: str |
None
91
command: str
92
failure_reason: str
93
94
def
__post_init__
(self) -> None:
95
super().__init__(
96
f
"HMAC verification failed for {self.command!r}: "
97
f
"{self.failure_reason}"
98
)
99
100
101
@dataclass
102
class
HandoverStateError
(
RegistryError
):
103
"""Raised when an operation violates the orchestrator handover state."""
104
105
state: str
106
actor_slot: str
107
operation_type: str
108
handover_id: int |
None
=
None
109
110
def
__post_init__
(self) -> None:
111
suffix =
""
if
self.
handover_id
is
None
else
f
" ({self.handover_id})"
112
super().__init__(
113
f
"handover state {self.state!r}{suffix} rejects "
114
f
"{self.operation_type!r} from {self.actor_slot!r}"
115
)
116
117
118
@dataclass
119
class
RotationInFlightError
(
RegistryError
):
120
"""Raised when a slot already has a pending key rotation."""
121
122
slot: str
123
124
def
__post_init__
(self) -> None:
125
super().__init__(f
"credential rotation is already in flight for {self.slot!r}"
)
126
127
128
@dataclass
129
class
LockHeldError
(
RegistryError
):
130
"""Raised when a named lock is already held."""
131
132
name: str
133
owner_slot: str
134
expires_at: str |
None
135
136
def
__post_init__
(self) -> None:
137
super().__init__(
138
f
"lock {self.name!r} is held by {self.owner_slot!r} "
139
f
"until {self.expires_at!r}"
140
)
141
142
143
@dataclass
144
class
LockOwnerError
(
RegistryError
):
145
"""Raised when a caller tries to release or renew another slot's lock."""
146
147
name: str
148
owner_slot: str
149
actual_owner_slot: str |
None
150
151
def
__post_init__
(self) -> None:
152
super().__init__(
153
f
"lock {self.name!r} is owned by {self.actual_owner_slot!r}, "
154
f
"not {self.owner_slot!r}"
155
)
156
157
158
@dataclass
159
class
PayloadVersionMismatchError
(
RegistryError
):
160
"""Raised when payload['version'] and operation payload_version differ."""
161
162
operation_type: str
163
payload_version: int
164
supplied_version: object
165
166
def
__post_init__
(self) -> None:
167
super().__init__(
168
f
"{self.operation_type!r} payload version mismatch: "
169
f
"expected {self.payload_version!r}, got {self.supplied_version!r}"
170
)
171
172
173
@dataclass
174
class
PayloadSchemaViolationError
(
RegistryError
):
175
"""Raised for the minimal payload-shape checks owned by M11.1b."""
176
177
operation_type: str
178
message: str
179
180
def
__post_init__
(self) -> None:
181
super().__init__(f
"{self.operation_type!r}: {self.message}"
)
182
183
184
@dataclass
185
class
ParentCycleError
(
RegistryError
):
186
"""Raised when parent_operation_id would create or expose a cycle."""
187
188
parent_operation_id: int
189
operation_uuid: str
190
191
def
__post_init__
(self) -> None:
192
super().__init__(
193
"parent_operation_id cycle detected for "
194
f
"{self.operation_uuid!r} via {self.parent_operation_id!r}"
195
)
196
197
198
@dataclass
199
class
UnknownActorError
(
RegistryError
):
200
"""Raised when actor_slot is not present in agents."""
201
202
actor_slot: str
203
204
def
__post_init__
(self) -> None:
205
super().__init__(f
"unknown actor_slot: {self.actor_slot!r}"
)
206
207
208
@dataclass
209
class
RoundStateError
(
RegistryError
):
210
"""Raised when a round.* operation is illegal for the round's status."""
211
212
round_no: int
213
status: str
214
operation_type: str
215
216
def
__post_init__
(self) -> None:
217
super().__init__(
218
f
"round {self.round_no} in status {self.status!r} rejects "
219
f
"{self.operation_type!r}"
220
)
221
222
223
@dataclass
224
class
RoundInvariantError
(
RegistryError
):
225
"""Raised when a round-close invariant is violated (torn state, gaps)."""
226
227
round_no: int
228
message: str
229
230
def
__post_init__
(self) -> None:
231
super().__init__(f
"round {self.round_no} invariant violated: {self.message}"
)
232
233
234
@dataclass
235
class
PreflightFailedError
(
RegistryError
):
236
"""Raised when a pre-open or pre-resume preflight check fails."""
237
238
round_no: int |
None
239
reasons: tuple[str, ...]
240
241
def
__post_init__
(self) -> None:
242
joined =
"; "
.join(self.
reasons
)
or
"unspecified"
243
scope =
"round-close"
if
self.
round_no
is
None
else
f
"round {self.round_no}"
244
super().__init__(f
"{scope} preflight failed: {joined}"
)
245
246
247
@dataclass
248
class
MergeConflictError
(
RegistryError
):
249
"""Raised when a plan-branch merge into staging hits a conflict."""
250
251
round_no: int
252
plan_id: str
253
conflicted_paths: tuple[str, ...]
254
255
def
__post_init__
(self) -> None:
256
paths =
", "
.join(self.
conflicted_paths
)
or
"unknown"
257
super().__init__(
258
f
"merge of {self.plan_id!r} into round {self.round_no} staging "
259
f
"conflicted on: {paths}"
260
)
261
262
263
@dataclass
264
class
ManualMergeRequiredError
(
RegistryError
):
265
"""Raised when a conflict is outside the auto-resolve allowlist."""
266
267
round_no: int
268
plan_id: str
269
conflicted_paths: tuple[str, ...]
270
271
def
__post_init__
(self) -> None:
272
paths =
", "
.join(self.
conflicted_paths
)
or
"unknown"
273
super().__init__(
274
f
"manual merge required for {self.plan_id!r} in round {self.round_no}; "
275
f
"non-allowlisted conflicts: {paths}"
276
)
277
278
279
@dataclass
280
class
StagingBranchDivergedError
(
RegistryError
):
281
"""Raised when the staging branch HEAD no longer matches recorded state."""
282
283
round_no: int
284
expected_sha: str |
None
285
actual_sha: str |
None
286
287
def
__post_init__
(self) -> None:
288
super().__init__(
289
f
"round {self.round_no} staging branch diverged: expected "
290
f
"{self.expected_sha!r}, found {self.actual_sha!r}; rollback required"
291
)
oc_chat.registry.errors.ActorRoleMismatchError
Definition
errors.py:69
oc_chat.registry.errors.ActorRoleMismatchError.__post_init__
None __post_init__(self)
Definition
errors.py:77
oc_chat.registry.errors.ActorTypeMismatchError
Definition
errors.py:53
oc_chat.registry.errors.ActorTypeMismatchError.__post_init__
None __post_init__(self)
Definition
errors.py:60
oc_chat.registry.errors.HandoverStateError
Definition
errors.py:102
oc_chat.registry.errors.HandoverStateError.__post_init__
None __post_init__(self)
Definition
errors.py:110
oc_chat.registry.errors.HandoverStateError.handover_id
int handover_id
Definition
errors.py:108
oc_chat.registry.errors.HmacVerifyError
Definition
errors.py:86
oc_chat.registry.errors.HmacVerifyError.__post_init__
None __post_init__(self)
Definition
errors.py:94
oc_chat.registry.errors.LockHeldError
Definition
errors.py:129
oc_chat.registry.errors.LockHeldError.__post_init__
None __post_init__(self)
Definition
errors.py:136
oc_chat.registry.errors.LockOwnerError
Definition
errors.py:144
oc_chat.registry.errors.LockOwnerError.__post_init__
None __post_init__(self)
Definition
errors.py:151
oc_chat.registry.errors.ManualMergeRequiredError
Definition
errors.py:264
oc_chat.registry.errors.ManualMergeRequiredError.__post_init__
None __post_init__(self)
Definition
errors.py:271
oc_chat.registry.errors.ManualMergeRequiredError.conflicted_paths
tuple conflicted_paths
Definition
errors.py:269
oc_chat.registry.errors.MergeConflictError
Definition
errors.py:248
oc_chat.registry.errors.MergeConflictError.conflicted_paths
tuple conflicted_paths
Definition
errors.py:253
oc_chat.registry.errors.MergeConflictError.__post_init__
None __post_init__(self)
Definition
errors.py:255
oc_chat.registry.errors.ParentCycleError
Definition
errors.py:185
oc_chat.registry.errors.ParentCycleError.__post_init__
None __post_init__(self)
Definition
errors.py:191
oc_chat.registry.errors.PayloadSchemaViolationError
Definition
errors.py:174
oc_chat.registry.errors.PayloadSchemaViolationError.__post_init__
None __post_init__(self)
Definition
errors.py:180
oc_chat.registry.errors.PayloadVersionMismatchError
Definition
errors.py:159
oc_chat.registry.errors.PayloadVersionMismatchError.__post_init__
None __post_init__(self)
Definition
errors.py:166
oc_chat.registry.errors.PreflightFailedError
Definition
errors.py:235
oc_chat.registry.errors.PreflightFailedError.round_no
int round_no
Definition
errors.py:238
oc_chat.registry.errors.PreflightFailedError.__post_init__
None __post_init__(self)
Definition
errors.py:241
oc_chat.registry.errors.PreflightFailedError.reasons
tuple reasons
Definition
errors.py:239
oc_chat.registry.errors.RegistryError
Definition
errors.py:48
oc_chat.registry.errors.RotationInFlightError
Definition
errors.py:119
oc_chat.registry.errors.RotationInFlightError.__post_init__
None __post_init__(self)
Definition
errors.py:124
oc_chat.registry.errors.RoundInvariantError
Definition
errors.py:224
oc_chat.registry.errors.RoundInvariantError.__post_init__
None __post_init__(self)
Definition
errors.py:230
oc_chat.registry.errors.RoundStateError
Definition
errors.py:209
oc_chat.registry.errors.RoundStateError.__post_init__
None __post_init__(self)
Definition
errors.py:216
oc_chat.registry.errors.StagingBranchDivergedError
Definition
errors.py:280
oc_chat.registry.errors.StagingBranchDivergedError.__post_init__
None __post_init__(self)
Definition
errors.py:287
oc_chat.registry.errors.UnknownActorError
Definition
errors.py:199
oc_chat.registry.errors.UnknownActorError.__post_init__
None __post_init__(self)
Definition
errors.py:204
oc_chat
registry
errors.py
Generated by
1.15.0