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,62 @@
1
+ """Typed results for the WP-5.6 retrieval spike battery."""
2
+
3
+ from typing import Annotated
4
+ from typing import get_args
5
+ from typing import Literal
6
+
7
+ from pydantic import BaseModel
8
+ from pydantic import ConfigDict
9
+ from pydantic import Field
10
+ from pydantic import model_validator
11
+
12
+ RetrievalSpikeName = Literal[
13
+ "lance_filtered_search",
14
+ "hub_pagination",
15
+ "rerank_weights",
16
+ "envelope_overhead",
17
+ "hydration_batching",
18
+ "resolve_context",
19
+ ]
20
+
21
+ RETRIEVAL_SPIKE_NAMES = frozenset(get_args(RetrievalSpikeName))
22
+ """The six WP-5.6 measurements; S58 and as-of graph cost closed earlier."""
23
+
24
+
25
+ class RetrievalSpikeMeasurement(BaseModel):
26
+ """One measured question, its selected setting, and honest limitations."""
27
+
28
+ model_config = ConfigDict(frozen=True, extra="forbid")
29
+
30
+ name: RetrievalSpikeName
31
+ scale: Annotated[int, Field(ge=1)]
32
+ metrics: dict[str, object]
33
+ selected: dict[str, object]
34
+ limitations: tuple[str, ...] = ()
35
+ passed: bool
36
+
37
+
38
+ class RetrievalSpikeReport(BaseModel):
39
+ """The complete six-spike WP-5.6 result written to ``eval_runs``."""
40
+
41
+ model_config = ConfigDict(frozen=True, extra="forbid")
42
+
43
+ measurements: Annotated[
44
+ tuple[RetrievalSpikeMeasurement, ...], Field(min_length=6, max_length=6)
45
+ ]
46
+
47
+ @model_validator(mode="after")
48
+ def complete_battery(self) -> "RetrievalSpikeReport":
49
+ """Reject duplicate or missing measurements; absence is not compliance."""
50
+ names = {measurement.name for measurement in self.measurements}
51
+ if names != RETRIEVAL_SPIKE_NAMES:
52
+ missing = sorted(RETRIEVAL_SPIKE_NAMES - names)
53
+ extra = sorted(names - RETRIEVAL_SPIKE_NAMES)
54
+ raise ValueError(
55
+ f"retrieval spike names mismatch: missing={missing}, extra={extra}"
56
+ )
57
+ return self
58
+
59
+ @property
60
+ def passed(self) -> bool:
61
+ """The battery passes only when every measured invariant holds."""
62
+ return all(measurement.passed for measurement in self.measurements)
@@ -0,0 +1,120 @@
1
+ """Section-structure values (D39/D57): the LLM's proposal and the snapped truth.
2
+
3
+ Two deliberately different trust levels share this module. `ProposedSection` /
4
+ `StructureResponse` are the structurer LLM's raw output — free-hand character
5
+ spans that may overlap, gap, nest wrongly, or point outside the document; they
6
+ are never persisted. `SnappedSection` is what the deterministic snap
7
+ (`core/section_snap.py`) makes of them: a well-formed partition on the block
8
+ grid, the only form that reaches `document_sections`.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from uuid import UUID
14
+
15
+ from pydantic import BaseModel
16
+ from pydantic import ConfigDict
17
+ from pydantic import Field
18
+ from pydantic import model_validator
19
+
20
+
21
+ class ProposedSection(BaseModel):
22
+ """One LLM-proposed section span (pre-snap): untrusted free-hand geometry."""
23
+
24
+ model_config = ConfigDict(frozen=True, extra="ignore")
25
+
26
+ title: str = ""
27
+ role: str = "body"
28
+ char_start: int = 0
29
+ char_end: int = 0
30
+ summary: str = ""
31
+ children: tuple[ProposedSection, ...] = ()
32
+
33
+
34
+ class StructureResponse(BaseModel):
35
+ """The structurer's structured output: a proposed tree + placement hint."""
36
+
37
+ model_config = ConfigDict(frozen=True, extra="ignore")
38
+
39
+ sections: tuple[ProposedSection, ...] = ()
40
+ placement: str = ""
41
+
42
+
43
+ class SnappedSection(BaseModel):
44
+ """One well-formed section after the deterministic snap (block coordinates).
45
+
46
+ ``block_end`` is inclusive; the empty document's root carries the empty
47
+ range ``0..-1`` on the block grid (D57) with a zero-width char span.
48
+ """
49
+
50
+ model_config = ConfigDict(frozen=True, extra="forbid")
51
+
52
+ node_path: str # materialized path, e.g. '0.2.1'; the root is '0'
53
+ parent_path: str | None
54
+ title: str
55
+ role: str
56
+ block_start: int = Field(ge=0)
57
+ block_end: int = Field(ge=-1)
58
+ char_start: int = Field(ge=0)
59
+ char_end: int = Field(ge=0)
60
+ summary: str
61
+ ordinal: int = Field(ge=0)
62
+
63
+
64
+ class SectionTreeRecord(BaseModel):
65
+ """The complete write input for one representation's section tree.
66
+
67
+ ``sections`` is in depth-first document order with the root first — the
68
+ catalog resolves each row's parent id from the paths as it inserts, so
69
+ the record refuses any ordering or path structure that would silently
70
+ persist a disconnected tree (an orphan row would reach E1 as a second
71
+ root and double-chunk its range).
72
+ """
73
+
74
+ model_config = ConfigDict(frozen=True, extra="forbid")
75
+
76
+ deployment_id: UUID
77
+ doc_id: UUID
78
+ version_id: UUID
79
+ representation_id: UUID
80
+ sections: tuple[SnappedSection, ...] = Field(min_length=1)
81
+ placement_path: str | None
82
+ structurer_name: str
83
+ structurer_version: str
84
+
85
+ @model_validator(mode="after")
86
+ def _tree_is_connected(self) -> SectionTreeRecord:
87
+ """Root first; every node extends a parent that appeared before it."""
88
+ root = self.sections[0]
89
+ if root.node_path != "0" or root.parent_path is not None:
90
+ raise ValueError("the first section must be the root '0'")
91
+ seen = {root.node_path}
92
+ for section in self.sections[1:]:
93
+ if section.node_path in seen:
94
+ raise ValueError(f"duplicate section path {section.node_path!r}")
95
+ if section.parent_path not in seen:
96
+ raise ValueError(
97
+ f"section {section.node_path!r} appears before its parent"
98
+ )
99
+ if section.node_path.rsplit(".", 1)[0] != section.parent_path:
100
+ raise ValueError(
101
+ f"section {section.node_path!r} does not extend its"
102
+ f" parent path {section.parent_path!r}"
103
+ )
104
+ seen.add(section.node_path)
105
+ return self
106
+
107
+
108
+ class PersistedSectionTree(BaseModel):
109
+ """What one representation's section-tree write actually landed.
110
+
111
+ On a retried attempt the FIRST write wins row by row, so the caller must
112
+ treat this — not its own input — as the truth (the sidecar is derived
113
+ from it, never from a fresher LLM proposal).
114
+ """
115
+
116
+ model_config = ConfigDict(frozen=True, extra="forbid")
117
+
118
+ sections: tuple[SnappedSection, ...] = Field(min_length=1)
119
+ placement_path: str | None
120
+ structurer_version: str
@@ -0,0 +1,30 @@
1
+ """Structured telemetry values that remain independent of exporter SDKs."""
2
+
3
+ from typing import Annotated
4
+
5
+ from pydantic import BaseModel
6
+ from pydantic import ConfigDict
7
+ from pydantic import Field
8
+
9
+ from rememberstack.model.queue import UTCDateTime
10
+
11
+ TelemetryScalar = str | int | float | bool | None
12
+
13
+
14
+ class TelemetryAttribute(BaseModel):
15
+ """One immutable structured attribute on a telemetry event."""
16
+
17
+ model_config = ConfigDict(frozen=True, extra="forbid")
18
+
19
+ name: Annotated[str, Field(min_length=1)]
20
+ value: TelemetryScalar
21
+
22
+
23
+ class TelemetryEvent(BaseModel):
24
+ """Provider-neutral structured event ready for export."""
25
+
26
+ model_config = ConfigDict(frozen=True, extra="forbid")
27
+
28
+ name: Annotated[str, Field(min_length=1)]
29
+ occurred_at: UTCDateTime
30
+ attributes: tuple[TelemetryAttribute, ...]
@@ -0,0 +1,29 @@
1
+ """Provider-neutral deployment substrate and store-capability protocols."""
2
+
3
+ from rememberstack.ports.auth import AuthPerimeterPort
4
+ from rememberstack.ports.forget import ForgetManifestPort
5
+ from rememberstack.ports.git import KGitRemotePort
6
+ from rememberstack.ports.model_provider import ModelProviderPort
7
+ from rememberstack.ports.mounts import MountPublisherPort
8
+ from rememberstack.ports.object_store import ObjectStorePort
9
+ from rememberstack.ports.purge import KGitPurgePort
10
+ from rememberstack.ports.purge import ObjectPurgePort
11
+ from rememberstack.ports.purge import P1PurgePort
12
+ from rememberstack.ports.purge import ProjectionPurgePort
13
+ from rememberstack.ports.queue import TaskQueuePort
14
+ from rememberstack.ports.telemetry import TelemetryPort
15
+
16
+ __all__ = (
17
+ "AuthPerimeterPort",
18
+ "ForgetManifestPort",
19
+ "KGitPurgePort",
20
+ "KGitRemotePort",
21
+ "ModelProviderPort",
22
+ "MountPublisherPort",
23
+ "ObjectStorePort",
24
+ "ObjectPurgePort",
25
+ "P1PurgePort",
26
+ "ProjectionPurgePort",
27
+ "TaskQueuePort",
28
+ "TelemetryPort",
29
+ )
@@ -0,0 +1,16 @@
1
+ """D50/D60 auth-perimeter seam for a single-deployment trust domain."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+
6
+ from rememberstack.model import AuthenticatedContext
7
+ from rememberstack.model import PerimeterCredential
8
+
9
+
10
+ @runtime_checkable
11
+ class AuthPerimeterPort(Protocol):
12
+ """Authenticate perimeter credentials without introducing internal tenancy."""
13
+
14
+ def authenticate(self, *, credential: PerimeterCredential) -> AuthenticatedContext:
15
+ """Return the authenticated principal and its one deployment context."""
16
+ ...
@@ -0,0 +1,23 @@
1
+ """D61 seam for watched sources: poll observations, fetch bytes (lifecycle §2)."""
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Protocol
5
+ from typing import runtime_checkable
6
+
7
+ from rememberstack.model import SourceItem
8
+
9
+
10
+ @runtime_checkable
11
+ class WatchedSourcePort(Protocol):
12
+ """One watched source: enumerate current items and detect deletions."""
13
+
14
+ def poll(self, *, known: Mapping[str, str]) -> tuple[SourceItem, ...]:
15
+ """Report every current item plus a deleted-marked item for each
16
+ known source_ref no longer present. `known` maps source_ref → the
17
+ last ingested revision, so unchanged items can skip fetch entirely.
18
+ """
19
+ ...
20
+
21
+ def fetch(self, *, source_ref: str) -> bytes:
22
+ """The item's current bytes (called only for changed items)."""
23
+ ...
@@ -0,0 +1,17 @@
1
+ """Provider-neutral sink for attributing one worker attempt's model calls."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+
6
+ from rememberstack.model import ProviderCallUsage
7
+
8
+
9
+ @runtime_checkable
10
+ class CostMeterPort(Protocol):
11
+ """Record provider usage under a deterministic call key and cascade tier."""
12
+
13
+ def record(
14
+ self, *, call_key: str, tier: str | None, usage: ProviderCallUsage
15
+ ) -> None:
16
+ """Persist one successful provider call for the bound processing attempt."""
17
+ ...
@@ -0,0 +1,20 @@
1
+ """D74 durable intent boundary for portable hard-forget manifests."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+ from uuid import UUID
6
+
7
+ from rememberstack.model import ForgetManifest
8
+
9
+
10
+ @runtime_checkable
11
+ class ForgetManifestPort(Protocol):
12
+ """Append and enumerate immutable intent outside the protected restore set."""
13
+
14
+ def append(self, *, manifest: ForgetManifest) -> None:
15
+ """Durably append one manifest; identical bytes are idempotent."""
16
+ ...
17
+
18
+ def manifests(self, *, deployment_id: UUID) -> tuple[ForgetManifest, ...]:
19
+ """Return every manifest for a deployment in deterministic order."""
20
+ ...
@@ -0,0 +1,20 @@
1
+ """D45/D61 remote interaction used by the single-writer Plane-K driver."""
2
+
3
+ from pathlib import Path
4
+ from typing import Protocol
5
+ from typing import runtime_checkable
6
+
7
+ from rememberstack.model import KRevision
8
+
9
+
10
+ @runtime_checkable
11
+ class KGitRemotePort(Protocol):
12
+ """Checkout and publish driver-owned Plane-K commits through one remote."""
13
+
14
+ def checkout(self, *, destination: Path) -> KRevision:
15
+ """Create the driver's working checkout and return its current revision."""
16
+ ...
17
+
18
+ def publish(self, *, worktree: Path) -> KRevision:
19
+ """Publish the commit prepared by the K driver and return its revision."""
20
+ ...
@@ -0,0 +1,28 @@
1
+ """D52/D61 substrate seam for typed model and embedding provider calls."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+ from typing import TypeVar
6
+
7
+ from rememberstack.model import EmbeddingRequest
8
+ from rememberstack.model import EmbeddingResponse
9
+ from rememberstack.model import GeneratedResponse
10
+ from rememberstack.model import ModelRequest
11
+ from rememberstack.model import StructuredResponseModel
12
+
13
+ ResponseT = TypeVar("ResponseT", bound=StructuredResponseModel)
14
+
15
+
16
+ @runtime_checkable
17
+ class ModelProviderPort(Protocol):
18
+ """Invoke configured models without owning prompts, cascades, or domain logic."""
19
+
20
+ def generate(
21
+ self, *, request: ModelRequest, response_type: type[ResponseT]
22
+ ) -> GeneratedResponse[ResponseT]:
23
+ """Return validated output plus the provider-reported usage for this call."""
24
+ ...
25
+
26
+ def embed(self, *, request: EmbeddingRequest) -> EmbeddingResponse:
27
+ """Return same-dimension embeddings for one caller-declared batch."""
28
+ ...
@@ -0,0 +1,16 @@
1
+ """D51/D61 portable publication boundary for four read-only mount views."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+ from uuid import UUID
6
+
7
+ from rememberstack.model import PublishedMounts
8
+
9
+
10
+ @runtime_checkable
11
+ class MountPublisherPort(Protocol):
12
+ """Publish P3, artifact, raw, and Plane-K views without mount mechanics."""
13
+
14
+ def publish(self, *, deployment_id: UUID) -> PublishedMounts:
15
+ """Publish and return the exact four read-only deployment views."""
16
+ ...
@@ -0,0 +1,27 @@
1
+ """D61 byte/object-key seam for immutable raw inputs, artifacts, and snapshots."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+
6
+ from rememberstack.model import ObjectKey
7
+
8
+
9
+ @runtime_checkable
10
+ class ObjectStorePort(Protocol):
11
+ """Read and create immutable objects without exposing storage-provider types."""
12
+
13
+ def read_bytes(self, *, key: ObjectKey) -> bytes:
14
+ """Read all bytes stored under an existing object key."""
15
+ ...
16
+
17
+ def write_bytes(
18
+ self, *, key: ObjectKey, content: bytes, storage_class: str | None = None
19
+ ) -> None:
20
+ """Create immutable bytes, failing rather than replacing an occupied key.
21
+
22
+ `storage_class` is the D51 mime routing decision made by the caller
23
+ (hot for media a harness reads, cold for originals kept only for
24
+ audit). Providers that have storage classes apply it; providers
25
+ that do not record it, so the routing is observable either way.
26
+ """
27
+ ...
@@ -0,0 +1,92 @@
1
+ """D61 seam for the P1 search indexes: chunks, claims, facts (D8)."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+
6
+ from rememberstack.model import P1ChunkRow
7
+ from rememberstack.model import P1ClaimRow
8
+ from rememberstack.model import P1EntityRow
9
+ from rememberstack.model import P1FactRow
10
+
11
+
12
+ @runtime_checkable
13
+ class ChunkIndexPort(Protocol):
14
+ """Write the P1 chunk table without exposing vector-store types."""
15
+
16
+ def upsert_chunks(self, *, rows: tuple[P1ChunkRow, ...]) -> None:
17
+ """Insert or replace rows by chunk_id; re-runs are idempotent."""
18
+ ...
19
+
20
+ def chunk_vectors(
21
+ self, *, deployment_id: str, chunk_ids: tuple[str, ...]
22
+ ) -> dict[str, tuple[float, ...]]:
23
+ """Stored vectors for the requested ids (absent ids are omitted).
24
+
25
+ The D56 embedding-reuse read: an unchanged chunk in a new version
26
+ copies its predecessor's vector instead of re-embedding.
27
+ """
28
+ ...
29
+
30
+
31
+ @runtime_checkable
32
+ class ClaimIndexPort(Protocol):
33
+ """Write the P1 claims channel — the needle index (D58)."""
34
+
35
+ def upsert_claims(self, *, rows: tuple[P1ClaimRow, ...]) -> None:
36
+ """Insert or replace rows by claim_id; re-runs are idempotent."""
37
+ ...
38
+
39
+
40
+ @runtime_checkable
41
+ class FactIndexPort(Protocol):
42
+ """Write the P1 facts channel — relation/observation labels (D8)."""
43
+
44
+ def upsert_facts(self, *, rows: tuple[P1FactRow, ...]) -> None:
45
+ """Insert or replace rows by fact_id; re-runs are idempotent."""
46
+ ...
47
+
48
+
49
+ @runtime_checkable
50
+ class P1SearchPort(Protocol):
51
+ """Nominate candidates from the P1 indexes (D48: propose, never dispose)."""
52
+
53
+ def search_claims(
54
+ self,
55
+ *,
56
+ deployment_id: str,
57
+ vector: tuple[float, ...],
58
+ k: int,
59
+ current_only: bool,
60
+ ) -> tuple[str, ...]:
61
+ """Ranked claim-id nominations from the claims channel."""
62
+ ...
63
+
64
+ def search_facts(
65
+ self, *, deployment_id: str, vector: tuple[float, ...], k: int, kind: str | None
66
+ ) -> tuple[str, ...]:
67
+ """Ranked fact-id nominations from the facts channel."""
68
+ ...
69
+
70
+
71
+ @runtime_checkable
72
+ class EntityIndexPort(Protocol):
73
+ """The T3 profile-embedding home: entity vectors in P1 (D8/D17)."""
74
+
75
+ def upsert_entities(self, *, rows: tuple[P1EntityRow, ...]) -> None:
76
+ """Insert or replace entity profiles by entity_id; idempotent."""
77
+ ...
78
+
79
+ def entity_vectors(
80
+ self, *, deployment_id: str, entity_ids: tuple[str, ...]
81
+ ) -> dict[str, tuple[float, ...]]:
82
+ """Profile vectors for the requested ids (absent ids are omitted)."""
83
+ ...
84
+
85
+
86
+ @runtime_checkable
87
+ class P1IndexMaintenancePort(Protocol):
88
+ """Explicit post-bulk-load maintenance for the P1 search indexes."""
89
+
90
+ def build_search_indexes(self) -> None:
91
+ """Build or refresh search indexes after a backfill has drained."""
92
+ ...
@@ -0,0 +1,93 @@
1
+ """D74 erasure capabilities of the already-selected serving stores."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+ from uuid import UUID
6
+
7
+ from rememberstack.model import ObjectKey
8
+
9
+
10
+ @runtime_checkable
11
+ class ObjectPurgePort(Protocol):
12
+ """Idempotently erase manifest-nominated immutable objects."""
13
+
14
+ def purge_objects(
15
+ self, *, keys: tuple[ObjectKey, ...], prefixes: tuple[ObjectKey, ...]
16
+ ) -> None:
17
+ """Delete exact keys and every object below exact prefixes; absence succeeds."""
18
+ ...
19
+
20
+ def verify_objects_purged(
21
+ self, *, keys: tuple[ObjectKey, ...], prefixes: tuple[ObjectKey, ...]
22
+ ) -> None:
23
+ """Raise unless every exact key and prefix is absent from the active store."""
24
+ ...
25
+
26
+
27
+ @runtime_checkable
28
+ class P1PurgePort(Protocol):
29
+ """Idempotently erase manifest-nominated P1 rows by identity."""
30
+
31
+ def purge_rows(
32
+ self,
33
+ *,
34
+ deployment_id: UUID,
35
+ chunk_ids: tuple[UUID, ...],
36
+ claim_ids: tuple[UUID, ...],
37
+ fact_ids: tuple[UUID, ...],
38
+ entity_ids: tuple[UUID, ...],
39
+ ) -> None:
40
+ """Delete exact rows and compact affected P1 tables; absence succeeds."""
41
+ ...
42
+
43
+ def verify_rows_purged(
44
+ self,
45
+ *,
46
+ deployment_id: UUID,
47
+ chunk_ids: tuple[UUID, ...],
48
+ claim_ids: tuple[UUID, ...],
49
+ fact_ids: tuple[UUID, ...],
50
+ entity_ids: tuple[UUID, ...],
51
+ ) -> None:
52
+ """Raise unless every nominated row is absent from active P1 tables."""
53
+ ...
54
+
55
+
56
+ @runtime_checkable
57
+ class ProjectionPurgePort(Protocol):
58
+ """Erase old P2/P3 durable prefixes and local serving copies."""
59
+
60
+ def purge_projections(
61
+ self, *, deployment_id: UUID, prefixes: tuple[ObjectKey, ...]
62
+ ) -> None:
63
+ """Delete every nominated projection copy; absence succeeds."""
64
+ ...
65
+
66
+ def verify_projections_purged(
67
+ self, *, deployment_id: UUID, prefixes: tuple[ObjectKey, ...]
68
+ ) -> None:
69
+ """Raise unless durable old prefixes and local serving copies are absent."""
70
+ ...
71
+
72
+
73
+ @runtime_checkable
74
+ class KGitPurgePort(Protocol):
75
+ """Erase affected Plane-K paths from all reachable Git history."""
76
+
77
+ def blocking_redaction_paths(
78
+ self, *, deployment_id: UUID, doc_id: UUID
79
+ ) -> tuple[str, ...]:
80
+ """Return sorted authored-body or curation paths that still cite a lineage."""
81
+ ...
82
+
83
+ def purge_artifacts(
84
+ self, *, deployment_id: UUID, forget_id: UUID, artifact_ids: tuple[UUID, ...]
85
+ ) -> None:
86
+ """Erase affected history and retain only already-sanitized current files."""
87
+ ...
88
+
89
+ def verify_artifacts_purged(
90
+ self, *, deployment_id: UUID, forget_id: UUID, artifact_ids: tuple[UUID, ...]
91
+ ) -> None:
92
+ """Raise unless the store-local receipt and affected-path history are clean."""
93
+ ...
@@ -0,0 +1,23 @@
1
+ """D67 delivery-only announcement seam over committed Postgres work truth."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+ from uuid import UUID
6
+
7
+ from rememberstack.model import QueueRoute
8
+ from rememberstack.model import UTCDateTime
9
+
10
+
11
+ @runtime_checkable
12
+ class TaskQueuePort(Protocol):
13
+ """Announce an existing row using non-authoritative route and due snapshots."""
14
+
15
+ def announce(
16
+ self,
17
+ *,
18
+ processing_id: UUID,
19
+ route_snapshot: QueueRoute,
20
+ not_before_snapshot: UTCDateTime,
21
+ ) -> None:
22
+ """Schedule at-least-once delivery without creating or mutating work state."""
23
+ ...
@@ -0,0 +1,21 @@
1
+ """D61 telemetry seam preserving structured data and real exception objects."""
2
+
3
+ from typing import Protocol
4
+ from typing import runtime_checkable
5
+
6
+ from rememberstack.model import TelemetryEvent
7
+
8
+
9
+ @runtime_checkable
10
+ class TelemetryPort(Protocol):
11
+ """Export telemetry without importing a vendor SDK or hiding exporter failure."""
12
+
13
+ def export_event(self, *, event: TelemetryEvent) -> None:
14
+ """Export one structured event and let exporter failures propagate."""
15
+ ...
16
+
17
+ def export_exception(
18
+ self, *, event: TelemetryEvent, exception: BaseException
19
+ ) -> None:
20
+ """Export the event with the original exception object and cause chain."""
21
+ ...
@@ -0,0 +1,22 @@
1
+ """Explicit composition root package."""
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from rememberstack.profiles.selfhost import SelfHostProfile
7
+ from rememberstack.profiles.selfhost import SelfHostSettings
8
+
9
+ __all__ = ("SelfHostProfile", "SelfHostSettings")
10
+
11
+
12
+ def __getattr__(name: str) -> object:
13
+ """Load a profile only when its explicit composition root is requested."""
14
+ if name == "SelfHostProfile":
15
+ from rememberstack.profiles.selfhost import SelfHostProfile
16
+
17
+ return SelfHostProfile
18
+ if name == "SelfHostSettings":
19
+ from rememberstack.profiles.selfhost import SelfHostSettings
20
+
21
+ return SelfHostSettings
22
+ raise AttributeError(name)