rememberstack 0.1.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.
Files changed (186) hide show
  1. rememberstack/__init__.py +9 -0
  2. rememberstack/adapters/__init__.py +42 -0
  3. rememberstack/adapters/codex_writer.py +221 -0
  4. rememberstack/adapters/markitdown_converter.py +42 -0
  5. rememberstack/adapters/openrouter.py +136 -0
  6. rememberstack/adapters/selfhost/__init__.py +54 -0
  7. rememberstack/adapters/selfhost/forget.py +66 -0
  8. rememberstack/adapters/selfhost/git.py +374 -0
  9. rememberstack/adapters/selfhost/lance.py +328 -0
  10. rememberstack/adapters/selfhost/minio.py +279 -0
  11. rememberstack/adapters/selfhost/mounts.py +249 -0
  12. rememberstack/adapters/selfhost/object_store.py +130 -0
  13. rememberstack/adapters/selfhost/projection.py +80 -0
  14. rememberstack/adapters/selfhost/queue.py +137 -0
  15. rememberstack/adapters/selfhost/telemetry.py +45 -0
  16. rememberstack/adapters/selfhost/watcher.py +70 -0
  17. rememberstack/adapters/testing/__init__.py +15 -0
  18. rememberstack/adapters/testing/cost_meter.py +13 -0
  19. rememberstack/adapters/testing/model_provider.py +83 -0
  20. rememberstack/adapters/testing/queue.py +43 -0
  21. rememberstack/adapters/testing/telemetry.py +22 -0
  22. rememberstack/client.py +19 -0
  23. rememberstack/core/__init__.py +127 -0
  24. rememberstack/core/blockizer.py +189 -0
  25. rememberstack/core/chunker.py +216 -0
  26. rememberstack/core/consumption_skill.py +275 -0
  27. rememberstack/core/conversion.py +76 -0
  28. rememberstack/core/core_manifest.py +598 -0
  29. rememberstack/core/extension_packs.py +124 -0
  30. rememberstack/core/forget.py +17 -0
  31. rememberstack/core/knowledge_authored.py +276 -0
  32. rememberstack/core/knowledge_compile.py +215 -0
  33. rememberstack/core/knowledge_fact_sheet.py +210 -0
  34. rememberstack/core/knowledge_hashing.py +68 -0
  35. rememberstack/core/knowledge_planner.py +64 -0
  36. rememberstack/core/knowledge_writer.py +175 -0
  37. rememberstack/core/ranking.py +200 -0
  38. rememberstack/core/recipe_linter.py +149 -0
  39. rememberstack/core/section_snap.py +209 -0
  40. rememberstack/core/storage_routing.py +27 -0
  41. rememberstack/eval/__init__.py +53 -0
  42. rememberstack/eval/consumption.py +141 -0
  43. rememberstack/eval/contradiction.py +184 -0
  44. rememberstack/eval/harness.py +136 -0
  45. rememberstack/eval/lifecycle.py +400 -0
  46. rememberstack/eval/operational_scale.py +49 -0
  47. rememberstack/eval/resolution.py +255 -0
  48. rememberstack/eval/retrieval_spikes.py +50 -0
  49. rememberstack/eval/skeleton.py +231 -0
  50. rememberstack/llm/__init__.py +1 -0
  51. rememberstack/model/__init__.py +589 -0
  52. rememberstack/model/adjudication.py +100 -0
  53. rememberstack/model/auth.py +27 -0
  54. rememberstack/model/blocks.py +30 -0
  55. rememberstack/model/chunks.py +190 -0
  56. rememberstack/model/claims.py +162 -0
  57. rememberstack/model/client.py +98 -0
  58. rememberstack/model/clustering.py +54 -0
  59. rememberstack/model/component_version.py +124 -0
  60. rememberstack/model/consumption.py +88 -0
  61. rememberstack/model/conversion.py +31 -0
  62. rememberstack/model/deployment.py +53 -0
  63. rememberstack/model/documents.py +168 -0
  64. rememberstack/model/envelope.py +513 -0
  65. rememberstack/model/evaluation.py +72 -0
  66. rememberstack/model/forget.py +143 -0
  67. rememberstack/model/git.py +13 -0
  68. rememberstack/model/knowledge.py +840 -0
  69. rememberstack/model/knowledge_authored.py +325 -0
  70. rememberstack/model/knowledge_planner.py +431 -0
  71. rememberstack/model/lifecycle.py +42 -0
  72. rememberstack/model/model_provider.py +78 -0
  73. rememberstack/model/mounts.py +24 -0
  74. rememberstack/model/object_store.py +21 -0
  75. rememberstack/model/operational_scale.py +59 -0
  76. rememberstack/model/operations.py +153 -0
  77. rememberstack/model/processing.py +228 -0
  78. rememberstack/model/queue.py +73 -0
  79. rememberstack/model/recipes.py +83 -0
  80. rememberstack/model/relations.py +79 -0
  81. rememberstack/model/resolution.py +83 -0
  82. rememberstack/model/retrieval_spikes.py +62 -0
  83. rememberstack/model/sections.py +120 -0
  84. rememberstack/model/telemetry.py +30 -0
  85. rememberstack/ports/__init__.py +29 -0
  86. rememberstack/ports/auth.py +16 -0
  87. rememberstack/ports/connector.py +23 -0
  88. rememberstack/ports/cost_meter.py +17 -0
  89. rememberstack/ports/forget.py +20 -0
  90. rememberstack/ports/git.py +20 -0
  91. rememberstack/ports/model_provider.py +28 -0
  92. rememberstack/ports/mounts.py +16 -0
  93. rememberstack/ports/object_store.py +27 -0
  94. rememberstack/ports/p1_index.py +92 -0
  95. rememberstack/ports/purge.py +93 -0
  96. rememberstack/ports/queue.py +23 -0
  97. rememberstack/ports/telemetry.py +21 -0
  98. rememberstack/profiles/__init__.py +22 -0
  99. rememberstack/profiles/selfhost.py +324 -0
  100. rememberstack/profiles/selfhost_forget.py +158 -0
  101. rememberstack/profiles/selfhost_operations.py +95 -0
  102. rememberstack/py.typed +1 -0
  103. rememberstack/spine/__init__.py +93 -0
  104. rememberstack/spine/admission.py +26 -0
  105. rememberstack/spine/backfill.py +168 -0
  106. rememberstack/spine/catalog_contract.py +742 -0
  107. rememberstack/spine/chunk_catalog.py +237 -0
  108. rememberstack/spine/claim_catalog.py +298 -0
  109. rememberstack/spine/clustering.py +740 -0
  110. rememberstack/spine/component_versions.py +208 -0
  111. rememberstack/spine/consumption.py +81 -0
  112. rememberstack/spine/deployment_bootstrap.py +445 -0
  113. rememberstack/spine/document_catalog.py +621 -0
  114. rememberstack/spine/entity_registry.py +205 -0
  115. rememberstack/spine/extension_packs.py +220 -0
  116. rememberstack/spine/fact_catalog.py +571 -0
  117. rememberstack/spine/forget.py +1753 -0
  118. rememberstack/spine/knowledge.py +5467 -0
  119. rememberstack/spine/lifecycle.py +1071 -0
  120. rememberstack/spine/migrations/__init__.py +1 -0
  121. rememberstack/spine/migrations/_helpers.py +153 -0
  122. rememberstack/spine/migrations/env.py +58 -0
  123. rememberstack/spine/migrations/script.py.mako +27 -0
  124. rememberstack/spine/migrations/versions/__init__.py +1 -0
  125. rememberstack/spine/migrations/versions/p0_02_0001_extensions_enums.py +189 -0
  126. rememberstack/spine/migrations/versions/p0_02_0002_infrastructure_registries.py +321 -0
  127. rememberstack/spine/migrations/versions/p0_02_0003_entities_evaluation_e0_e1.py +631 -0
  128. rememberstack/spine/migrations/versions/p0_02_0004_claims_facts_evidence.py +411 -0
  129. rememberstack/spine/migrations/versions/p0_02_0005_projection_knowledge_retrieval.py +391 -0
  130. rememberstack/spine/migrations/versions/p0_02_0006_partitions_views.py +158 -0
  131. rememberstack/spine/migrations/versions/p2_06_0007_invalidated_outcome.py +26 -0
  132. rememberstack/spine/migrations/versions/p3_01_0008_document_version_target.py +58 -0
  133. rememberstack/spine/migrations/versions/p3_05_0009_reconcile_stage.py +27 -0
  134. rememberstack/spine/migrations/versions/p3_07_0010_lifecycle_eval_suite.py +25 -0
  135. rememberstack/spine/migrations/versions/p4_01_0011_survivor_view_rewrite.py +57 -0
  136. rememberstack/spine/migrations/versions/p6_02_0012_knowledge_compile_recovery.py +58 -0
  137. rememberstack/spine/migrations/versions/p6_04_0013_knowledge_writer_ledger.py +46 -0
  138. rememberstack/spine/migrations/versions/p6_05_0014_knowledge_planner_runtime.py +217 -0
  139. rememberstack/spine/migrations/versions/p6_06_0015_authored_dispatch_runtime.py +38 -0
  140. rememberstack/spine/migrations/versions/p7_02_0016_operational_eval_suite.py +19 -0
  141. rememberstack/spine/migrations/versions/p7_05_0017_hard_forget.py +55 -0
  142. rememberstack/spine/observation_adjudication.py +778 -0
  143. rememberstack/spine/operations.py +298 -0
  144. rememberstack/spine/projection.py +662 -0
  145. rememberstack/spine/recipes.py +276 -0
  146. rememberstack/spine/resolver.py +763 -0
  147. rememberstack/spine/review.py +650 -0
  148. rememberstack/spine/settings.py +22 -0
  149. rememberstack/spine/supersession.py +510 -0
  150. rememberstack/spine/sync.py +128 -0
  151. rememberstack/spine/work_ledger.py +816 -0
  152. rememberstack/surfaces/__init__.py +110 -0
  153. rememberstack/surfaces/cli.py +447 -0
  154. rememberstack/surfaces/consumption_skill.py +87 -0
  155. rememberstack/surfaces/graph_queries.py +698 -0
  156. rememberstack/surfaces/http_api.py +377 -0
  157. rememberstack/surfaces/mcp.py +67 -0
  158. rememberstack/surfaces/query_engine.py +1591 -0
  159. rememberstack/surfaces/recipe_executor.py +185 -0
  160. rememberstack/surfaces/recipe_surface.py +219 -0
  161. rememberstack/surfaces/remote_mcp.py +133 -0
  162. rememberstack/surfaces/sdk.py +324 -0
  163. rememberstack/workers/__init__.py +155 -0
  164. rememberstack/workers/base.py +312 -0
  165. rememberstack/workers/e0.py +577 -0
  166. rememberstack/workers/e1.py +425 -0
  167. rememberstack/workers/e2.py +525 -0
  168. rememberstack/workers/e3.py +434 -0
  169. rememberstack/workers/forget.py +299 -0
  170. rememberstack/workers/knowledge_authored.py +146 -0
  171. rememberstack/workers/knowledge_driver.py +735 -0
  172. rememberstack/workers/knowledge_fact_sheet.py +123 -0
  173. rememberstack/workers/knowledge_planner.py +325 -0
  174. rememberstack/workers/knowledge_writer.py +393 -0
  175. rememberstack/workers/operations.py +42 -0
  176. rememberstack/workers/p1.py +234 -0
  177. rememberstack/workers/p2.py +513 -0
  178. rememberstack/workers/p2_analytics.py +276 -0
  179. rememberstack/workers/p3.py +673 -0
  180. rememberstack/workers/reconcile.py +485 -0
  181. rememberstack/workers/sync.py +168 -0
  182. rememberstack-0.1.0.dist-info/METADATA +213 -0
  183. rememberstack-0.1.0.dist-info/RECORD +186 -0
  184. rememberstack-0.1.0.dist-info/WHEEL +4 -0
  185. rememberstack-0.1.0.dist-info/entry_points.txt +2 -0
  186. rememberstack-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,153 @@
