clm-kernel 0.0.0__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.
- clm/__init__.py +230 -0
- clm/__main__.py +25 -0
- clm/cross_cutting/__init__.py +50 -0
- clm/cross_cutting/_test_runner.py +36 -0
- clm/cross_cutting/async_utils.py +25 -0
- clm/cross_cutting/cel_builder.py +123 -0
- clm/cross_cutting/cel_converter.py +697 -0
- clm/cross_cutting/cel_engine.py +252 -0
- clm/cross_cutting/cel_ops.py +72 -0
- clm/cross_cutting/cel_ops_db.py +157 -0
- clm/cross_cutting/cel_ops_files.py +99 -0
- clm/cross_cutting/cel_ops_state.py +71 -0
- clm/cross_cutting/cel_ops_strings.py +64 -0
- clm/cross_cutting/cel_resolver.py +63 -0
- clm/cross_cutting/cel_slices.py +56 -0
- clm/cross_cutting/cel_store.py +330 -0
- clm/cross_cutting/clm_logger.py +140 -0
- clm/cross_cutting/config/__init__.py +155 -0
- clm/cross_cutting/config/config_constants.py +392 -0
- clm/cross_cutting/config/env_parameters.py +90 -0
- clm/cross_cutting/config/logging.py +378 -0
- clm/cross_cutting/config/settings.py +504 -0
- clm/cross_cutting/context.py +5 -0
- clm/cross_cutting/domain_models.py +55 -0
- clm/cross_cutting/domain_types.py +61 -0
- clm/cross_cutting/effects.py +67 -0
- clm/cross_cutting/errors.py +153 -0
- clm/cross_cutting/governance.py +331 -0
- clm/cross_cutting/guards.py +41 -0
- clm/cross_cutting/mcard.py +321 -0
- clm/cross_cutting/native.py +185 -0
- clm/cross_cutting/observability.py +289 -0
- clm/cross_cutting/pcard.py +434 -0
- clm/cross_cutting/registry.py +71 -0
- clm/cross_cutting/resources.py +84 -0
- clm/cross_cutting/secrets.py +44 -0
- clm/cross_cutting/subprocess_utils.py +118 -0
- clm/cross_cutting/telemetry.py +164 -0
- clm/cross_cutting/testing/__init__.py +8 -0
- clm/cross_cutting/testing/comparator.py +187 -0
- clm/cross_cutting/testing/datasets.py +87 -0
- clm/cross_cutting/timeutil.py +20 -0
- clm/cross_cutting/types/new_type_check.py +0 -0
- clm/cross_cutting/utils/__init__.py +1 -0
- clm/cross_cutting/utils/url_safety.py +50 -0
- clm/cross_cutting/vcard.py +560 -0
- clm/cross_cutting/writer.py +118 -0
- clm/layer0/README.md +50 -0
- clm/layer0/__init__.py +223 -0
- clm/layer0/algebra.py +60 -0
- clm/layer0/algebra_baldwin.py +133 -0
- clm/layer0/algebra_baldwin_generators.py +36 -0
- clm/layer0/algebra_equivalence.py +262 -0
- clm/layer0/algebra_ingest.py +306 -0
- clm/layer0/algebra_manifest_io.py +156 -0
- clm/layer0/assembler.py +481 -0
- clm/layer0/cel/__init__.py +23 -0
- clm/layer0/cel_eval.py +491 -0
- clm/layer0/codec.py +106 -0
- clm/layer0/context.py +122 -0
- clm/layer0/db.py +160 -0
- clm/layer0/exceptions.py +73 -0
- clm/layer0/fibration.py +106 -0
- clm/layer0/gates.py +50 -0
- clm/layer0/hash.py +80 -0
- clm/layer0/loader.py +826 -0
- clm/layer0/math_utils.py +163 -0
- clm/layer0/mcard.py +96 -0
- clm/layer0/model/__init__.py +0 -0
- clm/layer0/model/action.py +223 -0
- clm/layer0/model/card.py +280 -0
- clm/layer0/model/card_triad.py +427 -0
- clm/layer0/model/compatibility.py +60 -0
- clm/layer0/model/dictionary.py +46 -0
- clm/layer0/model/dots.py +331 -0
- clm/layer0/model/event_producer.py +88 -0
- clm/layer0/model/g_time.py +89 -0
- clm/layer0/model/handle.py +163 -0
- clm/layer0/model/hash/__init__.py +6 -0
- clm/layer0/model/hash/algorithms/__init__.py +3 -0
- clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
- clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
- clm/layer0/model/hash/constants.py +53 -0
- clm/layer0/model/hash/enums.py +60 -0
- clm/layer0/model/hash/validator.py +307 -0
- clm/layer0/model/interpreter.py +275 -0
- clm/layer0/model/pagination.py +45 -0
- clm/layer0/model/pcard.py +716 -0
- clm/layer0/model/schema.py +44 -0
- clm/layer0/model/utils/__init__.py +1 -0
- clm/layer0/model/utils/content_analyzer.py +152 -0
- clm/layer0/model/validators/__init__.py +1 -0
- clm/layer0/model/validators/base_validator.py +44 -0
- clm/layer0/model/validators/binary_validator.py +66 -0
- clm/layer0/model/validators/text_validator.py +82 -0
- clm/layer0/model/validators/validation_registry.py +49 -0
- clm/layer0/model/vcard.py +823 -0
- clm/layer0/model/vcard_ext/__init__.py +39 -0
- clm/layer0/model/vcard_ext/core.py +60 -0
- clm/layer0/model/vcard_ext/network.py +23 -0
- clm/layer0/model/vcard_ext/observability.py +63 -0
- clm/layer0/model/vcard_ext/storage.py +60 -0
- clm/layer0/model/vcard_ext/vendors.py +211 -0
- clm/layer0/model/vcard_sandwich.py +119 -0
- clm/layer0/model/vcard_vocabulary.py +463 -0
- clm/layer0/model/workflow.py +274 -0
- clm/layer0/narrative.py +431 -0
- clm/layer0/ontology/collection.py +125 -0
- clm/layer0/parser.py +429 -0
- clm/layer0/relations.py +73 -0
- clm/layer0/rule_evaluator.py +251 -0
- clm/layer0/schema/__init__.py +38 -0
- clm/layer0/schema/ddl.py +97 -0
- clm/layer0/templates/abstract.yaml +99 -0
- clm/layer0/templates/balanced.yaml +253 -0
- clm/layer0/templates/concrete.yaml +172 -0
- clm/layer0/type_registry.py +223 -0
- clm/layer0/types.py +204 -0
- clm/layer0/utils.py +886 -0
- clm/layer0/verifier_core.py +262 -0
- clm/layer1/__init__.py +31 -0
- clm/layer1/action_dispatcher.py +275 -0
- clm/layer1/adapter_registry.py +233 -0
- clm/layer1/algebra_baldwin_generators.py +192 -0
- clm/layer1/arrow_compiler.py +525 -0
- clm/layer1/batch_processor.py +152 -0
- clm/layer1/cel/__init__.py +27 -0
- clm/layer1/cel/builder.py +173 -0
- clm/layer1/engine.py +759 -0
- clm/layer1/engine_async.py +89 -0
- clm/layer1/eval_callback.py +49 -0
- clm/layer1/gate_composer.py +229 -0
- clm/layer1/hoare_sandwich.py +298 -0
- clm/layer1/identity_guard.py +348 -0
- clm/layer1/matrix.py +138 -0
- clm/layer1/neural_runtime.py +221 -0
- clm/layer1/operations/__init__.py +123 -0
- clm/layer1/operations/augment.py +156 -0
- clm/layer1/operations/base.py +109 -0
- clm/layer1/operations/builtins.py +338 -0
- clm/layer1/operations/dispatch.py +144 -0
- clm/layer1/operations/exclude.py +107 -0
- clm/layer1/operations/handle.py +197 -0
- clm/layer1/operations/identity.py +139 -0
- clm/layer1/operations/invert.py +66 -0
- clm/layer1/operations/loader.py +116 -0
- clm/layer1/operations/manipulators/__init__.py +23 -0
- clm/layer1/operations/manipulators/document.py +211 -0
- clm/layer1/operations/manipulators/media.py +134 -0
- clm/layer1/operations/manipulators/structured.py +159 -0
- clm/layer1/operations/manipulators/tabular.py +334 -0
- clm/layer1/operations/port.py +65 -0
- clm/layer1/operations/services.py +529 -0
- clm/layer1/operations/split.py +117 -0
- clm/layer1/operations/substitute.py +75 -0
- clm/layer1/petri.py +1052 -0
- clm/layer1/pocketflow_orchestrator.py +408 -0
- clm/layer1/pocketflow_scheduler.py +519 -0
- clm/layer1/protocols.py +197 -0
- clm/layer1/runtime_adapter.py +718 -0
- clm/layer1/runtimes/__init__.py +71 -0
- clm/layer1/runtimes/_generated_assertions.py +125 -0
- clm/layer1/runtimes/_generated_types.py +42 -0
- clm/layer1/runtimes/base.py +265 -0
- clm/layer1/runtimes/binary.py +101 -0
- clm/layer1/runtimes/factory.py +203 -0
- clm/layer1/runtimes/javascript.py +321 -0
- clm/layer1/runtimes/javascript_runtime.js +33 -0
- clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
- clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
- clm/layer1/runtimes/lambda_calc.py +126 -0
- clm/layer1/runtimes/python.py +288 -0
- clm/layer1/runtimes/python_runtime.py +41 -0
- clm/layer1/runtimes/script.py +114 -0
- clm/layer1/sandbox.py +334 -0
- clm/layer1/savepoint_guard.py +136 -0
- clm/layer1/sparse_net.py +122 -0
- clm/layer1/spec_tester.py +165 -0
- clm/layer1/store.py +295 -0
- clm/layer1/test_runner.py +498 -0
- clm/layer1/type_inspector.py +166 -0
- clm/layer1/verifier.py +772 -0
- clm/layer1/vm.py +91 -0
- clm/layer2/__init__.py +78 -0
- clm/layer2/card_collection.py +678 -0
- clm/layer2/classifier.py +479 -0
- clm/layer2/engine/__init__.py +1 -0
- clm/layer2/engine/abstract_sql_engine.py +320 -0
- clm/layer2/engine/base.py +193 -0
- clm/layer2/engine/duckdb_engine.py +576 -0
- clm/layer2/engine/sqlite_engine.py +518 -0
- clm/layer2/fiber/__init__.py +31 -0
- clm/layer2/fiber/context.py +89 -0
- clm/layer2/fiber/disposable.py +163 -0
- clm/layer2/fiber/fiber.py +125 -0
- clm/layer2/fiber/hoare.py +191 -0
- clm/layer2/file_io.py +376 -0
- clm/layer2/file_registrar.py +77 -0
- clm/layer2/ingest.py +202 -0
- clm/layer2/lineage.py +322 -0
- clm/layer2/mcard_fs.py +262 -0
- clm/layer2/merkle.py +11 -0
- clm/layer2/mime.py +244 -0
- clm/layer2/mime_detector.py +245 -0
- clm/layer2/private_collection.py +118 -0
- clm/layer2/shared_store.py +11 -0
- clm/layer2/storage/__init__.py +190 -0
- clm/layer2/storage/db_bitemporal.py +141 -0
- clm/layer2/storage/db_driver.py +331 -0
- clm/layer2/storage/db_hyperlinks.py +214 -0
- clm/layer2/storage/db_pool.py +139 -0
- clm/layer2/storage/db_repository.py +229 -0
- clm/layer2/storage/savepoint.py +78 -0
- clm/layer2/storage/sqlite_wal.py +74 -0
- clm/layer2/tridb.py +245 -0
- clm/layer2/vcard_receipt.py +71 -0
- clm/layer2/vfs.py +17 -0
- clm/layer3/__init__.py +141 -0
- clm/layer3/agency/__init__.py +19 -0
- clm/layer3/agency/config.py +220 -0
- clm/layer3/agency/providers/__init__.py +15 -0
- clm/layer3/agency/providers/base.py +91 -0
- clm/layer3/agency/providers/mlc_llm.py +169 -0
- clm/layer3/agency/providers/ollama.py +203 -0
- clm/layer3/agency/router.py +71 -0
- clm/layer3/agency/runtime.py +524 -0
- clm/layer3/gateway.py +353 -0
- clm/layer3/open_interpreter_adapter.py +159 -0
- clm/layer3/rag/__init__.py +70 -0
- clm/layer3/rag/cli.py +228 -0
- clm/layer3/rag/config.py +153 -0
- clm/layer3/rag/embeddings/__init__.py +16 -0
- clm/layer3/rag/embeddings/base.py +79 -0
- clm/layer3/rag/embeddings/ollama.py +211 -0
- clm/layer3/rag/embeddings/vision.py +290 -0
- clm/layer3/rag/engine.py +276 -0
- clm/layer3/rag/graph/__init__.py +22 -0
- clm/layer3/rag/graph/community.py +198 -0
- clm/layer3/rag/graph/engine.py +433 -0
- clm/layer3/rag/graph/extractor.py +255 -0
- clm/layer3/rag/graph/schema.py +115 -0
- clm/layer3/rag/graph/store.py +641 -0
- clm/layer3/rag/indexer.py +333 -0
- clm/layer3/rag/llm_providers.json +52 -0
- clm/layer3/rag/semantic_versioning.py +313 -0
- clm/layer3/rag/vector/__init__.py +28 -0
- clm/layer3/rag/vector/handle_vector_store.py +749 -0
- clm/layer3/rag/vector/schema.py +151 -0
- clm/layer3/rag/vector/store.py +632 -0
- clm/layer3/reticulum/__init__.py +55 -0
- clm/layer3/reticulum/bridge.py +74 -0
- clm/layer3/reticulum/daemon.py +88 -0
- clm/layer3/reticulum/discovery.py +164 -0
- clm/layer3/reticulum/identity.py +107 -0
- clm/layer3/reticulum/media.py +148 -0
- clm/layer3/reticulum/transport.py +133 -0
- clm/layer3/satori/__init__.py +209 -0
- clm/layer3/satori/alpha_conversion.py +168 -0
- clm/layer3/satori/beta_reduction.py +359 -0
- clm/layer3/satori/continuation.py +38 -0
- clm/layer3/satori/eta_conversion.py +205 -0
- clm/layer3/satori/free_variables.py +163 -0
- clm/layer3/satori/io_effects.py +370 -0
- clm/layer3/satori/lambda_runtime.py +726 -0
- clm/layer3/satori/lambda_term.py +230 -0
- clm/layer3/satori/sheaf_audit.py +261 -0
- clm/layer3/satori/speech_act.py +279 -0
- clm/layer4/README.md +9 -0
- clm/layer4/__init__.py +67 -0
- clm/layer4/_sovereign_executor.py +580 -0
- clm/layer4/action_commands.py +719 -0
- clm/layer4/api.py +236 -0
- clm/layer4/bootstrap/__init__.py +13 -0
- clm/layer4/bootstrap/bootstrapper.py +118 -0
- clm/layer4/bootstrap/cleanup.py +44 -0
- clm/layer4/bootstrap/clm_hooks.py +49 -0
- clm/layer4/bootstrap/genesis.py +77 -0
- clm/layer4/bootstrap/injection.py +125 -0
- clm/layer4/cli.py +549 -0
- clm/layer4/collection_manager.py +553 -0
- clm/layer4/commands/__init__.py +81 -0
- clm/layer4/commands/action_cmd.py +42 -0
- clm/layer4/commands/check_cmd.py +82 -0
- clm/layer4/commands/envelope.py +73 -0
- clm/layer4/commands/evaluate_cmd.py +224 -0
- clm/layer4/commands/mcard_cmd.py +24 -0
- clm/layer4/commands/petri_cmd.py +92 -0
- clm/layer4/commands/tridb_cmd.py +319 -0
- clm/layer4/commands/version_cmd.py +27 -0
- clm/layer4/diff_commands.py +167 -0
- clm/layer4/eoa_sponsor.py +232 -0
- clm/layer4/events.py +69 -0
- clm/layer4/flux_gateway.py +133 -0
- clm/layer4/gateway.py +91 -0
- clm/layer4/loader.py +307 -0
- clm/layer4/mcard_commands.py +166 -0
- clm/layer4/membrane.py +273 -0
- clm/layer4/navigation.py +50 -0
- clm/layer4/protocol/protocol_entry_python.py +108 -0
- clm/layer4/protocol/python_sync.py +126 -0
- clm/layer4/runner.py +150 -0
- clm/layer4/satori.py +262 -0
- clm/layer4/server/python_sync_client.py +150 -0
- clm/layer4/server/python_sync_server.py +139 -0
- clm/layer4/server/service_api.py +315 -0
- clm/layer4/server/start_servers.py +107 -0
- clm/layer4/server/stop_servers.py +24 -0
- clm/layer4/simulated_optimization_fix.py +0 -0
- clm/layer4/storage.py +463 -0
- clm/layer4/tridb_admin.py +268 -0
- clm/layer4/websocket/test_ws_client.py +104 -0
- clm/layer4/websocket/test_ws_server.py +138 -0
- clm/layer4/websocket/ws_server_local.py +160 -0
- clm/layer5/__init__.py +51 -0
- clm/layer5/async_batch_writer.py +133 -0
- clm/layer5/learning.py +276 -0
- clm/layer5/telemetry_hook.py +107 -0
- clm/layer5/type_lattice.py +238 -0
- clm/py.typed +0 -0
- clm/schemas/__init__.py +652 -0
- clm/schemas/abstract_face_schema.yaml +77 -0
- clm/schemas/cel_schema.yaml +416 -0
- clm/schemas/lifecycle/__init__.py +45 -0
- clm/schemas/lifecycle/models.py +123 -0
- clm/schemas/lifecycle/service.py +364 -0
- clm/schemas/mcard_schema.sql +192 -0
- clm/schemas/mcard_vector_schema.sql +283 -0
- clm/schemas/mlp-context-v1.json +91 -0
- clm_kernel-0.0.0.dist-info/METADATA +121 -0
- clm_kernel-0.0.0.dist-info/RECORD +333 -0
- clm_kernel-0.0.0.dist-info/WHEEL +4 -0
- clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
- clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Cordis DisposableList & Savepoint Guard (DEL-389-01, INV-389-02, INV-389-03).
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
1. DisposableList: Strict LIFO stack of reversible side-effects for zero-residual state.
|
|
5
|
+
2. SavepointGuard: RAII SQLite savepoint manager with <1ms transactional rollback.
|
|
6
|
+
3. TriDbSavepointGuard: Multi-database atomic savepoint coordinator across the Triad.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import sqlite3
|
|
12
|
+
import time
|
|
13
|
+
import uuid
|
|
14
|
+
from typing import Callable
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DisposableList:
|
|
20
|
+
"""Strict LIFO stack of cleanup/rollback closures for Cordis fibers.
|
|
21
|
+
|
|
22
|
+
Enforces INV-389-02: Disposers execute in exact reverse registration order.
|
|
23
|
+
Aggregates exceptions so that all disposers run even if one raises.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self) -> None:
|
|
27
|
+
self._disposables: list[Callable[[], None]] = []
|
|
28
|
+
|
|
29
|
+
def push(self, disposable: Callable[[], None]) -> None:
|
|
30
|
+
"""Register a cleanup closure to be executed on disposal."""
|
|
31
|
+
if callable(disposable):
|
|
32
|
+
self._disposables.append(disposable)
|
|
33
|
+
|
|
34
|
+
def dispose(self) -> list[Exception]:
|
|
35
|
+
"""Execute all registered closures in reverse-LIFO order.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
List of any exceptions raised during disposal.
|
|
39
|
+
"""
|
|
40
|
+
errors: list[Exception] = []
|
|
41
|
+
while self._disposables:
|
|
42
|
+
fn = self._disposables.pop()
|
|
43
|
+
try:
|
|
44
|
+
fn()
|
|
45
|
+
except Exception as e:
|
|
46
|
+
logger.error(f"[DisposableList] Disposer failed: {e}", exc_info=True)
|
|
47
|
+
errors.append(e)
|
|
48
|
+
return errors
|
|
49
|
+
|
|
50
|
+
def clear(self) -> None:
|
|
51
|
+
"""Clear all registered disposers without executing them."""
|
|
52
|
+
self._disposables.clear()
|
|
53
|
+
|
|
54
|
+
def __len__(self) -> int:
|
|
55
|
+
return len(self._disposables)
|
|
56
|
+
|
|
57
|
+
def __bool__(self) -> bool:
|
|
58
|
+
return len(self._disposables) > 0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SavepointGuard:
|
|
62
|
+
"""RAII SQLite savepoint manager with sub-millisecond rollback (INV-389-03)."""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
conn: sqlite3.Connection,
|
|
67
|
+
name: str | None = None,
|
|
68
|
+
disposables: DisposableList | None = None,
|
|
69
|
+
) -> None:
|
|
70
|
+
self.conn = conn
|
|
71
|
+
self.name = name or f"sp_{uuid.uuid4().hex[:8]}"
|
|
72
|
+
self.disposables = disposables or DisposableList()
|
|
73
|
+
self._rolled_back = False
|
|
74
|
+
self._released = False
|
|
75
|
+
self.rollback_duration_ms: float = 0.0
|
|
76
|
+
|
|
77
|
+
def begin(self) -> None:
|
|
78
|
+
"""Issue SAVEPOINT statement."""
|
|
79
|
+
self.conn.execute(f"SAVEPOINT {self.name};")
|
|
80
|
+
|
|
81
|
+
def rollback(self) -> None:
|
|
82
|
+
"""Roll back to savepoint and unwind disposables in LIFO order."""
|
|
83
|
+
if self._rolled_back or self._released:
|
|
84
|
+
return
|
|
85
|
+
t0 = time.perf_counter()
|
|
86
|
+
try:
|
|
87
|
+
self.disposables.dispose()
|
|
88
|
+
self.conn.execute(f"ROLLBACK TO SAVEPOINT {self.name};")
|
|
89
|
+
self.conn.execute(f"RELEASE SAVEPOINT {self.name};")
|
|
90
|
+
finally:
|
|
91
|
+
self._rolled_back = True
|
|
92
|
+
self.rollback_duration_ms = (time.perf_counter() - t0) * 1000.0
|
|
93
|
+
logger.debug(f"[SavepointGuard] Rollback '{self.name}' completed in {self.rollback_duration_ms:.3f}ms")
|
|
94
|
+
|
|
95
|
+
def commit(self) -> None:
|
|
96
|
+
"""Release the savepoint cleanly."""
|
|
97
|
+
if self._rolled_back or self._released:
|
|
98
|
+
return
|
|
99
|
+
self.conn.execute(f"RELEASE SAVEPOINT {self.name};")
|
|
100
|
+
self._released = True
|
|
101
|
+
|
|
102
|
+
def __enter__(self) -> SavepointGuard:
|
|
103
|
+
self.begin()
|
|
104
|
+
return self
|
|
105
|
+
|
|
106
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> bool:
|
|
107
|
+
if exc_type is not None:
|
|
108
|
+
self.rollback()
|
|
109
|
+
return False
|
|
110
|
+
else:
|
|
111
|
+
self.commit()
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class TriDbSavepointGuard:
|
|
116
|
+
"""Coordinated savepoint guard across multiple SQLite database connections."""
|
|
117
|
+
|
|
118
|
+
def __init__(
|
|
119
|
+
self,
|
|
120
|
+
connections: dict[str, sqlite3.Connection] | list[sqlite3.Connection],
|
|
121
|
+
name: str | None = None,
|
|
122
|
+
disposables: DisposableList | None = None,
|
|
123
|
+
) -> None:
|
|
124
|
+
if isinstance(connections, dict):
|
|
125
|
+
self.conns = list(connections.values())
|
|
126
|
+
else:
|
|
127
|
+
self.conns = list(connections)
|
|
128
|
+
self.name = name or f"tridb_sp_{uuid.uuid4().hex[:8]}"
|
|
129
|
+
self.disposables = disposables or DisposableList()
|
|
130
|
+
self._guards = [SavepointGuard(c, self.name, None) for c in self.conns]
|
|
131
|
+
self.rollback_duration_ms: float = 0.0
|
|
132
|
+
|
|
133
|
+
def begin(self) -> None:
|
|
134
|
+
for g in self._guards:
|
|
135
|
+
g.begin()
|
|
136
|
+
|
|
137
|
+
def rollback(self) -> None:
|
|
138
|
+
t0 = time.perf_counter()
|
|
139
|
+
try:
|
|
140
|
+
self.disposables.dispose()
|
|
141
|
+
for g in self._guards:
|
|
142
|
+
g.rollback()
|
|
143
|
+
finally:
|
|
144
|
+
self.rollback_duration_ms = (time.perf_counter() - t0) * 1000.0
|
|
145
|
+
|
|
146
|
+
def commit(self) -> None:
|
|
147
|
+
for g in self._guards:
|
|
148
|
+
g.commit()
|
|
149
|
+
|
|
150
|
+
def __enter__(self) -> TriDbSavepointGuard:
|
|
151
|
+
self.begin()
|
|
152
|
+
return self
|
|
153
|
+
|
|
154
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> bool:
|
|
155
|
+
if exc_type is not None:
|
|
156
|
+
self.rollback()
|
|
157
|
+
return False
|
|
158
|
+
else:
|
|
159
|
+
self.commit()
|
|
160
|
+
return True
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
__all__ = ["DisposableList", "SavepointGuard", "TriDbSavepointGuard"]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Cordis 5-State Fiber Lifecycle Machine (DEL-389-01, INV-389-01).
|
|
2
|
+
|
|
3
|
+
Implements the formal state machine:
|
|
4
|
+
Pending ➔ Loading ➔ Active ➔ Unloading ➔ Disposed
|
|
5
|
+
|
|
6
|
+
Enforces INV-389-01: Every execution fiber must strictly follow valid transitions;
|
|
7
|
+
direct jumps or resurrections raise FiberLifecycleError.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from typing import Callable
|
|
14
|
+
|
|
15
|
+
from clm.layer0.exceptions import CLMError
|
|
16
|
+
from clm.layer2.fiber.context import Context
|
|
17
|
+
from clm.layer2.fiber.disposable import DisposableList
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FiberState(str, Enum):
|
|
23
|
+
"""The five canonical lifecycle states of a Cordis Fiber."""
|
|
24
|
+
PENDING = "pending"
|
|
25
|
+
LOADING = "loading"
|
|
26
|
+
ACTIVE = "active"
|
|
27
|
+
UNLOADING = "unloading"
|
|
28
|
+
DISPOSED = "disposed"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class FiberLifecycleError(CLMError):
|
|
32
|
+
"""Raised when an illegal transition is attempted across fiber states."""
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Strict transition matrix enforcing INV-389-01
|
|
37
|
+
VALID_TRANSITIONS: dict[FiberState, set[FiberState]] = {
|
|
38
|
+
FiberState.PENDING: {FiberState.LOADING},
|
|
39
|
+
FiberState.LOADING: {FiberState.ACTIVE, FiberState.DISPOSED},
|
|
40
|
+
FiberState.ACTIVE: {FiberState.UNLOADING},
|
|
41
|
+
FiberState.UNLOADING: {FiberState.DISPOSED},
|
|
42
|
+
FiberState.DISPOSED: set(), # Terminal state: cannot transition out
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Fiber:
|
|
47
|
+
"""Cordis Execution Fiber.
|
|
48
|
+
|
|
49
|
+
Represents an isolated unit of execution with a 5-state lifecycle and LIFO disposer stack.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
fiber_id: str,
|
|
55
|
+
context: Context | None = None,
|
|
56
|
+
disposables: DisposableList | None = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
self.id = fiber_id
|
|
59
|
+
self.state: FiberState = FiberState.PENDING
|
|
60
|
+
self.context = context or Context()
|
|
61
|
+
self.disposables = disposables or DisposableList()
|
|
62
|
+
self._history: list[FiberState] = [FiberState.PENDING]
|
|
63
|
+
|
|
64
|
+
def register_disposable(self, callback: Callable[[], None]) -> None:
|
|
65
|
+
"""Register a LIFO cleanup closure on this fiber."""
|
|
66
|
+
self.disposables.push(callback)
|
|
67
|
+
|
|
68
|
+
def transition(self, target: FiberState | str) -> None:
|
|
69
|
+
"""Transition the fiber to a new lifecycle state.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
FiberLifecycleError: If the transition violates INV-389-01.
|
|
73
|
+
"""
|
|
74
|
+
if isinstance(target, str):
|
|
75
|
+
try:
|
|
76
|
+
target_state = FiberState(target.lower())
|
|
77
|
+
except ValueError:
|
|
78
|
+
raise FiberLifecycleError(f"Unknown FiberState '{target}' for fiber '{self.id}'")
|
|
79
|
+
else:
|
|
80
|
+
target_state = target
|
|
81
|
+
|
|
82
|
+
allowed = VALID_TRANSITIONS.get(self.state, set())
|
|
83
|
+
if target_state not in allowed:
|
|
84
|
+
raise FiberLifecycleError(
|
|
85
|
+
f"Illegal fiber lifecycle transition for '{self.id}': "
|
|
86
|
+
f"cannot jump from {self.state.value.upper()} to {target_state.value.upper()}. "
|
|
87
|
+
f"Valid target states: {[s.value.upper() for s in allowed]}"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
logger.debug(f"[Fiber:{self.id}] {self.state.value} -> {target_state.value}")
|
|
91
|
+
self.state = target_state
|
|
92
|
+
self._history.append(target_state)
|
|
93
|
+
|
|
94
|
+
# When entering DISPOSED, automatically unwind disposables
|
|
95
|
+
if target_state == FiberState.DISPOSED:
|
|
96
|
+
self.disposables.dispose()
|
|
97
|
+
|
|
98
|
+
def dispose(self) -> None:
|
|
99
|
+
"""Gracefully transition this fiber to DISPOSED via valid intermediate steps."""
|
|
100
|
+
if self.state == FiberState.DISPOSED:
|
|
101
|
+
return
|
|
102
|
+
if self.state == FiberState.PENDING:
|
|
103
|
+
self.transition(FiberState.LOADING)
|
|
104
|
+
self.transition(FiberState.DISPOSED)
|
|
105
|
+
elif self.state == FiberState.LOADING:
|
|
106
|
+
self.transition(FiberState.DISPOSED)
|
|
107
|
+
elif self.state == FiberState.ACTIVE:
|
|
108
|
+
self.transition(FiberState.UNLOADING)
|
|
109
|
+
self.transition(FiberState.DISPOSED)
|
|
110
|
+
elif self.state == FiberState.UNLOADING:
|
|
111
|
+
self.transition(FiberState.DISPOSED)
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def is_active(self) -> bool:
|
|
115
|
+
return self.state == FiberState.ACTIVE
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def is_disposed(self) -> bool:
|
|
119
|
+
return self.state == FiberState.DISPOSED
|
|
120
|
+
|
|
121
|
+
def __repr__(self) -> str:
|
|
122
|
+
return f"<Fiber id={self.id!r} state={self.state.value!r} disposables={len(self.disposables)}>"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
__all__ = ["FiberState", "FiberLifecycleError", "Fiber"]
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Hoare Fiber State Machine & Deterministic Rollback (DEL-389-01, INV-389-03).
|
|
2
|
+
|
|
3
|
+
Executes formal Hoare Triples {P} C {Q} across Fiber lifecycles with guaranteed
|
|
4
|
+
sub-millisecond (<1ms) transactional rollback via SavepointGuard and DisposableList.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import sqlite3
|
|
10
|
+
import time
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any, Callable
|
|
13
|
+
|
|
14
|
+
from clm.layer2.fiber.context import Context
|
|
15
|
+
from clm.layer2.fiber.disposable import SavepointGuard
|
|
16
|
+
from clm.layer2.fiber.fiber import Fiber, FiberState
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class HoareTriple:
|
|
23
|
+
"""Formal specification of a state transition as a Hoare Triple {P} C {Q}."""
|
|
24
|
+
name: str
|
|
25
|
+
transition: Callable[[Context, Fiber], Any]
|
|
26
|
+
precondition: Callable[[Context], bool | tuple[bool, str]] | None = None
|
|
27
|
+
postcondition: Callable[[Context, Any], bool | tuple[bool, str]] | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class HoareExecutionReport:
|
|
32
|
+
"""Execution telemetry report measuring rollback timing and determinism."""
|
|
33
|
+
fiber_id: str
|
|
34
|
+
success: bool
|
|
35
|
+
rollback_triggered: bool
|
|
36
|
+
rollback_duration_ms: float
|
|
37
|
+
final_state: FiberState
|
|
38
|
+
value: Any = None
|
|
39
|
+
error: str | None = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class HoareFiberExecutor:
|
|
43
|
+
"""Executes transitions under formal Hoare contract verification with deterministic rollback."""
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def execute(
|
|
47
|
+
context: Context,
|
|
48
|
+
fiber: Fiber,
|
|
49
|
+
triple: HoareTriple,
|
|
50
|
+
conn: sqlite3.Connection | None = None,
|
|
51
|
+
) -> HoareExecutionReport:
|
|
52
|
+
"""Execute a HoareTriple {P} C {Q} over a Fiber.
|
|
53
|
+
|
|
54
|
+
Guarantees:
|
|
55
|
+
- {P} evaluated in LOADING state.
|
|
56
|
+
- Computation C evaluated in ACTIVE state.
|
|
57
|
+
- {Q} evaluated on concrete output value before state commitment.
|
|
58
|
+
- On {P} or {Q} failure or exception: triggers LIFO unwinding and savepoint rollback in <1ms.
|
|
59
|
+
"""
|
|
60
|
+
# Step 1: Ensure fiber is in LOADING state
|
|
61
|
+
if fiber.state == FiberState.PENDING:
|
|
62
|
+
fiber.transition(FiberState.LOADING)
|
|
63
|
+
|
|
64
|
+
# Step 2: Evaluate Precondition {P} in LOADING state
|
|
65
|
+
if triple.precondition is not None:
|
|
66
|
+
try:
|
|
67
|
+
pre_res = triple.precondition(context)
|
|
68
|
+
if isinstance(pre_res, tuple):
|
|
69
|
+
passed, msg = pre_res
|
|
70
|
+
if not passed:
|
|
71
|
+
fiber.transition(FiberState.DISPOSED)
|
|
72
|
+
return HoareExecutionReport(
|
|
73
|
+
fiber_id=fiber.id,
|
|
74
|
+
success=False,
|
|
75
|
+
rollback_triggered=False,
|
|
76
|
+
rollback_duration_ms=0.0,
|
|
77
|
+
final_state=fiber.state,
|
|
78
|
+
error=f"Precondition {{P}} failed: {msg}",
|
|
79
|
+
)
|
|
80
|
+
elif not pre_res:
|
|
81
|
+
fiber.transition(FiberState.DISPOSED)
|
|
82
|
+
return HoareExecutionReport(
|
|
83
|
+
fiber_id=fiber.id,
|
|
84
|
+
success=False,
|
|
85
|
+
rollback_triggered=False,
|
|
86
|
+
rollback_duration_ms=0.0,
|
|
87
|
+
final_state=fiber.state,
|
|
88
|
+
error="Precondition {P} check returned False",
|
|
89
|
+
)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
fiber.transition(FiberState.DISPOSED)
|
|
92
|
+
return HoareExecutionReport(
|
|
93
|
+
fiber_id=fiber.id,
|
|
94
|
+
success=False,
|
|
95
|
+
rollback_triggered=False,
|
|
96
|
+
rollback_duration_ms=0.0,
|
|
97
|
+
final_state=fiber.state,
|
|
98
|
+
error=f"Precondition {{P}} exception: {e}",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Step 3: Transition to ACTIVE state
|
|
102
|
+
fiber.transition(FiberState.ACTIVE)
|
|
103
|
+
|
|
104
|
+
# Step 4: Setup SavepointGuard for computation C
|
|
105
|
+
sp_guard: SavepointGuard | None = None
|
|
106
|
+
if conn is not None:
|
|
107
|
+
sp_guard = SavepointGuard(conn, name=f"sp_{triple.name[:12]}", disposables=fiber.disposables)
|
|
108
|
+
sp_guard.begin()
|
|
109
|
+
|
|
110
|
+
# Step 5: Execute computation C
|
|
111
|
+
try:
|
|
112
|
+
val = triple.transition(context, fiber)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.warning(f"[HoareFiberExecutor] Computation error in '{triple.name}': {e}", exc_info=True)
|
|
115
|
+
rollback_duration = 0.0
|
|
116
|
+
if sp_guard is not None:
|
|
117
|
+
sp_guard.rollback()
|
|
118
|
+
rollback_duration = sp_guard.rollback_duration_ms
|
|
119
|
+
else:
|
|
120
|
+
t0 = time.perf_counter()
|
|
121
|
+
fiber.disposables.dispose()
|
|
122
|
+
rollback_duration = (time.perf_counter() - t0) * 1000.0
|
|
123
|
+
|
|
124
|
+
fiber.transition(FiberState.UNLOADING)
|
|
125
|
+
fiber.transition(FiberState.DISPOSED)
|
|
126
|
+
|
|
127
|
+
return HoareExecutionReport(
|
|
128
|
+
fiber_id=fiber.id,
|
|
129
|
+
success=False,
|
|
130
|
+
rollback_triggered=True,
|
|
131
|
+
rollback_duration_ms=rollback_duration,
|
|
132
|
+
final_state=fiber.state,
|
|
133
|
+
error=f"Execution error in {triple.name}: {e}",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Step 6: Evaluate Postcondition {Q} in ACTIVE state on output value
|
|
137
|
+
if triple.postcondition is not None:
|
|
138
|
+
post_passed = True
|
|
139
|
+
post_error = ""
|
|
140
|
+
try:
|
|
141
|
+
post_res = triple.postcondition(context, val)
|
|
142
|
+
if isinstance(post_res, tuple):
|
|
143
|
+
post_passed, post_error = post_res
|
|
144
|
+
elif not post_res:
|
|
145
|
+
post_passed = False
|
|
146
|
+
post_error = "Postcondition {Q} returned False"
|
|
147
|
+
except Exception as e:
|
|
148
|
+
post_passed = False
|
|
149
|
+
post_error = f"Postcondition {{Q}} exception: {e}"
|
|
150
|
+
|
|
151
|
+
if not post_passed:
|
|
152
|
+
logger.warning(f"[HoareFiberExecutor] Postcondition {{Q}} failed in '{triple.name}': {post_error}")
|
|
153
|
+
rollback_duration = 0.0
|
|
154
|
+
if sp_guard is not None:
|
|
155
|
+
sp_guard.rollback()
|
|
156
|
+
rollback_duration = sp_guard.rollback_duration_ms
|
|
157
|
+
else:
|
|
158
|
+
t0 = time.perf_counter()
|
|
159
|
+
fiber.disposables.dispose()
|
|
160
|
+
rollback_duration = (time.perf_counter() - t0) * 1000.0
|
|
161
|
+
|
|
162
|
+
fiber.transition(FiberState.UNLOADING)
|
|
163
|
+
fiber.transition(FiberState.DISPOSED)
|
|
164
|
+
|
|
165
|
+
return HoareExecutionReport(
|
|
166
|
+
fiber_id=fiber.id,
|
|
167
|
+
success=False,
|
|
168
|
+
rollback_triggered=True,
|
|
169
|
+
rollback_duration_ms=rollback_duration,
|
|
170
|
+
final_state=fiber.state,
|
|
171
|
+
error=f"Postcondition {{Q}} failed: {post_error}",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Step 7: Postconditions satisfied, commit transaction
|
|
175
|
+
if sp_guard is not None:
|
|
176
|
+
sp_guard.commit()
|
|
177
|
+
|
|
178
|
+
fiber.transition(FiberState.UNLOADING)
|
|
179
|
+
fiber.transition(FiberState.DISPOSED)
|
|
180
|
+
|
|
181
|
+
return HoareExecutionReport(
|
|
182
|
+
fiber_id=fiber.id,
|
|
183
|
+
success=True,
|
|
184
|
+
rollback_triggered=False,
|
|
185
|
+
rollback_duration_ms=0.0,
|
|
186
|
+
final_state=fiber.state,
|
|
187
|
+
value=val,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
__all__ = ["HoareTriple", "HoareExecutionReport", "HoareFiberExecutor"]
|