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,298 @@
1
+ """Bounded read-only operational inspection over the durable Postgres truth."""
2
+
3
+ import re
4
+ from uuid import UUID
5
+
6
+ from pydantic import Field
7
+ from pydantic_settings import BaseSettings
8
+ from pydantic_settings import SettingsConfigDict
9
+ from sqlalchemy import text
10
+ from sqlalchemy.engine import Connection
11
+ from sqlalchemy.engine import Engine
12
+ from sqlalchemy.sql.elements import TextClause
13
+
14
+ from rememberstack.model import CurrencyLedgerAudit
15
+ from rememberstack.model import CurrencyMismatch
16
+ from rememberstack.model import DeadLetterGroup
17
+ from rememberstack.model import DeadLetterRecord
18
+ from rememberstack.model import DeadLetterReport
19
+ from rememberstack.model import OperationalReport
20
+ from rememberstack.model import PipelineRouteStatus
21
+ from rememberstack.model import PoisonTargetRecord
22
+ from rememberstack.model import PoisonTargetReport
23
+ from rememberstack.model import ProjectionSnapshotState
24
+ from rememberstack.spine.lifecycle import CURRENCY_CACHE_MISMATCH_SQL
25
+
26
+ _ERROR_CLASS = re.compile(r"^[A-Za-z_][A-Za-z0-9_.]*$")
27
+
28
+
29
+ class OperationalSettings(BaseSettings):
30
+ """One cap shared by every variable-size operational detail collection."""
31
+
32
+ model_config = SettingsConfigDict(
33
+ env_prefix="REMEMBERSTACK_OPERATIONAL_", extra="ignore"
34
+ )
35
+
36
+ sample_limit: int = Field(default=20, ge=1, le=1_000)
37
+
38
+
39
+ class OperationalCatalog:
40
+ """Build a coherent deployment report without a dashboard or control plane."""
41
+
42
+ def __init__(self, *, engine: Engine, settings: OperationalSettings) -> None:
43
+ """Bind an engine and the single explicit sample bound."""
44
+ self._engine = engine
45
+ self._settings = settings
46
+
47
+ def inspect(self, *, deployment_id: UUID) -> OperationalReport:
48
+ """Return aggregate truth plus bounded evidence in one repeatable-read view."""
49
+ parameters = {
50
+ "deployment_id": deployment_id,
51
+ "sample_limit": self._settings.sample_limit,
52
+ }
53
+ with self._engine.connect().execution_options(
54
+ isolation_level="REPEATABLE READ"
55
+ ) as connection:
56
+ generated_at = connection.execute(text("SELECT now()")).scalar_one()
57
+ routes = tuple(
58
+ PipelineRouteStatus(**row)
59
+ for row in connection.execute(_ROUTE_STATUS, parameters).mappings()
60
+ )
61
+ dead_letters = self._dead_letters(
62
+ connection=connection, parameters=parameters
63
+ )
64
+ poison_targets = self._poison_targets(
65
+ connection=connection, parameters=parameters
66
+ )
67
+ latest_projections = tuple(
68
+ ProjectionSnapshotState(**row)
69
+ for row in connection.execute(
70
+ _LATEST_PROJECTIONS, parameters
71
+ ).mappings()
72
+ )
73
+ currency = self._currency(connection=connection, parameters=parameters)
74
+ return OperationalReport(
75
+ deployment_id=deployment_id,
76
+ generated_at=generated_at,
77
+ routes=routes,
78
+ dead_letters=dead_letters,
79
+ poison_targets=poison_targets,
80
+ latest_projections=latest_projections,
81
+ currency=currency,
82
+ )
83
+
84
+ def _dead_letters(
85
+ self, *, connection: Connection, parameters: dict[str, object]
86
+ ) -> DeadLetterReport:
87
+ """Return DLQ totals and bounded grouped/detail evidence."""
88
+ total = _count(connection=connection, statement=_DLQ_TOTAL, values=parameters)
89
+ group_total = _count(
90
+ connection=connection, statement=_DLQ_GROUP_TOTAL, values=parameters
91
+ )
92
+ groups = tuple(
93
+ DeadLetterGroup(**row)
94
+ for row in connection.execute(_DLQ_GROUPS, parameters).mappings()
95
+ )
96
+ items = tuple(
97
+ DeadLetterRecord(
98
+ **row, error_class=error_class_from_traceback(row["last_error"])
99
+ )
100
+ for row in connection.execute(_DLQ_ITEMS, parameters).mappings()
101
+ )
102
+ return DeadLetterReport(
103
+ total=total, group_total=group_total, groups=groups, items=items
104
+ )
105
+
106
+ def _poison_targets(
107
+ self, *, connection: Connection, parameters: dict[str, object]
108
+ ) -> PoisonTargetReport:
109
+ """Find targets whose same stage failed under multiple component versions."""
110
+ total = _count(
111
+ connection=connection, statement=_POISON_TOTAL, values=parameters
112
+ )
113
+ items = tuple(
114
+ PoisonTargetRecord(
115
+ target_kind=row["target_kind"],
116
+ target_id=row["target_id"],
117
+ stage=row["stage"],
118
+ component_version_total=row["component_version_total"],
119
+ component_versions=tuple(row["component_versions"]),
120
+ dead_letters=row["dead_letters"],
121
+ )
122
+ for row in connection.execute(_POISON_ITEMS, parameters).mappings()
123
+ )
124
+ return PoisonTargetReport(total=total, items=items)
125
+
126
+ def _currency(
127
+ self, *, connection: Connection, parameters: dict[str, object]
128
+ ) -> CurrencyLedgerAudit:
129
+ """Apply the exact lifecycle invariant and bound its diagnostic rows."""
130
+ claims = _count(
131
+ connection=connection, statement=_CLAIM_TOTAL, values=parameters
132
+ )
133
+ mismatch_total = _count(
134
+ connection=connection, statement=_CURRENCY_TOTAL, values=parameters
135
+ )
136
+ mismatches = tuple(
137
+ CurrencyMismatch(**row)
138
+ for row in connection.execute(_CURRENCY_ITEMS, parameters).mappings()
139
+ )
140
+ return CurrencyLedgerAudit(
141
+ claims=claims, mismatch_total=mismatch_total, mismatches=mismatches
142
+ )
143
+
144
+
145
+ def error_class_from_traceback(error: str | None) -> str:
146
+ """Derive one stable class from the last non-empty traceback line."""
147
+ if error is None:
148
+ return "unknown"
149
+ lines = [line.strip() for line in error.splitlines() if line.strip()]
150
+ if not lines:
151
+ return "unknown"
152
+ candidate = lines[-1].split(":", maxsplit=1)[0].strip()
153
+ return candidate if _ERROR_CLASS.fullmatch(candidate) is not None else "unknown"
154
+
155
+
156
+ def _count(
157
+ *, connection: Connection, statement: TextClause, values: dict[str, object]
158
+ ) -> int:
159
+ """Normalize one PostgreSQL count scalar."""
160
+ return int(connection.execute(statement, values).scalar_one())
161
+
162
+
163
+ _ROUTE_STATUS = text(
164
+ """
165
+ SELECT stage, lane, status, count(*) AS count
166
+ FROM processing_state
167
+ WHERE deployment_id = :deployment_id
168
+ GROUP BY stage, lane, status
169
+ ORDER BY stage, lane NULLS FIRST, status
170
+ """
171
+ )
172
+
173
+ _CLASSIFIED_DEAD_LETTERS = """
174
+ WITH dead_letters AS (
175
+ SELECT stage, component_version, enqueued_at, finished_at,
176
+ btrim(reverse(split_part(
177
+ reverse(rtrim(coalesce(last_error, ''), E' \\t\\n\\r')),
178
+ E'\\n', 1
179
+ ))) AS last_line
180
+ FROM processing_state
181
+ WHERE deployment_id = :deployment_id AND status = 'dead_letter'
182
+ ), classified AS (
183
+ SELECT stage, component_version, enqueued_at, finished_at,
184
+ CASE
185
+ WHEN split_part(last_line, ':', 1)
186
+ ~ '^[A-Za-z_][A-Za-z0-9_.]*$'
187
+ THEN split_part(last_line, ':', 1)
188
+ ELSE 'unknown'
189
+ END AS error_class
190
+ FROM dead_letters
191
+ )
192
+ """
193
+
194
+ _DLQ_TOTAL = text(
195
+ """
196
+ SELECT count(*) FROM processing_state
197
+ WHERE deployment_id = :deployment_id AND status = 'dead_letter'
198
+ """
199
+ )
200
+
201
+ _DLQ_GROUP_TOTAL = text(
202
+ _CLASSIFIED_DEAD_LETTERS
203
+ + """
204
+ SELECT count(*) FROM (
205
+ SELECT 1 FROM classified
206
+ GROUP BY stage, error_class, component_version
207
+ ) groups
208
+ """
209
+ )
210
+
211
+ _DLQ_GROUPS = text(
212
+ _CLASSIFIED_DEAD_LETTERS
213
+ + """
214
+ SELECT stage, error_class, component_version, count(*) AS count,
215
+ min(enqueued_at) AS oldest_enqueued_at,
216
+ max(finished_at) AS latest_finished_at
217
+ FROM classified
218
+ GROUP BY stage, error_class, component_version
219
+ ORDER BY count(*) DESC, stage, error_class, component_version
220
+ LIMIT :sample_limit
221
+ """
222
+ )
223
+
224
+ _DLQ_ITEMS = text(
225
+ """
226
+ SELECT processing_id, target_kind, target_id, stage, component_version,
227
+ content_hash, lane, attempts, max_attempts, last_error, payload,
228
+ enqueued_at, finished_at
229
+ FROM processing_state
230
+ WHERE deployment_id = :deployment_id AND status = 'dead_letter'
231
+ ORDER BY finished_at DESC NULLS LAST, processing_id
232
+ LIMIT :sample_limit
233
+ """
234
+ )
235
+
236
+ _POISON_GROUPS = """
237
+ SELECT target_kind, target_id, stage,
238
+ count(DISTINCT component_version) AS component_version_total,
239
+ count(*) AS dead_letters
240
+ FROM processing_state
241
+ WHERE deployment_id = :deployment_id AND status = 'dead_letter'
242
+ GROUP BY target_kind, target_id, stage
243
+ HAVING count(DISTINCT component_version) >= 2
244
+ """
245
+
246
+ _POISON_TOTAL = text("SELECT count(*) FROM (" + _POISON_GROUPS + ") poison")
247
+
248
+ _POISON_ITEMS = text(
249
+ "WITH poison AS ("
250
+ + _POISON_GROUPS
251
+ + ")"
252
+ + """
253
+ SELECT poison.*,
254
+ ARRAY(
255
+ SELECT DISTINCT versioned.component_version
256
+ FROM processing_state versioned
257
+ WHERE versioned.deployment_id = :deployment_id
258
+ AND versioned.status = 'dead_letter'
259
+ AND versioned.target_kind = poison.target_kind
260
+ AND versioned.target_id = poison.target_id
261
+ AND versioned.stage = poison.stage
262
+ ORDER BY versioned.component_version
263
+ LIMIT :sample_limit
264
+ ) AS component_versions
265
+ FROM poison
266
+ ORDER BY poison.dead_letters DESC, poison.target_kind,
267
+ poison.target_id, poison.stage
268
+ LIMIT :sample_limit
269
+ """
270
+ )
271
+
272
+ _LATEST_PROJECTIONS = text(
273
+ """
274
+ SELECT snapshot_id, plane, version, gcs_uri AS store_uri, row_counts,
275
+ built_at, published_at
276
+ FROM projection_snapshots
277
+ WHERE deployment_id = :deployment_id
278
+ AND plane IN ('P2_graph', 'P3_corpusfs')
279
+ AND is_latest
280
+ ORDER BY plane
281
+ """
282
+ )
283
+
284
+ _CLAIM_TOTAL = text("SELECT count(*) FROM claims WHERE deployment_id = :deployment_id")
285
+
286
+ _CURRENCY_TOTAL = text(
287
+ "WITH mismatches AS ("
288
+ + CURRENCY_CACHE_MISMATCH_SQL
289
+ + ") SELECT count(*) FROM mismatches"
290
+ )
291
+
292
+ _CURRENCY_ITEMS = text(
293
+ CURRENCY_CACHE_MISMATCH_SQL
294
+ + """
295
+ ORDER BY cl.claim_id
296
+ LIMIT :sample_limit
297
+ """
298
+ )