projectdavid-orm 1.13.1__tar.gz → 1.14.0__tar.gz
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.
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/CHANGELOG.md +7 -0
- {projectdavid_orm-1.13.1/src/projectdavid_orm.egg-info → projectdavid_orm-1.14.0}/PKG-INFO +1 -1
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/pyproject.toml +1 -1
- projectdavid_orm-1.14.0/src/projectdavid_orm/projectdavid_orm/execution_ledger.py +340 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/projectdavid_orm/models.py +4 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0/src/projectdavid_orm.egg-info}/PKG-INFO +1 -1
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm.egg-info/SOURCES.txt +3 -0
- projectdavid_orm-1.14.0/tests/test_execution_ledger_registration.py +42 -0
- projectdavid_orm-1.14.0/tests/test_execution_ledger_schema.py +82 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/LICENSE +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/MANIFEST.in +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/README.md +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/setup.cfg +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/PKG-INFO +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/SOURCES.txt +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/dependency_links.txt +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/entry_points.txt +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/requires.txt +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/top_level.txt +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/__init__.py +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/constants/__init__.py +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/enums.py +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/ormInterface.py +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/projectdavid_orm/__init__.py +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/projectdavid_orm/base.py +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/utilities/__init__.py +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm.egg-info/dependency_links.txt +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm.egg-info/requires.txt +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm.egg-info/top_level.txt +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/tests/test_clients.py +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/tests/test_runtime_dependency_boundary.py +0 -0
- {projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/tests/test_scratchpad_public_exports.py +0 -0
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [1.14.0](https://github.com/project-david-ai/projectdavid-orm/compare/v1.13.1...v1.14.0) (2026-11-13)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **orm:** add durable execution lineage and forensic audit models ([21f89d3](https://github.com/project-david-ai/projectdavid-orm/commit/21f89d385273d21e39fcc32911039fcde6457032))
|
|
7
|
+
|
|
1
8
|
## [1.13.1](https://github.com/project-david-ai/projectdavid-orm/compare/v1.13.0...v1.13.1) (2026-09-29)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""Durable execution lineage and forensic evidence metadata.
|
|
2
|
+
|
|
3
|
+
These tables survive normal ephemeral Assistant lifecycle operations.
|
|
4
|
+
|
|
5
|
+
Foreign keys to Runs and Actions use RESTRICT, not CASCADE. Controlled
|
|
6
|
+
retention and erasure services must reconcile lineage before deletion.
|
|
7
|
+
|
|
8
|
+
Import this module to register its models on the shared ORM Base.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
from sqlalchemy import (
|
|
16
|
+
JSON,
|
|
17
|
+
BigInteger,
|
|
18
|
+
Boolean,
|
|
19
|
+
Column,
|
|
20
|
+
ForeignKey,
|
|
21
|
+
Index,
|
|
22
|
+
Integer,
|
|
23
|
+
String,
|
|
24
|
+
UniqueConstraint,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from .base import Base
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _now() -> int:
|
|
31
|
+
return int(time.time())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _now_us() -> int:
|
|
35
|
+
return time.time_ns() // 1_000
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class RunExecutionEpoch(Base):
|
|
39
|
+
"""One execution attempt belonging to a durable logical Run."""
|
|
40
|
+
|
|
41
|
+
__tablename__ = "run_execution_epochs"
|
|
42
|
+
|
|
43
|
+
id = Column(String(64), primary_key=True)
|
|
44
|
+
|
|
45
|
+
run_id = Column(
|
|
46
|
+
String(64),
|
|
47
|
+
ForeignKey("runs.id", ondelete="RESTRICT"),
|
|
48
|
+
nullable=False,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
root_run_id = Column(
|
|
52
|
+
String(64),
|
|
53
|
+
ForeignKey("runs.id", ondelete="RESTRICT"),
|
|
54
|
+
nullable=False,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
owner_user_id = Column(String(64), nullable=False)
|
|
58
|
+
epoch_number = Column(Integer, nullable=False)
|
|
59
|
+
|
|
60
|
+
state = Column(
|
|
61
|
+
String(32),
|
|
62
|
+
nullable=False,
|
|
63
|
+
default="reserved",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
created_at = Column(BigInteger, nullable=False, default=_now)
|
|
67
|
+
started_at = Column(BigInteger, nullable=True)
|
|
68
|
+
last_seen_at = Column(BigInteger, nullable=True)
|
|
69
|
+
ended_at = Column(BigInteger, nullable=True)
|
|
70
|
+
terminal_status = Column(String(32), nullable=True)
|
|
71
|
+
|
|
72
|
+
__table_args__ = (
|
|
73
|
+
UniqueConstraint(
|
|
74
|
+
"run_id",
|
|
75
|
+
"epoch_number",
|
|
76
|
+
name="uq_exec_epoch_run_number",
|
|
77
|
+
),
|
|
78
|
+
Index(
|
|
79
|
+
"ix_exec_epoch_root_created",
|
|
80
|
+
"root_run_id",
|
|
81
|
+
"created_at",
|
|
82
|
+
),
|
|
83
|
+
Index(
|
|
84
|
+
"ix_exec_epoch_owner_root",
|
|
85
|
+
"owner_user_id",
|
|
86
|
+
"root_run_id",
|
|
87
|
+
),
|
|
88
|
+
Index(
|
|
89
|
+
"ix_exec_epoch_state_seen",
|
|
90
|
+
"state",
|
|
91
|
+
"last_seen_at",
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ExecutionActor(Base):
|
|
97
|
+
"""Durable actor reservation and identity snapshot.
|
|
98
|
+
|
|
99
|
+
One actor can occupy a named slot within a Run execution epoch.
|
|
100
|
+
Database uniqueness prevents competing supervisor reservations.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
__tablename__ = "execution_actors"
|
|
104
|
+
|
|
105
|
+
id = Column(String(64), primary_key=True)
|
|
106
|
+
|
|
107
|
+
epoch_id = Column(
|
|
108
|
+
String(64),
|
|
109
|
+
ForeignKey(
|
|
110
|
+
"run_execution_epochs.id",
|
|
111
|
+
ondelete="RESTRICT",
|
|
112
|
+
),
|
|
113
|
+
nullable=False,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
slot = Column(String(48), nullable=False)
|
|
117
|
+
execution_role = Column(String(48), nullable=False)
|
|
118
|
+
|
|
119
|
+
# Snapshots intentionally have no FK to assistants.
|
|
120
|
+
# Assistant cleanup must not invalidate execution provenance.
|
|
121
|
+
assistant_id_snapshot = Column(String(64), nullable=True)
|
|
122
|
+
model_snapshot = Column(String(128), nullable=True)
|
|
123
|
+
|
|
124
|
+
ephemeral = Column(Boolean, nullable=False, default=False)
|
|
125
|
+
|
|
126
|
+
state = Column(
|
|
127
|
+
String(32),
|
|
128
|
+
nullable=False,
|
|
129
|
+
default="reserved",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
reserved_at = Column(BigInteger, nullable=False, default=_now)
|
|
133
|
+
activated_at = Column(BigInteger, nullable=True)
|
|
134
|
+
retired_at = Column(BigInteger, nullable=True)
|
|
135
|
+
last_seen_at = Column(BigInteger, nullable=True)
|
|
136
|
+
|
|
137
|
+
__table_args__ = (
|
|
138
|
+
UniqueConstraint(
|
|
139
|
+
"epoch_id",
|
|
140
|
+
"slot",
|
|
141
|
+
name="uq_exec_actor_epoch_slot",
|
|
142
|
+
),
|
|
143
|
+
Index(
|
|
144
|
+
"ix_exec_actor_assistant",
|
|
145
|
+
"assistant_id_snapshot",
|
|
146
|
+
),
|
|
147
|
+
Index(
|
|
148
|
+
"ix_exec_actor_state_seen",
|
|
149
|
+
"state",
|
|
150
|
+
"last_seen_at",
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class RunDelegation(Base):
|
|
156
|
+
"""Links a parent Action to a delegated child execution."""
|
|
157
|
+
|
|
158
|
+
__tablename__ = "run_delegations"
|
|
159
|
+
|
|
160
|
+
id = Column(String(64), primary_key=True)
|
|
161
|
+
|
|
162
|
+
parent_epoch_id = Column(
|
|
163
|
+
String(64),
|
|
164
|
+
ForeignKey(
|
|
165
|
+
"run_execution_epochs.id",
|
|
166
|
+
ondelete="RESTRICT",
|
|
167
|
+
),
|
|
168
|
+
nullable=False,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
child_epoch_id = Column(
|
|
172
|
+
String(64),
|
|
173
|
+
ForeignKey(
|
|
174
|
+
"run_execution_epochs.id",
|
|
175
|
+
ondelete="RESTRICT",
|
|
176
|
+
),
|
|
177
|
+
nullable=False,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
parent_action_id = Column(
|
|
181
|
+
String(64),
|
|
182
|
+
ForeignKey("actions.id", ondelete="RESTRICT"),
|
|
183
|
+
nullable=False,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
initiating_actor_id = Column(
|
|
187
|
+
String(64),
|
|
188
|
+
ForeignKey("execution_actors.id", ondelete="RESTRICT"),
|
|
189
|
+
nullable=True,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
created_at = Column(BigInteger, nullable=False, default=_now)
|
|
193
|
+
|
|
194
|
+
__table_args__ = (
|
|
195
|
+
UniqueConstraint(
|
|
196
|
+
"child_epoch_id",
|
|
197
|
+
name="uq_run_delegation_child_epoch",
|
|
198
|
+
),
|
|
199
|
+
Index(
|
|
200
|
+
"ix_run_delegation_parent",
|
|
201
|
+
"parent_epoch_id",
|
|
202
|
+
),
|
|
203
|
+
Index(
|
|
204
|
+
"ix_run_delegation_parent_action",
|
|
205
|
+
"parent_action_id",
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class ExecutionEvent(Base):
|
|
211
|
+
"""Structured, ordered execution evidence.
|
|
212
|
+
|
|
213
|
+
Raw reasoning, credentials and unfiltered tool outputs must not be
|
|
214
|
+
stored in details. The writer service must enforce an allowlist,
|
|
215
|
+
redact secrets, and protect events against unauthorised mutation.
|
|
216
|
+
|
|
217
|
+
Evidence digests require protected external checkpoints to provide
|
|
218
|
+
meaningful tamper-evidence.
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
__tablename__ = "execution_events"
|
|
222
|
+
|
|
223
|
+
id = Column(
|
|
224
|
+
BigInteger,
|
|
225
|
+
primary_key=True,
|
|
226
|
+
autoincrement=True,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
epoch_id = Column(
|
|
230
|
+
String(64),
|
|
231
|
+
ForeignKey(
|
|
232
|
+
"run_execution_epochs.id",
|
|
233
|
+
ondelete="RESTRICT",
|
|
234
|
+
),
|
|
235
|
+
nullable=False,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
actor_id = Column(
|
|
239
|
+
String(64),
|
|
240
|
+
ForeignKey(
|
|
241
|
+
"execution_actors.id",
|
|
242
|
+
ondelete="RESTRICT",
|
|
243
|
+
),
|
|
244
|
+
nullable=True,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
origin_run_id = Column(
|
|
248
|
+
String(64),
|
|
249
|
+
ForeignKey("runs.id", ondelete="RESTRICT"),
|
|
250
|
+
nullable=False,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
event_type = Column(String(64), nullable=False)
|
|
254
|
+
schema_version = Column(Integer, nullable=False, default=1)
|
|
255
|
+
|
|
256
|
+
evidence_class = Column(
|
|
257
|
+
String(24),
|
|
258
|
+
nullable=False,
|
|
259
|
+
default="operational",
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Microsecond timestamps permit high-resolution reconstruction
|
|
263
|
+
# of interleaved agent and tool activity.
|
|
264
|
+
occurred_at_us = Column(BigInteger, nullable=False)
|
|
265
|
+
recorded_at_us = Column(
|
|
266
|
+
BigInteger,
|
|
267
|
+
nullable=False,
|
|
268
|
+
default=_now_us,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
evidence_expires_at = Column(BigInteger, nullable=True)
|
|
272
|
+
|
|
273
|
+
action_id_snapshot = Column(String(64), nullable=True)
|
|
274
|
+
tool_call_id_snapshot = Column(String(64), nullable=True)
|
|
275
|
+
|
|
276
|
+
details = Column(JSON, nullable=False, default=dict)
|
|
277
|
+
|
|
278
|
+
# Opaque reference to governed, separately protected evidence.
|
|
279
|
+
evidence_ref = Column(String(255), nullable=True)
|
|
280
|
+
evidence_sha256 = Column(String(64), nullable=True)
|
|
281
|
+
|
|
282
|
+
previous_digest = Column(String(64), nullable=True)
|
|
283
|
+
record_digest = Column(String(64), nullable=True)
|
|
284
|
+
|
|
285
|
+
__table_args__ = (
|
|
286
|
+
Index(
|
|
287
|
+
"ix_exec_event_epoch_id",
|
|
288
|
+
"epoch_id",
|
|
289
|
+
"id",
|
|
290
|
+
),
|
|
291
|
+
Index(
|
|
292
|
+
"ix_exec_event_origin_time",
|
|
293
|
+
"origin_run_id",
|
|
294
|
+
"occurred_at_us",
|
|
295
|
+
),
|
|
296
|
+
Index(
|
|
297
|
+
"ix_exec_event_type_time",
|
|
298
|
+
"event_type",
|
|
299
|
+
"occurred_at_us",
|
|
300
|
+
),
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class EvidenceRetentionHold(Base):
|
|
305
|
+
"""Authorised preservation hold for one root Run.
|
|
306
|
+
|
|
307
|
+
An open hold blocks ordinary retention purging. Releasing a hold
|
|
308
|
+
does not independently authorise deletion of otherwise protected data.
|
|
309
|
+
"""
|
|
310
|
+
|
|
311
|
+
__tablename__ = "evidence_retention_holds"
|
|
312
|
+
|
|
313
|
+
id = Column(String(64), primary_key=True)
|
|
314
|
+
|
|
315
|
+
root_run_id = Column(
|
|
316
|
+
String(64),
|
|
317
|
+
ForeignKey("runs.id", ondelete="RESTRICT"),
|
|
318
|
+
nullable=False,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
case_reference = Column(String(128), nullable=False)
|
|
322
|
+
reason_code = Column(String(48), nullable=False)
|
|
323
|
+
authorised_by = Column(String(64), nullable=False)
|
|
324
|
+
|
|
325
|
+
created_at = Column(BigInteger, nullable=False, default=_now)
|
|
326
|
+
reviewed_at = Column(BigInteger, nullable=True)
|
|
327
|
+
expires_at = Column(BigInteger, nullable=True)
|
|
328
|
+
released_at = Column(BigInteger, nullable=True)
|
|
329
|
+
|
|
330
|
+
__table_args__ = (
|
|
331
|
+
Index(
|
|
332
|
+
"ix_evidence_hold_root_released",
|
|
333
|
+
"root_run_id",
|
|
334
|
+
"released_at",
|
|
335
|
+
),
|
|
336
|
+
Index(
|
|
337
|
+
"ix_evidence_hold_case",
|
|
338
|
+
"case_reference",
|
|
339
|
+
),
|
|
340
|
+
)
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/projectdavid_orm/models.py
RENAMED
|
@@ -21,6 +21,10 @@ from sqlalchemy.orm import joinedload, relationship
|
|
|
21
21
|
|
|
22
22
|
from projectdavid_orm.enums import StatusEnum
|
|
23
23
|
|
|
24
|
+
# Register execution-lineage models on the shared declarative Base.
|
|
25
|
+
# This import is intentionally placed after existing model declarations.
|
|
26
|
+
# Required for Alembic and normal ORM metadata discovery.
|
|
27
|
+
from . import execution_ledger as _execution_ledger # noqa: E402, F401
|
|
24
28
|
from .base import Base
|
|
25
29
|
|
|
26
30
|
# --- Association Tables ---
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm.egg-info/SOURCES.txt
RENAMED
|
@@ -20,8 +20,11 @@ src/projectdavid_orm.egg-info/top_level.txt
|
|
|
20
20
|
src/projectdavid_orm/constants/__init__.py
|
|
21
21
|
src/projectdavid_orm/projectdavid_orm/__init__.py
|
|
22
22
|
src/projectdavid_orm/projectdavid_orm/base.py
|
|
23
|
+
src/projectdavid_orm/projectdavid_orm/execution_ledger.py
|
|
23
24
|
src/projectdavid_orm/projectdavid_orm/models.py
|
|
24
25
|
src/projectdavid_orm/utilities/__init__.py
|
|
25
26
|
tests/test_clients.py
|
|
27
|
+
tests/test_execution_ledger_registration.py
|
|
28
|
+
tests/test_execution_ledger_schema.py
|
|
26
29
|
tests/test_runtime_dependency_boundary.py
|
|
27
30
|
tests/test_scratchpad_public_exports.py
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Verify normal ORM imports register the execution ledger.
|
|
2
|
+
|
|
3
|
+
The test runs in a fresh Python interpreter so that earlier test imports
|
|
4
|
+
cannot accidentally register the tables and mask a packaging defect.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_fresh_models_import_registers_execution_ledger():
|
|
12
|
+
code = """
|
|
13
|
+
from projectdavid_orm.projectdavid_orm import models
|
|
14
|
+
|
|
15
|
+
expected = {
|
|
16
|
+
"run_execution_epochs",
|
|
17
|
+
"execution_actors",
|
|
18
|
+
"run_delegations",
|
|
19
|
+
"execution_events",
|
|
20
|
+
"evidence_retention_holds",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
registered = set(models.User.metadata.tables)
|
|
24
|
+
missing = expected - registered
|
|
25
|
+
|
|
26
|
+
assert not missing, (
|
|
27
|
+
f"Unregistered execution ledger tables: {sorted(missing)}"
|
|
28
|
+
)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
result = subprocess.run(
|
|
32
|
+
[sys.executable, "-c", code],
|
|
33
|
+
capture_output=True,
|
|
34
|
+
text=True,
|
|
35
|
+
check=False,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
assert result.returncode == 0, (
|
|
39
|
+
"Fresh ORM import failed.\n"
|
|
40
|
+
f"STDOUT:\n{result.stdout}\n"
|
|
41
|
+
f"STDERR:\n{result.stderr}"
|
|
42
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Structural tests for Project David execution-lineage models."""
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import UniqueConstraint
|
|
4
|
+
from sqlalchemy.dialects.mysql import dialect as mysql_dialect
|
|
5
|
+
from sqlalchemy.schema import CreateTable
|
|
6
|
+
|
|
7
|
+
from projectdavid_orm.projectdavid_orm import execution_ledger, models
|
|
8
|
+
|
|
9
|
+
EXPECTED_TABLES = {
|
|
10
|
+
"run_execution_epochs",
|
|
11
|
+
"execution_actors",
|
|
12
|
+
"run_delegations",
|
|
13
|
+
"execution_events",
|
|
14
|
+
"evidence_retention_holds",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_models_share_existing_metadata():
|
|
19
|
+
metadata = models.User.metadata
|
|
20
|
+
|
|
21
|
+
assert EXPECTED_TABLES.issubset(metadata.tables.keys())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_supervisor_and_epoch_uniqueness():
|
|
25
|
+
metadata = models.User.metadata
|
|
26
|
+
|
|
27
|
+
expected = {
|
|
28
|
+
"run_execution_epochs": "uq_exec_epoch_run_number",
|
|
29
|
+
"execution_actors": "uq_exec_actor_epoch_slot",
|
|
30
|
+
"run_delegations": "uq_run_delegation_child_epoch",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for table_name, constraint_name in expected.items():
|
|
34
|
+
table = metadata.tables[table_name]
|
|
35
|
+
names = {
|
|
36
|
+
constraint.name
|
|
37
|
+
for constraint in table.constraints
|
|
38
|
+
if isinstance(constraint, UniqueConstraint)
|
|
39
|
+
}
|
|
40
|
+
assert constraint_name in names
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_protected_foreign_keys_restrict_deletion():
|
|
44
|
+
metadata = models.User.metadata
|
|
45
|
+
|
|
46
|
+
for name in EXPECTED_TABLES:
|
|
47
|
+
table = metadata.tables[name]
|
|
48
|
+
|
|
49
|
+
assert table.foreign_keys
|
|
50
|
+
|
|
51
|
+
for foreign_key in table.foreign_keys:
|
|
52
|
+
assert foreign_key.ondelete == "RESTRICT"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_actor_identity_survives_assistant_deletion():
|
|
56
|
+
actor_table = models.User.metadata.tables["execution_actors"]
|
|
57
|
+
|
|
58
|
+
assistant_id = actor_table.c.assistant_id_snapshot
|
|
59
|
+
|
|
60
|
+
# This is an identity snapshot, deliberately not a foreign key.
|
|
61
|
+
assert not assistant_id.foreign_keys
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_forensic_event_resolution():
|
|
65
|
+
table = models.User.metadata.tables["execution_events"]
|
|
66
|
+
|
|
67
|
+
assert "occurred_at_us" in table.c
|
|
68
|
+
assert "recorded_at_us" in table.c
|
|
69
|
+
assert "evidence_sha256" in table.c
|
|
70
|
+
assert "evidence_expires_at" in table.c
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_mysql_ddl_compiles_for_every_new_table():
|
|
74
|
+
metadata = models.User.metadata
|
|
75
|
+
dialect = mysql_dialect()
|
|
76
|
+
|
|
77
|
+
for table_name in EXPECTED_TABLES:
|
|
78
|
+
statement = CreateTable(metadata.tables[table_name])
|
|
79
|
+
compiled = str(statement.compile(dialect=dialect))
|
|
80
|
+
|
|
81
|
+
assert "CREATE TABLE" in compiled
|
|
82
|
+
assert "RESTRICT" in compiled
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/PKG-INFO
RENAMED
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/requires.txt
RENAMED
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_common.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/constants/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/projectdavid_orm/base.py
RENAMED
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm/utilities/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm.egg-info/requires.txt
RENAMED
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/src/projectdavid_orm.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
{projectdavid_orm-1.13.1 → projectdavid_orm-1.14.0}/tests/test_runtime_dependency_boundary.py
RENAMED
|
File without changes
|
|
File without changes
|