1
+ """Typed, bounded operational inspection and dead-letter replay values."""
2
+
3
+ from typing import Literal
4
+ from uuid import UUID
5
+
6
+ from pydantic import BaseModel
7
+ from pydantic import ConfigDict
8
+ from pydantic import Field
9
+
10
+ from rememberstack.model.processing import ProcessingStatus
11
+ from rememberstack.model.processing import ProcessingTarget
12
+ from rememberstack.model.queue import PipelineStage
13
+ from rememberstack.model.queue import ProcessingLane
14
+ from rememberstack.model.queue import QueueRoute
15
+ from rememberstack.model.queue import UTCDateTime
16
+
17
+
18
+ class PipelineRouteStatus(BaseModel):
19
+ """One deployment route/status aggregate; the enum vocabulary bounds its size."""
20
+
21
+ model_config = ConfigDict(frozen=True, extra="forbid")
22
+
23
+ stage: PipelineStage
24
+ lane: ProcessingLane | None
25
+ status: ProcessingStatus
26
+ count: int = Field(ge=0)
27
+
28
+
29
+ class DeadLetterGroup(BaseModel):
30
+ """One dead-letter aggregate used to spot a failing version or exception."""
31
+
32
+ model_config = ConfigDict(frozen=True, extra="forbid")
33
+
34
+ stage: PipelineStage
35
+ error_class: str
36
+ component_version: str
37
+ count: int = Field(ge=1)
38
+ oldest_enqueued_at: UTCDateTime
39
+ latest_finished_at: UTCDateTime | None
40
+
41
+
42
+ class DeadLetterRecord(BaseModel):
43
+ """One bounded DLQ sample retaining the complete diagnostic and replay input."""
44
+
45
+ model_config = ConfigDict(frozen=True, extra="forbid")
46
+
47
+ processing_id: UUID
48
+ target_kind: ProcessingTarget
49
+ target_id: UUID
50
+ stage: PipelineStage
51
+ component_version: str
52
+ content_hash: str
53
+ lane: ProcessingLane | None
54
+ attempts: int = Field(ge=0)
55
+ max_attempts: int = Field(ge=1)
56
+ error_class: str
57
+ last_error: str | None
58
+ payload: dict[str, object] | None
59
+ enqueued_at: UTCDateTime
60
+ finished_at: UTCDateTime | None
61
+
62
+
63
+ class DeadLetterReport(BaseModel):
64
+ """Deployment DLQ totals plus independently bounded aggregate/detail samples."""
65
+
66
+ model_config = ConfigDict(frozen=True, extra="forbid")
67
+
68
+ total: int = Field(ge=0)
69
+ group_total: int = Field(ge=0)
70
+ groups: tuple[DeadLetterGroup, ...]
71
+ items: tuple[DeadLetterRecord, ...]
72
+
73
+
74
+ class PoisonTargetRecord(BaseModel):
75
+ """A target dead-lettered by at least two component versions for one stage."""
76
+
77
+ model_config = ConfigDict(frozen=True, extra="forbid")
78
+
79
+ target_kind: ProcessingTarget
80
+ target_id: UUID
81
+ stage: PipelineStage
82
+ component_version_total: int = Field(ge=2)
83
+ component_versions: tuple[str, ...]
84
+ dead_letters: int = Field(ge=2)
85
+
86
+
87
+ class PoisonTargetReport(BaseModel):
88
+ """Total poison targets and a bounded sample."""
89
+
90
+ model_config = ConfigDict(frozen=True, extra="forbid")
91
+
92
+ total: int = Field(ge=0)
93
+ items: tuple[PoisonTargetRecord, ...]
94
+
95
+
96
+ class ProjectionSnapshotState(BaseModel):
97
+ """The current P2 or P3 snapshot pointer, if that plane has one."""
98
+
99
+ model_config = ConfigDict(frozen=True, extra="forbid")
100
+
101
+ snapshot_id: UUID
102
+ plane: Literal["P2_graph", "P3_corpusfs"]
103
+ version: str
104
+ store_uri: str
105
+ row_counts: dict[str, object] | None
106
+ built_at: UTCDateTime
107
+ published_at: UTCDateTime | None
108
+
109
+
110
+ class CurrencyMismatch(BaseModel):
111
+ """A claim whose cached currency differs from the append-only ledger truth."""
112
+
113
+ model_config = ConfigDict(frozen=True, extra="forbid")
114
+
115
+ claim_id: UUID
116
+ cached_current: bool
117
+ ledger_current: bool
118
+
119
+
120
+ class CurrencyLedgerAudit(BaseModel):
121
+ """Deployment claim count and bounded currency-ledger mismatch evidence."""
122
+
123
+ model_config = ConfigDict(frozen=True, extra="forbid")
124
+
125
+ claims: int = Field(ge=0)
126
+ mismatch_total: int = Field(ge=0)
127
+ mismatches: tuple[CurrencyMismatch, ...]
128
+
129
+
130
+ class OperationalReport(BaseModel):
131
+ """One coherent, deployment-scoped operational inspection snapshot."""
132
+
133
+ model_config = ConfigDict(frozen=True, extra="forbid")
134
+
135
+ deployment_id: UUID
136
+ generated_at: UTCDateTime
137
+ routes: tuple[PipelineRouteStatus, ...]
138
+ dead_letters: DeadLetterReport
139
+ poison_targets: PoisonTargetReport
140
+ latest_projections: tuple[ProjectionSnapshotState, ...]
141
+ currency: CurrencyLedgerAudit
142
+
143
+
144
+ class DeadLetterReplayResult(BaseModel):
145
+ """Authoritative route and due time after reopening one dead-letter row."""
146
+
147
+ model_config = ConfigDict(frozen=True, extra="forbid")
148
+
149
+ processing_id: UUID
150
+ route: QueueRoute
151
+ not_before: UTCDateTime
152
+ attempts: int = Field(ge=0)
153
+ max_attempts: int = Field(ge=1)
@@ -0,0 +1,228 @@
1
+ """Typed records for the D67 work ledger: enqueue, claim, attempt, and cost rows."""
2
+
3
+ from decimal import Decimal
4
+ from enum import StrEnum
5
+ from uuid import UUID
6
+
7
+ from pydantic import BaseModel
8
+ from pydantic import ConfigDict
9
+ from pydantic import Field
10
+
11
+ from rememberstack.model.queue import PipelineStage
12
+ from rememberstack.model.queue import ProcessingLane
13
+ from rememberstack.model.queue import UTCDateTime
14
+
15
+
16
+ class ProcessingTarget(StrEnum):
17
+ """Exact values of the binding Postgres ``processing_target`` enum."""
18
+
19
+ DOCUMENT = "document"
20
+ DOCUMENT_VERSION = "document_version"
21
+ DOCUMENT_SECTION = "document_section"
22
+ CHUNK = "chunk"
23
+ CLAIM = "claim"
24
+ RELATION = "relation"
25
+ OBSERVATION = "observation"
26
+ ENTITY = "entity"
27
+ SNAPSHOT = "snapshot"
28
+ KNOWLEDGE_ARTIFACT = "knowledge_artifact"
29
+ KNOWLEDGE_DISPATCH = "knowledge_dispatch"
30
+
31
+
32
+ class ProcessingStatus(StrEnum):
33
+ """Exact values of the binding Postgres ``processing_status`` enum."""
34
+
35
+ PENDING = "pending"
36
+ RUNNING = "running"
37
+ SUCCEEDED = "succeeded"
38
+ FAILED = "failed"
39
+ DEAD_LETTER = "dead_letter"
40
+ SKIPPED = "skipped"
41
+
42
+
43
+ class DeferReason(StrEnum):
44
+ """Exact values of the binding Postgres ``processing_defer_reason`` enum (D67)."""
45
+
46
+ SCHEDULED = "scheduled"
47
+ RETRY_BACKOFF = "retry_backoff"
48
+ BUDGET = "budget"
49
+
50
+
51
+ class EnqueueWork(BaseModel):
52
+ """One unit of work to insert into ``processing_state`` (D12 idempotency key)."""
53
+
54
+ model_config = ConfigDict(frozen=True, extra="forbid")
55
+
56
+ deployment_id: UUID
57
+ target_kind: ProcessingTarget
58
+ target_id: UUID
59
+ stage: PipelineStage
60
+ component_version: str
61
+ content_hash: str
62
+ lane: ProcessingLane | None
63
+ payload: dict[str, object] | None = None
64
+ not_before: UTCDateTime | None = None
65
+
66
+
67
+ class EnqueueOutcome(BaseModel):
68
+ """What an enqueue did: created a row, promoted its lane, or found it existing."""
69
+
70
+ model_config = ConfigDict(frozen=True, extra="forbid")
71
+
72
+ processing_id: UUID
73
+ created: bool
74
+ promoted_to_steady: bool
75
+
76
+
77
+ class BackfillSeedRequest(BaseModel):
78
+ """One version-bump campaign to enumerate into the backfill lane."""
79
+
80
+ model_config = ConfigDict(frozen=True, extra="forbid")
81
+
82
+ deployment_id: UUID
83
+ stage: PipelineStage
84
+ component_version: str = Field(min_length=1)
85
+
86
+
87
+ class BackfillSeedResult(BaseModel):
88
+ """Outcome of one bounded, restartable seeder transaction."""
89
+
90
+ model_config = ConfigDict(frozen=True, extra="forbid")
91
+
92
+ selected: int = Field(ge=0)
93
+ created: int = Field(ge=0)
94
+ already_present: int = Field(ge=0)
95
+ complete: bool
96
+
97
+
98
+ class ClaimedWork(BaseModel):
99
+ """A claimed ``processing_state`` row, running its ``attempt``-th handler execution."""
100
+
101
+ model_config = ConfigDict(frozen=True, extra="forbid")
102
+
103
+ processing_id: UUID
104
+ deployment_id: UUID
105
+ target_kind: ProcessingTarget
106
+ target_id: UUID
107
+ stage: PipelineStage
108
+ component_version: str
109
+ content_hash: str
110
+ lane: ProcessingLane | None
111
+ attempt: int = Field(ge=1)
112
+ payload: dict[str, object] | None
113
+
114
+
115
+ class CostBudget(BaseModel):
116
+ """One explicit spend ceiling for a deployment, stage, lane, and fixed window."""
117
+
118
+ model_config = ConfigDict(frozen=True, extra="forbid")
119
+
120
+ deployment_id: UUID
121
+ stage: PipelineStage
122
+ lane: ProcessingLane | None
123
+ window_seconds: int = Field(gt=0)
124
+ ceiling_usd: Decimal = Field(gt=Decimal(0))
125
+
126
+
127
+ class BudgetParked(BaseModel):
128
+ """A due work row parked before its next handler attempt because spend is exhausted."""
129
+
130
+ model_config = ConfigDict(frozen=True, extra="forbid")
131
+
132
+ processing_id: UUID
133
+ resume_at: UTCDateTime
134
+ spent_usd: Decimal = Field(ge=Decimal(0))
135
+ ceiling_usd: Decimal = Field(gt=Decimal(0))
136
+
137
+
138
+ class CostTierSpend(BaseModel):
139
+ """Current-window spend attributed to one recorded cascade tier."""
140
+
141
+ model_config = ConfigDict(frozen=True, extra="forbid")
142
+
143
+ tier: str | None
144
+ cost_usd: Decimal = Field(ge=Decimal(0))
145
+
146
+
147
+ class CostBudgetStatus(BaseModel):
148
+ """Admin-visible state derived from one configured ceiling and the durable ledgers."""
149
+
150
+ model_config = ConfigDict(frozen=True, extra="forbid")
151
+
152
+ deployment_id: UUID
153
+ stage: PipelineStage
154
+ lane: ProcessingLane | None
155
+ window_seconds: int = Field(gt=0)
156
+ window_started_at: UTCDateTime
157
+ window_ends_at: UTCDateTime
158
+ ceiling_usd: Decimal = Field(gt=Decimal(0))
159
+ spent_usd: Decimal = Field(ge=Decimal(0))
160
+ remaining_usd: Decimal = Field(ge=Decimal(0))
161
+ exhausted: bool
162
+ parked_work: int = Field(ge=0)
163
+ tiers: tuple[CostTierSpend, ...]
164
+
165
+
166
+ class RecordCall(BaseModel):
167
+ """One billed model/provider call to attribute to the claimed row's attempt.
168
+
169
+ Attribution fields (stage, lane, attempt) are copied from the locked
170
+ ``processing_state`` row by the spine and can never be supplied here (D67).
171
+ """
172
+
173
+ model_config = ConfigDict(frozen=True, extra="forbid")
174
+
175
+ processing_id: UUID
176
+ call_key: str
177
+ model_name: str | None = None
178
+ tier: str | None = None
179
+ tokens_in: int | None = None
180
+ tokens_out: int | None = None
181
+ cost_usd: Decimal | None = None
182
+ latency_ms: int | None = None
183
+
184
+
185
+ class RunResultOutcome(StrEnum):
186
+ """How one worker pass ended for the row it claimed (or that none was due)."""
187
+
188
+ NO_WORK = "no_work"
189
+ BUDGET_PARKED = "budget_parked"
190
+ SUCCEEDED = "succeeded"
191
+ RETRY_SCHEDULED = "retry_scheduled"
192
+ DEAD_LETTERED = "dead_lettered"
193
+
194
+
195
+ class WorkLedgerError(Exception):
196
+ """Base error for work-ledger operations."""
197
+
198
+
199
+ class BackfillNotDrainedError(WorkLedgerError):
200
+ """Search-index maintenance was requested while backfill work was unresolved."""
201
+
202
+
203
+ class LaneRouteError(WorkLedgerError):
204
+ """A lane value that is illegal for the stage's route (D67 pairing rule)."""
205
+
206
+
207
+ class WorkNotFoundError(WorkLedgerError):
208
+ """A ``processing_id`` that does not exist in ``processing_state``."""
209
+
210
+
211
+ class WorkNotRunningError(WorkLedgerError):
212
+ """An operation that requires a running attempt hit a non-running row."""
213
+
214
+
215
+ class WorkNotDeadLetterError(WorkLedgerError):
216
+ """An operation that requires a dead-letter row hit another status."""
217
+
218
+
219
+ class NonRetryableHandlerError(Exception):
220
+ """A handler failure classified as permanent: the work dead-letters immediately."""
221
+
222
+
223
+ class HandlerAlreadyRegisteredError(Exception):
224
+ """A second handler registration for a stage that already has one."""
225
+
226
+
227
+ class UnknownStageHandlerError(Exception):
228
+ """A claimed stage with no registered handler."""
@@ -0,0 +1,73 @@
1
+ """D67 queue-route snapshots and the binding Postgres stage/lane vocabulary."""
2
+
3
+ from datetime import datetime
4
+ from datetime import timedelta
5
+ from enum import StrEnum
6
+ from typing import Annotated
7
+ from typing import TypeAlias
8
+ from uuid import UUID
9
+
10
+ from pydantic import AfterValidator
11
+ from pydantic import BaseModel
12
+ from pydantic import ConfigDict
13
+ from pydantic import Field
14
+
15
+
16
+ class PipelineStage(StrEnum):
17
+ """Exact values of the binding Postgres ``pipeline_stage`` enum."""
18
+
19
+ INGEST = "ingest"
20
+ CONVERT = "convert"
21
+ STRUCTURE = "structure"
22
+ CROSSREF = "crossref"
23
+ CHUNK = "chunk"
24
+ EMBED_CHUNK = "embed_chunk"
25
+ EXTRACT_CLAIMS = "extract_claims"
26
+ EMBED_CLAIM = "embed_claim"
27
+ GROUND_CLAIMS = "ground_claims"
28
+ RESOLVE_ENTITIES = "resolve_entities"
29
+ NORMALIZE_RELATIONS = "normalize_relations"
30
+ ADJUDICATE_SUPERSESSION = "adjudicate_supersession"
31
+ ADJUDICATE_OBSERVATIONS = "adjudicate_observations"
32
+ EMBED_RELATION = "embed_relation"
33
+ LABEL_RELATION = "label_relation"
34
+ EMBED_OBSERVATION = "embed_observation"
35
+ LABEL_OBSERVATION = "label_observation"
36
+ REFRESH_PROFILE = "refresh_profile"
37
+ BUILD_SNAPSHOT = "build_snapshot"
38
+ DETECT_COMMUNITIES = "detect_communities"
39
+ COMPILE_KNOWLEDGE = "compile_knowledge"
40
+ REFLECT_KNOWLEDGE = "reflect_knowledge"
41
+ LINT_KNOWLEDGE = "lint_knowledge"
42
+ RECONCILE = "reconcile"
43
+ DISPATCH_KNOWLEDGE = "dispatch_knowledge" # appended by WP-6.6
44
+ HARD_FORGET = "hard_forget"
45
+
46
+
47
+ class ProcessingLane(StrEnum):
48
+ """The two and only two lane values used by Plane-E work."""
49
+
50
+ STEADY = "steady"
51
+ BACKFILL = "backfill"
52
+
53
+
54
+ def _require_utc(value: datetime) -> datetime:
55
+ """Require an aware datetime whose UTC offset is exactly zero."""
56
+ if value.tzinfo is None or value.utcoffset() != timedelta(0):
57
+ raise ValueError("datetime must be timezone-aware UTC")
58
+ return value
59
+
60
+
61
+ UTCDateTime: TypeAlias = Annotated[
62
+ datetime, Field(strict=True), AfterValidator(_require_utc)
63
+ ]
64
+
65
+
66
+ class QueueRoute(BaseModel):
67
+ """Non-authoritative D67 physical-delivery route snapshot."""
68
+
69
+ model_config = ConfigDict(frozen=True, extra="forbid")
70
+
71
+ deployment_id: UUID
72
+ stage: PipelineStage
73
+ lane: ProcessingLane | None
@@ -0,0 +1,83 @@
1
+ """Recipe registry values (D50): frozen query plans as data, not code.
2
+
3
+ A **recipe** is a named, versioned composition of the zero-LLM primitives
4
+ (retrieval §4) — `relation_hybrid_rrf`, `claims_as_of`, `entity_timeline`,
5
+ and the rest. It is a *registry row*, never code: the MCP tool list renders
6
+ from these rows, the eval harness measures recall@k per recipe version, and
7
+ adding a query pattern is inserting a row. The load-bearing property is that
8
+ a recipe adds **no capability** — anything it does, an agent can compose from
9
+ §3 — so a recipe is exactly its `chain`, and the eval harness proves it by
10
+ replaying the chain and diffing.
11
+
12
+ Two declared enums make the D41 grain bar ("claims never answer *is it true
13
+ now*") a mechanical constraint rather than a prose judgment: `output_grain`
14
+ (the D49 envelope grain the recipe returns) and `answer_intent` (what kind of
15
+ question it answers). The database CHECK enforces the headline rule
16
+ (`current_facts` ⇒ `fact` grain); the registration linter (`core`) validates
17
+ the chain itself against the same enums.
18
+ """
19
+
20
+ from enum import StrEnum
21
+
22
+ from pydantic import BaseModel
23
+ from pydantic import ConfigDict
24
+ from pydantic import Field
25
+
26
+ from rememberstack.model.envelope import Grain
27
+
28
+
29
+ class RecipeAnswerIntent(StrEnum):
30
+ """What kind of question a recipe answers (the D50 `answer_intent`).
31
+
32
+ The intent is what the grain linter reasons over: `current_facts` may
33
+ only ride validity-filtered fact primitives, `assertion_history` is the
34
+ evidence-grain "what did sources assert" read, `audit` is the decision
35
+ trail, `change_feed` is the delta, and `orientation` is a pre-paid
36
+ synthesis (K pages, briefs).
37
+ """
38
+
39
+ CURRENT_FACTS = "current_facts"
40
+ ASSERTION_HISTORY = "assertion_history"
41
+ ORIENTATION = "orientation"
42
+ AUDIT = "audit"
43
+ CHANGE_FEED = "change_feed"
44
+
45
+
46
+ class RecipeStep(BaseModel):
47
+ """One primitive invocation in a recipe chain (retrieval §3 op + settings).
48
+
49
+ `op` names a §3 primitive; `settings` are the FIXED arguments the recipe
50
+ freezes (channel sets, RRF constants, rerank weights); `bind` maps a
51
+ primitive keyword to the name of a recipe parameter the caller supplies;
52
+ and `inputs` names the prior steps (by index) whose outputs this op
53
+ consumes — how `fuse` references the two searches above it. A recipe with
54
+ one step is the common case (a recipe over a single primitive with frozen
55
+ settings); the chain generalizes to fusion pipelines.
56
+ """
57
+
58
+ model_config = ConfigDict(frozen=True, extra="forbid")
59
+
60
+ op: str
61
+ settings: dict[str, object] = Field(default_factory=dict)
62
+ bind: dict[str, str] = Field(default_factory=dict)
63
+ inputs: tuple[int, ...] = ()
64
+
65
+
66
+ class Recipe(BaseModel):
67
+ """One recipe registry row (D50): the frozen plan and its declared grain.
68
+
69
+ `parameters` is a JSON-Schema-shaped description of the arguments a
70
+ caller passes (rendered into the MCP tool signature). `chain` is the
71
+ ordered primitive composition — the recipe's entire behavior. The two
72
+ enums are the contract the linter and the DB CHECK enforce.
73
+ """
74
+
75
+ model_config = ConfigDict(frozen=True, extra="forbid")
76
+
77
+ name: str = Field(min_length=1)
78
+ description: str = Field(min_length=1)
79
+ parameters: dict[str, object] = Field(default_factory=dict)
80
+ chain: tuple[RecipeStep, ...] = Field(min_length=1)
81
+ output_grain: Grain
82
+ answer_intent: RecipeAnswerIntent
83
+ version: int = Field(default=1, ge=1)
@@ -0,0 +1,79 @@
1
+ """E3 normalization values: LLM candidates, resolution, and fact records (D2-D5, D17-D18, D43)."""
2
+
3
+ from typing import Annotated
4
+ from uuid import UUID
5
+
6
+ from pydantic import BaseModel
7
+ from pydantic import ConfigDict
8
+ from pydantic import Field
9
+
10
+ _NonEmpty = Annotated[str, Field(min_length=1)]
11
+
12
+
13
+ class EntityRef(BaseModel):
14
+ """One entity as the normalizer emitted it: canonical form + registry type."""
15
+
16
+ model_config = ConfigDict(frozen=True, extra="forbid")
17
+
18
+ name: _NonEmpty
19
+ type: _NonEmpty
20
+
21
+
22
+ class RelationCandidate(BaseModel):
23
+ """One (subject, predicate, object) proposal from the normalizer call."""
24
+
25
+ model_config = ConfigDict(frozen=True, extra="forbid")
26
+
27
+ subject: EntityRef
28
+ predicate: _NonEmpty
29
+ object: EntityRef
30
+
31
+
32
+ class ObservationCandidate(BaseModel):
33
+ """One entity-anchored value/statement proposal (D43), incl. stances (D59)."""
34
+
35
+ model_config = ConfigDict(frozen=True, extra="forbid")
36
+
37
+ subject: EntityRef
38
+ statement: _NonEmpty
39
+
40
+
41
+ class ObservationAssertion(BaseModel):
42
+ """One resolved observation input in a document/entity adjudication batch."""
43
+
44
+ model_config = ConfigDict(frozen=True, extra="forbid")
45
+
46
+ statement: _NonEmpty
47
+ claim_id: UUID
48
+ doc_id: UUID
49
+
50
+
51
+ class NormalizationResponse(BaseModel):
52
+ """The normalizer call's structured output for one claim (0..n of each)."""
53
+
54
+ model_config = ConfigDict(frozen=True, extra="forbid")
55
+
56
+ relations: tuple[RelationCandidate, ...] = ()
57
+ observations: tuple[ObservationCandidate, ...] = ()
58
+
59
+
60
+ class ClaimForNormalization(BaseModel):
61
+ """One accepted claim as the normalize stage loads it."""
62
+
63
+ model_config = ConfigDict(frozen=True, extra="forbid")
64
+
65
+ claim_id: UUID
66
+ doc_id: UUID
67
+ chunk_id: UUID
68
+ claim_text: str
69
+ is_attributed: bool
70
+
71
+
72
+ class ResolvedEntity(BaseModel):
73
+ """A T0 resolution outcome: the canonical id and whether it was minted."""
74
+
75
+ model_config = ConfigDict(frozen=True, extra="forbid")
76
+
77
+ entity_id: UUID
78
+ created: bool
79
+ entity_type: _NonEmpty
@@ -0,0 +1,83 @@
1
+ """ER cascade values (D17): candidates, bands, verdicts, and the T4 response.
2
+
3
+ Block-loose / decide-tight: T1/T2 generate candidates and never decide; T0,
4
+ T3, and T4 decide. Thresholds are per-type, golden-set-measured starting
5
+ points versioned in `resolver_versions` — never committed constants.
6
+ """
7
+
8
+ from typing import Annotated
9
+ from uuid import UUID
10
+
11
+ from pydantic import BaseModel
12
+ from pydantic import ConfigDict
13
+ from pydantic import Field
14
+
15
+ _NonEmpty = Annotated[str, Field(min_length=1)]
16
+ _Unit = Annotated[float, Field(ge=-1.0, le=1.0)]
17
+
18
+
19
+ class ResolutionCandidate(BaseModel):
20
+ """One blocked candidate: which tier surfaced it and its scores."""
21
+
22
+ model_config = ConfigDict(frozen=True, extra="forbid")
23
+
24
+ entity_id: UUID
25
+ canonical_name: _NonEmpty
26
+ type: _NonEmpty
27
+ blocking_tier: _NonEmpty # T0 | T1 | T2
28
+ trigram_score: float | None = None
29
+ embedding_score: _Unit | None = None
30
+
31
+
32
+ class TypeThresholds(BaseModel):
33
+ """One entity type's decision bands (starting points to measure, D22).
34
+
35
+ T3 cosine >= accept: match. <= reject: not this candidate. Between the
36
+ bands: escalate to T4 — cheap tiers never auto-reject near-misses (the
37
+ blocking ceiling is a recall ceiling, not a verdict).
38
+ """
39
+
40
+ model_config = ConfigDict(frozen=True, extra="forbid")
41
+
42
+ t3_accept: _Unit = 0.88
43
+ t3_reject: _Unit = 0.60
44
+ t4_small_confidence_floor: Annotated[float, Field(ge=0.0, le=1.0)] = 0.75
45
+
46
+
47
+ class ResolverConfig(BaseModel):
48
+ """The versioned cascade configuration (`resolver_versions` row shape)."""
49
+
50
+ model_config = ConfigDict(frozen=True, extra="forbid")
51
+
52
+ resolver_version: _NonEmpty
53
+ trigram_floor: Annotated[float, Field(ge=0.0, le=1.0)] = 0.3
54
+ blocking_limit: Annotated[int, Field(ge=1)] = 10
55
+ t4_max_candidates: Annotated[int, Field(ge=1)] = 3
56
+ default_thresholds: TypeThresholds = TypeThresholds()
57
+ thresholds_by_type: dict[str, TypeThresholds] = {}
58
+
59
+ def thresholds_for(self, *, entity_type: str) -> TypeThresholds:
60
+ """The type's bands, falling back to the defaults."""
61
+ return self.thresholds_by_type.get(entity_type, self.default_thresholds)
62
+
63
+
64
+ class AdjudicationVerdict(BaseModel):
65
+ """The T4 call's structured output: same entity or not, with confidence."""
66
+
67
+ model_config = ConfigDict(frozen=True, extra="forbid")
68
+
69
+ match: bool
70
+ confidence: Annotated[float, Field(ge=0.0, le=1.0)]
71
+ rationale: str | None = None
72
+
73
+
74
+ class P1EntityRow(BaseModel):
75
+ """One row of the P1 entities table: the T3 profile embedding home (D8)."""
76
+
77
+ model_config = ConfigDict(frozen=True, extra="forbid")
78
+
79
+ entity_id: UUID
80
+ deployment_id: UUID
81
+ type: _NonEmpty
82
+ canonical_name: _NonEmpty
83
+ vector: Annotated[tuple[float, ...], Field(min_length=1)]