arkhai-kit-settlement-runtime 0.1.2__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.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.5
2
+ Name: arkhai-kit-settlement-runtime
3
+ Version: 0.1.2
4
+ Summary: Mechanism-neutral commercial settlement lifecycle, durable servicing, and SQLite repository.
5
+ License-Expression: MIT
6
+ Requires-Python: <3.14,>=3.10
7
+ Requires-Dist: arkhai-core==0.2.0
8
+ Requires-Dist: arkhai-kit-identity==0.3.0
9
+ Requires-Dist: pydantic>=2.7
@@ -0,0 +1,16 @@
1
+ market_settlement_runtime/__init__.py,sha256=Ze__7QPyFReV6d-7zGUQ_jWnLs4r2AJtxl7v-2PlVWI,5028
2
+ market_settlement_runtime/clauses.py,sha256=fWjRB2gKuU_d4mlqPXCDuxJk_EhXaPbRCGKz-STbL74,10977
3
+ market_settlement_runtime/configuration.py,sha256=dLl6IvuNjVS7WiKW150nL5DmJnbduKvzZI1LCS2Emd0,42147
4
+ market_settlement_runtime/hosted_routes.py,sha256=-RuWBn23jsadcwOxFaFJrtyW6kS8zh6nxXet5CQ1OF0,13918
5
+ market_settlement_runtime/jobs.py,sha256=Ckjk7BwSn1-YNGzIfC-_v4BSi8h-0oCMlmzlD7Dq96g,5759
6
+ market_settlement_runtime/models.py,sha256=l04V4PIimNjA207TLWkgtlLUGGDG4I4CI8oVtUaAPKE,8411
7
+ market_settlement_runtime/policy.py,sha256=S8S1EsyWzF4FjnvkN5XGAbIexvAvogm0dZ751IlhL_A,2660
8
+ market_settlement_runtime/ports.py,sha256=vum4jPQ4Snjyp8q43JVRbaNep7TYdnwuDu0IlqBo6ps,4402
9
+ market_settlement_runtime/publication.py,sha256=xfq1RrvBYrYuwdUt7ZVhoDBnWDvTxOmupQx2-RVDPcY,8790
10
+ market_settlement_runtime/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ market_settlement_runtime/runtime.py,sha256=hUFSoMEiGspJZ1DSfkRAAKhLQtZLO_mVP3BZ7uF8HAg,35069
12
+ market_settlement_runtime/servicing.py,sha256=-c3m7i7ECslGH3YoHxFdL2flj4HzNzLd1Inum-5WJXY,12254
13
+ market_settlement_runtime/sqlite_repository.py,sha256=iw7DCjo6QBAm6P6Ien6VoDyr6f2WdPOiknCvI5j0juo,64954
14
+ arkhai_kit_settlement_runtime-0.1.2.dist-info/METADATA,sha256=KEnrvE9Y-fmT7PZF2x02v1EDUMKx7unjzI99BkLRL8Y,334
15
+ arkhai_kit_settlement_runtime-0.1.2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
16
+ arkhai_kit_settlement_runtime-0.1.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,191 @@
1
+ """One durable, mechanism-neutral commercial settlement runtime."""
2
+
3
+ from market_core.query_dsl import (
4
+ ComparisonOperator,
5
+ FieldDescriptor,
6
+ MissingValueRule,
7
+ QueryValueType,
8
+ )
9
+ from .clauses import (
10
+ CompiledSettlementClause,
11
+ SettlementCandidate,
12
+ SettlementClauseResult,
13
+ compile_settlement_clause,
14
+ select_settlement_candidates,
15
+ settlement_clause_descriptors,
16
+ settlement_clause_matches,
17
+ )
18
+ from .configuration import (
19
+ SETTLEMENT_CONFIG_SCHEMA_VERSION,
20
+ AcceptedObligationArtifacts,
21
+ AcceptedObligationBuilder,
22
+ BuyerCompatibilityHook,
23
+ ClientFactory,
24
+ MechanismReadiness,
25
+ MechanismRegistration,
26
+ OptionBuilder,
27
+ PreflightCallback,
28
+ PublicationInputValidator,
29
+ ReadinessBlocker,
30
+ RegisteredSettlementClauseField,
31
+ SettlementClauseField,
32
+ SettlementClauseProjector,
33
+ SettlementConfig,
34
+ SettlementConfigurationError,
35
+ SettlementConfigurationRegistry,
36
+ SettlementRole,
37
+ )
38
+ from .jobs import (
39
+ FulfillmentOutcome,
40
+ PreparedSettlement,
41
+ SettlementJobCoordinator,
42
+ )
43
+ from .hosted_routes import (
44
+ AuthorizedSettlementRequest,
45
+ BeforeHostedReclaim,
46
+ HostedAcceptedAgreement,
47
+ HostedSettlementRouteCallbacks,
48
+ HostedSettlementRouteError,
49
+ HostedSettlementRouteService,
50
+ HostedSettlementStart,
51
+ )
52
+ from .models import (
53
+ ConditionDecision,
54
+ ConditionOutcome,
55
+ ConditionState,
56
+ EffectOutcome,
57
+ EscrowStatus,
58
+ MaterializationOutcome,
59
+ MaterializationState,
60
+ MaterializationStatus,
61
+ OperationKind,
62
+ OperationState,
63
+ Party,
64
+ SettlementObligationRecord,
65
+ SettlementOperationOutcome,
66
+ SettlementOperationRecord,
67
+ SettlementPlanStatus,
68
+ StatusOutcome,
69
+ TerminalEffectState,
70
+ aggregate_settlement_status,
71
+ canonical_json,
72
+ derive_obligation_ref,
73
+ obligation_payload_hash,
74
+ )
75
+ from .policy import (
76
+ FailureActionHandler,
77
+ FailurePolicy,
78
+ FailurePolicyResult,
79
+ )
80
+ from .ports import (
81
+ ConditionalEscrowClient,
82
+ SettlementRuntimeRepository,
83
+ SettlementServicingRepository,
84
+ )
85
+ from .runtime import (
86
+ MANUAL_REASON_KEY,
87
+ SettlementManualRequired,
88
+ SettlementRuntime,
89
+ settlement_operation_ref,
90
+ )
91
+ from .publication import (
92
+ SettlementPublicationClause,
93
+ compile_settlement_publication_clause,
94
+ )
95
+ from .servicing import (
96
+ EventCallback,
97
+ SettlementServicingWorker,
98
+ TerminalCallback,
99
+ )
100
+ from .sqlite_repository import (
101
+ SETTLEMENT_MIGRATION_ID,
102
+ SETTLEMENT_MECHANISM_PARAMS_MIGRATION_ID,
103
+ SETTLEMENT_PRINCIPAL_MIGRATION_ID,
104
+ SettlementMigration,
105
+ SettlementSQLiteRepository,
106
+ settlement_migrations,
107
+ )
108
+
109
+ __all__ = [
110
+ "SETTLEMENT_CONFIG_SCHEMA_VERSION",
111
+ "AcceptedObligationArtifacts",
112
+ "AcceptedObligationBuilder",
113
+ "SETTLEMENT_MIGRATION_ID",
114
+ "SETTLEMENT_MECHANISM_PARAMS_MIGRATION_ID",
115
+ "SETTLEMENT_PRINCIPAL_MIGRATION_ID",
116
+ "CompiledSettlementClause",
117
+ "ComparisonOperator",
118
+ "BuyerCompatibilityHook",
119
+ "ClientFactory",
120
+ "SettlementCandidate",
121
+ "SettlementClauseField",
122
+ "SettlementClauseProjector",
123
+ "SettlementClauseResult",
124
+ "ConditionDecision",
125
+ "ConditionOutcome",
126
+ "ConditionState",
127
+ "ConditionalEscrowClient",
128
+ "AuthorizedSettlementRequest",
129
+ "BeforeHostedReclaim",
130
+ "HostedAcceptedAgreement",
131
+ "HostedSettlementRouteCallbacks",
132
+ "HostedSettlementRouteError",
133
+ "HostedSettlementRouteService",
134
+ "HostedSettlementStart",
135
+ "EffectOutcome",
136
+ "EscrowStatus",
137
+ "FieldDescriptor",
138
+ "EventCallback",
139
+ "FailureActionHandler",
140
+ "FailurePolicy",
141
+ "FailurePolicyResult",
142
+ "FulfillmentOutcome",
143
+ "MaterializationOutcome",
144
+ "MaterializationState",
145
+ "MaterializationStatus",
146
+ "MechanismReadiness",
147
+ "MechanismRegistration",
148
+ "OperationKind",
149
+ "OperationState",
150
+ "MissingValueRule",
151
+ "OptionBuilder",
152
+ "Party",
153
+ "PreflightCallback",
154
+ "PublicationInputValidator",
155
+ "PreparedSettlement",
156
+ "ReadinessBlocker",
157
+ "RegisteredSettlementClauseField",
158
+ "SettlementConfig",
159
+ "QueryValueType",
160
+ "SettlementConfigurationError",
161
+ "SettlementConfigurationRegistry",
162
+ "SettlementJobCoordinator",
163
+ "MANUAL_REASON_KEY",
164
+ "SettlementManualRequired",
165
+ "SettlementMigration",
166
+ "SettlementObligationRecord",
167
+ "SettlementOperationOutcome",
168
+ "SettlementOperationRecord",
169
+ "SettlementPlanStatus",
170
+ "SettlementPublicationClause",
171
+ "SettlementRole",
172
+ "SettlementRuntime",
173
+ "SettlementRuntimeRepository",
174
+ "SettlementSQLiteRepository",
175
+ "SettlementServicingRepository",
176
+ "SettlementServicingWorker",
177
+ "StatusOutcome",
178
+ "TerminalCallback",
179
+ "TerminalEffectState",
180
+ "aggregate_settlement_status",
181
+ "canonical_json",
182
+ "derive_obligation_ref",
183
+ "obligation_payload_hash",
184
+ "settlement_migrations",
185
+ "settlement_operation_ref",
186
+ "compile_settlement_publication_clause",
187
+ "compile_settlement_clause",
188
+ "select_settlement_candidates",
189
+ "settlement_clause_descriptors",
190
+ "settlement_clause_matches",
191
+ ]
@@ -0,0 +1,318 @@
1
+ """Typed settlement-clause compilation and same-option evaluation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable, Mapping, Sequence
6
+ from dataclasses import dataclass
7
+ from decimal import Decimal
8
+ from typing import Any
9
+
10
+ from market_core.query_dsl import (
11
+ ComparisonOperator,
12
+ FieldDescriptor,
13
+ MissingValueRule,
14
+ QueryValidationError,
15
+ QueryValueType,
16
+ ScalarValue,
17
+ ValidatedComparison,
18
+ ValidatedQuery,
19
+ compile_query,
20
+ )
21
+ from market_core.schemas import SettlementOption
22
+
23
+ from .configuration import (
24
+ MechanismRegistration,
25
+ RegisteredSettlementClauseField,
26
+ SettlementConfig,
27
+ SettlementConfigurationError,
28
+ SettlementConfigurationRegistry,
29
+ SettlementRole,
30
+ )
31
+
32
+ _COMMON_OPERATORS = frozenset(
33
+ {
34
+ ComparisonOperator.EQUAL,
35
+ ComparisonOperator.NOT_EQUAL,
36
+ ComparisonOperator.IN,
37
+ ComparisonOperator.NOT_IN,
38
+ }
39
+ )
40
+ _COMMON_FIELDS = (
41
+ FieldDescriptor(
42
+ name="mechanism",
43
+ value_type=QueryValueType.STRING,
44
+ operators=_COMMON_OPERATORS,
45
+ description="canonical settlement mechanism ID or configuration key",
46
+ ),
47
+ FieldDescriptor(
48
+ name="asset",
49
+ value_type=QueryValueType.STRING,
50
+ operators=_COMMON_OPERATORS,
51
+ description="public settlement asset",
52
+ ),
53
+ FieldDescriptor(
54
+ name="option_id",
55
+ value_type=QueryValueType.STRING,
56
+ operators=_COMMON_OPERATORS,
57
+ description="immutable settlement option ID",
58
+ ),
59
+ )
60
+ _MISSING = object()
61
+
62
+
63
+ @dataclass(frozen=True, slots=True)
64
+ class CompiledSettlementClause:
65
+ """One validated conjunction over one advertised settlement option."""
66
+
67
+ query: ValidatedQuery
68
+
69
+ def render(self) -> str:
70
+ return self.query.render()
71
+
72
+
73
+ @dataclass(frozen=True, slots=True)
74
+ class SettlementCandidate:
75
+ """One installed, enabled, role-compatible advertised option."""
76
+
77
+ registration: MechanismRegistration
78
+ option: SettlementOption
79
+
80
+
81
+ @dataclass(frozen=True, slots=True)
82
+ class SettlementClauseResult:
83
+ """Priority-ordered survivors from the first accepted explicit clause."""
84
+
85
+ candidates: tuple[SettlementCandidate, ...]
86
+ matched_clause_index: int | None
87
+
88
+
89
+ def settlement_clause_descriptors(
90
+ registry: SettlementConfigurationRegistry,
91
+ *,
92
+ role: SettlementRole,
93
+ ) -> tuple[FieldDescriptor, ...]:
94
+ """Return common and role-applicable mechanism fields deterministically."""
95
+
96
+ contributed = tuple(
97
+ item.field.descriptor for item in registry.clause_fields(role=role)
98
+ )
99
+ return (*_COMMON_FIELDS, *contributed)
100
+
101
+
102
+ def compile_settlement_clause(
103
+ source: str,
104
+ registry: SettlementConfigurationRegistry,
105
+ *,
106
+ role: SettlementRole = "buyer",
107
+ ) -> CompiledSettlementClause:
108
+ """Compile one clause and normalize public mechanism aliases."""
109
+
110
+ query = compile_query(source, settlement_clause_descriptors(registry, role=role))
111
+ comparisons: list[ValidatedComparison] = []
112
+ for comparison in query.comparisons:
113
+ if comparison.field != "mechanism":
114
+ comparisons.append(comparison)
115
+ continue
116
+ value = comparison.value
117
+ values = value if isinstance(value, tuple) else (value,)
118
+ normalized: list[ScalarValue] = []
119
+ for item in values:
120
+ assert isinstance(item, str)
121
+ try:
122
+ normalized.append(registry.canonical_mechanism_id(item, role=role))
123
+ except SettlementConfigurationError as exc:
124
+ raise QueryValidationError(
125
+ "unknown_mechanism",
126
+ f"unknown or role-inapplicable settlement mechanism at position {comparison.span.start}",
127
+ position=comparison.span.start,
128
+ field="mechanism",
129
+ accepted_fields=("mechanism",),
130
+ ) from exc
131
+ comparisons.append(
132
+ ValidatedComparison(
133
+ field=comparison.field,
134
+ operator=comparison.operator,
135
+ value=tuple(normalized) if isinstance(value, tuple) else normalized[0],
136
+ span=comparison.span,
137
+ )
138
+ )
139
+ return CompiledSettlementClause(ValidatedQuery(tuple(comparisons)))
140
+
141
+
142
+ def settlement_clause_matches(
143
+ clause: CompiledSettlementClause,
144
+ option: SettlementOption,
145
+ registry: SettlementConfigurationRegistry,
146
+ *,
147
+ role: SettlementRole = "buyer",
148
+ ) -> bool:
149
+ """Evaluate every predicate against the same immutable option."""
150
+
151
+ contributed = _field_bindings(registry, role=role)
152
+ descriptors = {
153
+ descriptor.name: descriptor
154
+ for descriptor in settlement_clause_descriptors(registry, role=role)
155
+ }
156
+ for comparison in clause.query.comparisons:
157
+ descriptor = descriptors[comparison.field]
158
+ actual: Any
159
+ if comparison.field == "mechanism":
160
+ actual = option.mechanism
161
+ elif comparison.field == "asset":
162
+ actual = option.asset
163
+ elif comparison.field == "option_id":
164
+ actual = option.option_id
165
+ else:
166
+ binding = contributed[comparison.field]
167
+ if option.mechanism != binding.mechanism_id:
168
+ return False
169
+ actual = binding.field.projector(option)
170
+ if actual is None or (
171
+ isinstance(actual, Sequence)
172
+ and not isinstance(actual, (str, bytes))
173
+ and not actual
174
+ ):
175
+ actual = _MISSING
176
+ if actual is _MISSING:
177
+ if descriptor.on_missing is MissingValueRule.PASS:
178
+ continue
179
+ return False
180
+ projected = _validated_projection(actual, descriptor)
181
+ if not _comparison_matches(comparison, projected):
182
+ return False
183
+ return True
184
+
185
+
186
+ def select_settlement_candidates(
187
+ advertised: Iterable[SettlementOption | Mapping[str, Any]],
188
+ *,
189
+ registry: SettlementConfigurationRegistry,
190
+ config: SettlementConfig,
191
+ clauses: Iterable[str | CompiledSettlementClause] = (),
192
+ public_context: Mapping[str, Any] | None = None,
193
+ ) -> SettlementClauseResult:
194
+ """Admit compatible options, then apply ordered clause alternatives."""
195
+
196
+ registry.validate(config, role="buyer")
197
+ decoded = tuple(SettlementOption.model_validate(option) for option in advertised)
198
+ by_mechanism: dict[str, list[SettlementOption]] = {}
199
+ for option in decoded:
200
+ by_mechanism.setdefault(option.mechanism, []).append(option)
201
+
202
+ candidates: list[SettlementCandidate] = []
203
+ for registration in registry.ordered_registrations(config, role="buyer"):
204
+ section = config.mechanism_config(registration.config_key)
205
+ if section is None or not bool(getattr(section, "enabled", False)):
206
+ continue
207
+ for option in sorted(
208
+ by_mechanism.get(registration.mechanism_id, ()),
209
+ key=lambda candidate: candidate.option_id,
210
+ ):
211
+ if registry.buyer_compatible(
212
+ registration.mechanism_id,
213
+ option,
214
+ config,
215
+ public_context=public_context,
216
+ ):
217
+ candidates.append(SettlementCandidate(registration, option))
218
+
219
+ compiled = tuple(
220
+ clause
221
+ if isinstance(clause, CompiledSettlementClause)
222
+ else compile_settlement_clause(clause, registry, role="buyer")
223
+ for clause in clauses
224
+ )
225
+ if not compiled:
226
+ return SettlementClauseResult(tuple(candidates), None)
227
+ for index, clause in enumerate(compiled):
228
+ survivors = tuple(
229
+ candidate
230
+ for candidate in candidates
231
+ if settlement_clause_matches(
232
+ clause,
233
+ candidate.option,
234
+ registry,
235
+ role="buyer",
236
+ )
237
+ )
238
+ if survivors:
239
+ return SettlementClauseResult(survivors, index)
240
+ return SettlementClauseResult((), None)
241
+
242
+
243
+ def _field_bindings(
244
+ registry: SettlementConfigurationRegistry,
245
+ *,
246
+ role: SettlementRole,
247
+ ) -> dict[str, RegisteredSettlementClauseField]:
248
+ bindings: dict[str, RegisteredSettlementClauseField] = {}
249
+ for item in registry.clause_fields(role=role):
250
+ bindings[item.field.descriptor.name] = item
251
+ for alias in item.field.descriptor.aliases:
252
+ bindings[alias] = item
253
+ return bindings
254
+
255
+
256
+ def _validated_projection(
257
+ actual: Any,
258
+ descriptor: FieldDescriptor,
259
+ ) -> tuple[ScalarValue, ...]:
260
+ values = (
261
+ tuple(actual)
262
+ if isinstance(actual, Sequence) and not isinstance(actual, (str, bytes))
263
+ else (actual,)
264
+ )
265
+ if not values:
266
+ raise SettlementConfigurationError(
267
+ f"projector for {descriptor.name!r} returned an empty value"
268
+ )
269
+ normalized: list[ScalarValue] = []
270
+ for value in values:
271
+ if descriptor.value_type is QueryValueType.STRING:
272
+ valid = isinstance(value, str)
273
+ converted: ScalarValue = value
274
+ elif descriptor.value_type is QueryValueType.INTEGER:
275
+ valid = isinstance(value, int) and not isinstance(value, bool)
276
+ converted = value
277
+ elif descriptor.value_type is QueryValueType.DECIMAL:
278
+ valid = isinstance(value, (int, Decimal)) and not isinstance(value, bool)
279
+ converted = Decimal(value) if valid else value
280
+ else:
281
+ valid = isinstance(value, bool)
282
+ converted = value
283
+ if not valid:
284
+ raise SettlementConfigurationError(
285
+ f"projector for {descriptor.name!r} returned the wrong value type"
286
+ )
287
+ normalized.append(converted)
288
+ return tuple(normalized)
289
+
290
+
291
+ def _comparison_matches(
292
+ comparison: ValidatedComparison,
293
+ actual: tuple[ScalarValue, ...],
294
+ ) -> bool:
295
+ expected = comparison.value
296
+ operator = comparison.operator
297
+ if operator is ComparisonOperator.EQUAL:
298
+ assert not isinstance(expected, tuple)
299
+ return any(value == expected for value in actual)
300
+ if operator is ComparisonOperator.NOT_EQUAL:
301
+ assert not isinstance(expected, tuple)
302
+ return all(value != expected for value in actual)
303
+ if operator is ComparisonOperator.IN:
304
+ assert isinstance(expected, tuple)
305
+ return any(value in expected for value in actual)
306
+ if operator is ComparisonOperator.NOT_IN:
307
+ assert isinstance(expected, tuple)
308
+ return all(value not in expected for value in actual)
309
+ assert not isinstance(expected, tuple)
310
+ if operator is ComparisonOperator.LESS_THAN:
311
+ return any(value < expected for value in actual)
312
+ if operator is ComparisonOperator.LESS_THAN_OR_EQUAL:
313
+ return any(value <= expected for value in actual)
314
+ if operator is ComparisonOperator.GREATER_THAN:
315
+ return any(value > expected for value in actual)
316
+ if operator is ComparisonOperator.GREATER_THAN_OR_EQUAL:
317
+ return any(value >= expected for value in actual)
318
+ raise AssertionError(f"unsupported comparison operator {operator}")