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,88 @@
1
+ """Typed values for the deployment-rendered D51 consumption skill."""
2
+
3
+ from typing import Annotated
4
+ from typing import Literal
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.envelope import Grain
12
+ from rememberstack.model.mounts import PublishedMounts
13
+ from rememberstack.model.recipes import RecipeAnswerIntent
14
+
15
+ _NonEmptyText = Annotated[str, Field(min_length=1)]
16
+
17
+
18
+ class ConsumptionScope(BaseModel):
19
+ """One deployment scope the rendered skill can name to a cold agent."""
20
+
21
+ model_config = ConfigDict(frozen=True, extra="forbid")
22
+
23
+ slug: _NonEmptyText
24
+ name: _NonEmptyText
25
+ description: str | None = None
26
+ git_path: str | None = None
27
+
28
+
29
+ class ConsumptionRecipe(BaseModel):
30
+ """One latest active recipe advertised by the rendered skill."""
31
+
32
+ model_config = ConfigDict(frozen=True, extra="forbid")
33
+
34
+ name: _NonEmptyText
35
+ description: _NonEmptyText
36
+ output_grain: Grain
37
+ answer_intent: RecipeAnswerIntent
38
+
39
+
40
+ class ConsumptionDeployment(BaseModel):
41
+ """Deployment-owned data that varies between rendered skill revisions."""
42
+
43
+ model_config = ConfigDict(frozen=True, extra="forbid")
44
+
45
+ deployment_id: UUID
46
+ slug: _NonEmptyText
47
+ name: _NonEmptyText
48
+ description: str | None = None
49
+ default_language: _NonEmptyText
50
+ scopes: tuple[ConsumptionScope, ...] = ()
51
+ knowledge_page_count: int = Field(ge=0)
52
+
53
+
54
+ class ConsumptionSkillContext(BaseModel):
55
+ """Complete typed input to the pure consumption-skill renderer."""
56
+
57
+ model_config = ConfigDict(frozen=True, extra="forbid")
58
+
59
+ deployment: ConsumptionDeployment
60
+ recipes: tuple[ConsumptionRecipe, ...]
61
+ mounts: PublishedMounts | None = None
62
+
63
+
64
+ class RenderedConsumptionSkill(BaseModel):
65
+ """One versioned, deployment-specific ``SKILL.md`` artifact."""
66
+
67
+ model_config = ConfigDict(frozen=True, extra="forbid")
68
+
69
+ deployment_id: UUID
70
+ version: _NonEmptyText
71
+ filename: Literal["SKILL.md"] = "SKILL.md"
72
+ content: _NonEmptyText
73
+
74
+
75
+ class S58Answer(BaseModel):
76
+ """A cold harness's structured choices for the S58 consumption protocol."""
77
+
78
+ model_config = ConfigDict(frozen=True, extra="forbid")
79
+
80
+ orientation: Literal["knowledge", "facts", "claims_as_of"]
81
+ empty_knowledge: Literal["fallback_p3_or_search", "stop", "invent_summary"]
82
+ current_truth: Literal["fact_lookup", "claim_search", "claims_as_of"]
83
+ grain_handling: Literal["separate", "blend", "drop_evidence_label"]
84
+ withdrawn_support: Literal["caveat_and_transcript", "trust", "discard"]
85
+ claims_as_of: Literal["assertion_history_only", "current_truth", "compiled_truth"]
86
+ contradictions: Literal["report_co_members", "pick_one", "ignore"]
87
+ readable_content: Literal["prefer_mounts", "prefer_api", "prefer_raw"]
88
+ audit: Literal["hydrate_to_sources", "read_k_only", "use_claims_as_fact"]
@@ -0,0 +1,31 @@
1
+ """D38 conversion-module values: converter output and its typed failures."""
2
+
3
+ from pydantic import BaseModel
4
+ from pydantic import ConfigDict
5
+
6
+
7
+ class ConversionResult(BaseModel):
8
+ """What a converter route produced from one raw input.
9
+
10
+ `document_md` is the clean Markdown rendering — the immutable coordinate
11
+ system everything downstream references by offset (D57). Source maps and
12
+ derived assets (media routes, D65) extend this model when their routes
13
+ arrive; every converter can always deliver the Markdown itself.
14
+ """
15
+
16
+ model_config = ConfigDict(frozen=True, extra="forbid")
17
+
18
+ document_md: str
19
+ warnings: tuple[str, ...] = ()
20
+
21
+
22
+ class ConversionError(Exception):
23
+ """A converter could not produce Markdown from the input bytes.
24
+
25
+ Deterministic for given bytes, so retrying cannot help — handlers treat
26
+ this as non-retryable and dead-letter the work with the cause chained.
27
+ """
28
+
29
+
30
+ class UnroutableMimeError(Exception):
31
+ """No configured conversion route accepts the input's MIME type (D38)."""
@@ -0,0 +1,53 @@
1
+ """Typed values and conflicts for deployment bootstrap."""
2
+
3
+ from typing import Annotated
4
+ from typing import TypeAlias
5
+ from uuid import UUID
6
+
7
+ from pydantic import BaseModel
8
+ from pydantic import ConfigDict
9
+ from pydantic import StringConstraints
10
+
11
+ NonEmptyStrictString: TypeAlias = Annotated[
12
+ str, StringConstraints(strict=True, min_length=1)
13
+ ]
14
+
15
+
16
+ class DeploymentBootstrapInput(BaseModel):
17
+ """Explicit profile-owned values required to bootstrap one deployment."""
18
+
19
+ model_config = ConfigDict(frozen=True, extra="forbid")
20
+
21
+ deployment_id: UUID
22
+ slug: NonEmptyStrictString
23
+ name: NonEmptyStrictString
24
+ description: str | None = None
25
+ default_language: NonEmptyStrictString
26
+ raw_bucket: NonEmptyStrictString
27
+ artifacts_bucket: NonEmptyStrictString
28
+ corpusfs_bucket: NonEmptyStrictString
29
+ knowledge_repo_uri: str | None = None
30
+
31
+
32
+ class DeploymentBootstrapResult(BaseModel):
33
+ """Verified deployment and universal-core counts after bootstrap."""
34
+
35
+ model_config = ConfigDict(frozen=True, extra="forbid")
36
+
37
+ deployment_id: UUID
38
+ deployment_created: bool
39
+ entity_types_count: int
40
+ predicates_count: int
41
+ predicate_signatures_count: int
42
+
43
+
44
+ class DeploymentBootstrapConflictError(RuntimeError):
45
+ """Base class for a known bootstrap state conflict."""
46
+
47
+
48
+ class DeploymentConflictError(DeploymentBootstrapConflictError):
49
+ """A deployment identity or mapped profile value conflicts with input."""
50
+
51
+
52
+ class CoreManifestConflictError(DeploymentBootstrapConflictError):
53
+ """Stored core registry state conflicts with the immutable manifest."""
@@ -0,0 +1,168 @@
1
+ """E0 document-layer values: uploads, ledger records, and stage-load shapes (D36/D37).
2
+
3
+ Object URIs in these models are provider-neutral object-store *keys*; which
4
+ bucket a key resolves in (raw vs artifacts) is deployment configuration, and
5
+ the composing profile binds one `ObjectStorePort` per bucket.
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
+ from rememberstack.model.queue import UTCDateTime
16
+
17
+ NonEmptyString = Annotated[str, Field(min_length=1)]
18
+
19
+
20
+ class DocumentUpload(BaseModel):
21
+ """One file handed to the upload connector: bytes plus what the caller knows."""
22
+
23
+ model_config = ConfigDict(frozen=True, extra="forbid")
24
+
25
+ filename: NonEmptyString
26
+ mime: NonEmptyString
27
+ content: bytes
28
+ title: str | None = None
29
+
30
+
31
+ class IngestedVersion(BaseModel):
32
+ """What one ingest did: the lineage/version it landed on, and whether it was new.
33
+
34
+ `created=False` is the D55 content-hash no-op: identical bytes re-ingested
35
+ never create a second version or re-run the chain.
36
+ """
37
+
38
+ model_config = ConfigDict(frozen=True, extra="forbid")
39
+
40
+ deployment_id: UUID
41
+ doc_id: UUID
42
+ version_id: UUID
43
+ content_hash: str
44
+ created: bool
45
+
46
+
47
+ class UploadRecord(BaseModel):
48
+ """The complete row-write input for recording one upload in the spine."""
49
+
50
+ model_config = ConfigDict(frozen=True, extra="forbid")
51
+
52
+ deployment_id: UUID
53
+ doc_id: UUID
54
+ source_kind: NonEmptyString
55
+ source_ref: NonEmptyString
56
+ source_uri: str | None
57
+ title: str | None
58
+ content_hash: NonEmptyString
59
+ mime: NonEmptyString
60
+ byte_size: int = Field(ge=0)
61
+ raw_uri: NonEmptyString
62
+ versioning_mode: str = "snapshot" # snapshot (fail-safe) | living (D55)
63
+ source_modified_at: UTCDateTime | None = None
64
+ source_version_ref: str | None = None
65
+ sync_cycle_id: UUID | None = None
66
+
67
+
68
+ class ConvertSource(BaseModel):
69
+ """Everything the convert stage loads about its claimed document version."""
70
+
71
+ model_config = ConfigDict(frozen=True, extra="forbid")
72
+
73
+ deployment_id: UUID
74
+ doc_id: UUID
75
+ version_id: UUID
76
+ content_hash: str
77
+ mime: str
78
+ raw_uri: str
79
+ title: str | None
80
+
81
+
82
+ class RepresentationRecord(BaseModel):
83
+ """One conversion run's immutable output row (D65): the reading of a version."""
84
+
85
+ model_config = ConfigDict(frozen=True, extra="forbid")
86
+
87
+ representation_id: UUID
88
+ deployment_id: UUID
89
+ version_id: UUID
90
+ route: NonEmptyString
91
+ converter_name: NonEmptyString
92
+ converter_version: NonEmptyString
93
+ blockizer_version: NonEmptyString
94
+ markdown_uri: NonEmptyString
95
+ blocks_uri: NonEmptyString
96
+ conversion_uri: NonEmptyString
97
+ meta_uri: NonEmptyString
98
+ markdown_hash: NonEmptyString
99
+ manifest_hash: NonEmptyString
100
+
101
+
102
+ class StructureSource(BaseModel):
103
+ """Everything the structure stage loads about its claimed representation."""
104
+
105
+ model_config = ConfigDict(frozen=True, extra="forbid")
106
+
107
+ deployment_id: UUID
108
+ doc_id: UUID
109
+ version_id: UUID
110
+ representation_id: UUID
111
+ blocks_uri: str
112
+ markdown_uri: str
113
+ title: str | None
114
+
115
+
116
+ class SyntheticRootRecord(BaseModel):
117
+ """The single full-document root section every document gets (D39).
118
+
119
+ The root spans the whole block grid and character range of `document.md`;
120
+ an empty document still gets the row (zero-width span) so E1/E2/P3 always
121
+ have a path and role to read.
122
+ """
123
+
124
+ model_config = ConfigDict(frozen=True, extra="forbid")
125
+
126
+ deployment_id: UUID
127
+ doc_id: UUID
128
+ version_id: UUID
129
+ representation_id: UUID
130
+ block_count: int = Field(ge=0)
131
+ markdown_chars: int = Field(ge=0)
132
+ title: str | None
133
+ structurer_version: NonEmptyString
134
+
135
+
136
+ class DocumentVersionNotFoundError(Exception):
137
+ """A stage referenced a document version the spine does not know."""
138
+
139
+
140
+ class RepresentationNotFoundError(Exception):
141
+ """A stage referenced a document representation the spine does not know."""
142
+
143
+
144
+ class SourceItem(BaseModel):
145
+ """One observation a watched source reports in a poll (lifecycle §2)."""
146
+
147
+ model_config = ConfigDict(frozen=True, extra="forbid")
148
+
149
+ source_ref: NonEmptyString # connector-native stable id (path, file id)
150
+ revision: NonEmptyString # revision/etag; unchanged revision = no fetch
151
+ modified_at: UTCDateTime
152
+ deleted: bool = False
153
+ filename: str = ""
154
+ mime: str = "text/markdown"
155
+
156
+
157
+ class SyncCycleSummary(BaseModel):
158
+ """What one recorded sync cycle did (connector_sync_cycles, D55/F8)."""
159
+
160
+ model_config = ConfigDict(frozen=True, extra="forbid")
161
+
162
+ cycle_id: UUID
163
+ observed: int
164
+ ingested: tuple[UUID, ...] = () # version ids created this cycle
165
+ unchanged: int = 0
166
+ debounced: int = 0
167
+ deletions_observed: tuple[UUID, ...] = () # lineage ids tombstoned
168
+ failed: int = 0 # items lost to per-item errors; the cycle is lossy