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.
- rememberstack/__init__.py +9 -0
- rememberstack/adapters/__init__.py +42 -0
- rememberstack/adapters/codex_writer.py +221 -0
- rememberstack/adapters/markitdown_converter.py +42 -0
- rememberstack/adapters/openrouter.py +136 -0
- rememberstack/adapters/selfhost/__init__.py +54 -0
- rememberstack/adapters/selfhost/forget.py +66 -0
- rememberstack/adapters/selfhost/git.py +374 -0
- rememberstack/adapters/selfhost/lance.py +328 -0
- rememberstack/adapters/selfhost/minio.py +279 -0
- rememberstack/adapters/selfhost/mounts.py +249 -0
- rememberstack/adapters/selfhost/object_store.py +130 -0
- rememberstack/adapters/selfhost/projection.py +80 -0
- rememberstack/adapters/selfhost/queue.py +137 -0
- rememberstack/adapters/selfhost/telemetry.py +45 -0
- rememberstack/adapters/selfhost/watcher.py +70 -0
- rememberstack/adapters/testing/__init__.py +15 -0
- rememberstack/adapters/testing/cost_meter.py +13 -0
- rememberstack/adapters/testing/model_provider.py +83 -0
- rememberstack/adapters/testing/queue.py +43 -0
- rememberstack/adapters/testing/telemetry.py +22 -0
- rememberstack/client.py +19 -0
- rememberstack/core/__init__.py +127 -0
- rememberstack/core/blockizer.py +189 -0
- rememberstack/core/chunker.py +216 -0
- rememberstack/core/consumption_skill.py +275 -0
- rememberstack/core/conversion.py +76 -0
- rememberstack/core/core_manifest.py +598 -0
- rememberstack/core/extension_packs.py +124 -0
- rememberstack/core/forget.py +17 -0
- rememberstack/core/knowledge_authored.py +276 -0
- rememberstack/core/knowledge_compile.py +215 -0
- rememberstack/core/knowledge_fact_sheet.py +210 -0
- rememberstack/core/knowledge_hashing.py +68 -0
- rememberstack/core/knowledge_planner.py +64 -0
- rememberstack/core/knowledge_writer.py +175 -0
- rememberstack/core/ranking.py +200 -0
- rememberstack/core/recipe_linter.py +149 -0
- rememberstack/core/section_snap.py +209 -0
- rememberstack/core/storage_routing.py +27 -0
- rememberstack/eval/__init__.py +53 -0
- rememberstack/eval/consumption.py +141 -0
- rememberstack/eval/contradiction.py +184 -0
- rememberstack/eval/harness.py +136 -0
- rememberstack/eval/lifecycle.py +400 -0
- rememberstack/eval/operational_scale.py +49 -0
- rememberstack/eval/resolution.py +255 -0
- rememberstack/eval/retrieval_spikes.py +50 -0
- rememberstack/eval/skeleton.py +231 -0
- rememberstack/llm/__init__.py +1 -0
- rememberstack/model/__init__.py +589 -0
- rememberstack/model/adjudication.py +100 -0
- rememberstack/model/auth.py +27 -0
- rememberstack/model/blocks.py +30 -0
- rememberstack/model/chunks.py +190 -0
- rememberstack/model/claims.py +162 -0
- rememberstack/model/client.py +98 -0
- rememberstack/model/clustering.py +54 -0
- rememberstack/model/component_version.py +124 -0
- rememberstack/model/consumption.py +88 -0
- rememberstack/model/conversion.py +31 -0
- rememberstack/model/deployment.py +53 -0
- rememberstack/model/documents.py +168 -0
- rememberstack/model/envelope.py +513 -0
- rememberstack/model/evaluation.py +72 -0
- rememberstack/model/forget.py +143 -0
- rememberstack/model/git.py +13 -0
- rememberstack/model/knowledge.py +840 -0
- rememberstack/model/knowledge_authored.py +325 -0
- rememberstack/model/knowledge_planner.py +431 -0
- rememberstack/model/lifecycle.py +42 -0
- rememberstack/model/model_provider.py +78 -0
- rememberstack/model/mounts.py +24 -0
- rememberstack/model/object_store.py +21 -0
- rememberstack/model/operational_scale.py +59 -0
- rememberstack/model/operations.py +153 -0
- rememberstack/model/processing.py +228 -0
- rememberstack/model/queue.py +73 -0
- rememberstack/model/recipes.py +83 -0
- rememberstack/model/relations.py +79 -0
- rememberstack/model/resolution.py +83 -0
- rememberstack/model/retrieval_spikes.py +62 -0
- rememberstack/model/sections.py +120 -0
- rememberstack/model/telemetry.py +30 -0
- rememberstack/ports/__init__.py +29 -0
- rememberstack/ports/auth.py +16 -0
- rememberstack/ports/connector.py +23 -0
- rememberstack/ports/cost_meter.py +17 -0
- rememberstack/ports/forget.py +20 -0
- rememberstack/ports/git.py +20 -0
- rememberstack/ports/model_provider.py +28 -0
- rememberstack/ports/mounts.py +16 -0
- rememberstack/ports/object_store.py +27 -0
- rememberstack/ports/p1_index.py +92 -0
- rememberstack/ports/purge.py +93 -0
- rememberstack/ports/queue.py +23 -0
- rememberstack/ports/telemetry.py +21 -0
- rememberstack/profiles/__init__.py +22 -0
- rememberstack/profiles/selfhost.py +324 -0
- rememberstack/profiles/selfhost_forget.py +158 -0
- rememberstack/profiles/selfhost_operations.py +95 -0
- rememberstack/py.typed +1 -0
- rememberstack/spine/__init__.py +93 -0
- rememberstack/spine/admission.py +26 -0
- rememberstack/spine/backfill.py +168 -0
- rememberstack/spine/catalog_contract.py +742 -0
- rememberstack/spine/chunk_catalog.py +237 -0
- rememberstack/spine/claim_catalog.py +298 -0
- rememberstack/spine/clustering.py +740 -0
- rememberstack/spine/component_versions.py +208 -0
- rememberstack/spine/consumption.py +81 -0
- rememberstack/spine/deployment_bootstrap.py +445 -0
- rememberstack/spine/document_catalog.py +621 -0
- rememberstack/spine/entity_registry.py +205 -0
- rememberstack/spine/extension_packs.py +220 -0
- rememberstack/spine/fact_catalog.py +571 -0
- rememberstack/spine/forget.py +1753 -0
- rememberstack/spine/knowledge.py +5467 -0
- rememberstack/spine/lifecycle.py +1071 -0
- rememberstack/spine/migrations/__init__.py +1 -0
- rememberstack/spine/migrations/_helpers.py +153 -0
- rememberstack/spine/migrations/env.py +58 -0
- rememberstack/spine/migrations/script.py.mako +27 -0
- rememberstack/spine/migrations/versions/__init__.py +1 -0
- rememberstack/spine/migrations/versions/p0_02_0001_extensions_enums.py +189 -0
- rememberstack/spine/migrations/versions/p0_02_0002_infrastructure_registries.py +321 -0
- rememberstack/spine/migrations/versions/p0_02_0003_entities_evaluation_e0_e1.py +631 -0
- rememberstack/spine/migrations/versions/p0_02_0004_claims_facts_evidence.py +411 -0
- rememberstack/spine/migrations/versions/p0_02_0005_projection_knowledge_retrieval.py +391 -0
- rememberstack/spine/migrations/versions/p0_02_0006_partitions_views.py +158 -0
- rememberstack/spine/migrations/versions/p2_06_0007_invalidated_outcome.py +26 -0
- rememberstack/spine/migrations/versions/p3_01_0008_document_version_target.py +58 -0
- rememberstack/spine/migrations/versions/p3_05_0009_reconcile_stage.py +27 -0
- rememberstack/spine/migrations/versions/p3_07_0010_lifecycle_eval_suite.py +25 -0
- rememberstack/spine/migrations/versions/p4_01_0011_survivor_view_rewrite.py +57 -0
- rememberstack/spine/migrations/versions/p6_02_0012_knowledge_compile_recovery.py +58 -0
- rememberstack/spine/migrations/versions/p6_04_0013_knowledge_writer_ledger.py +46 -0
- rememberstack/spine/migrations/versions/p6_05_0014_knowledge_planner_runtime.py +217 -0
- rememberstack/spine/migrations/versions/p6_06_0015_authored_dispatch_runtime.py +38 -0
- rememberstack/spine/migrations/versions/p7_02_0016_operational_eval_suite.py +19 -0
- rememberstack/spine/migrations/versions/p7_05_0017_hard_forget.py +55 -0
- rememberstack/spine/observation_adjudication.py +778 -0
- rememberstack/spine/operations.py +298 -0
- rememberstack/spine/projection.py +662 -0
- rememberstack/spine/recipes.py +276 -0
- rememberstack/spine/resolver.py +763 -0
- rememberstack/spine/review.py +650 -0
- rememberstack/spine/settings.py +22 -0
- rememberstack/spine/supersession.py +510 -0
- rememberstack/spine/sync.py +128 -0
- rememberstack/spine/work_ledger.py +816 -0
- rememberstack/surfaces/__init__.py +110 -0
- rememberstack/surfaces/cli.py +447 -0
- rememberstack/surfaces/consumption_skill.py +87 -0
- rememberstack/surfaces/graph_queries.py +698 -0
- rememberstack/surfaces/http_api.py +377 -0
- rememberstack/surfaces/mcp.py +67 -0
- rememberstack/surfaces/query_engine.py +1591 -0
- rememberstack/surfaces/recipe_executor.py +185 -0
- rememberstack/surfaces/recipe_surface.py +219 -0
- rememberstack/surfaces/remote_mcp.py +133 -0
- rememberstack/surfaces/sdk.py +324 -0
- rememberstack/workers/__init__.py +155 -0
- rememberstack/workers/base.py +312 -0
- rememberstack/workers/e0.py +577 -0
- rememberstack/workers/e1.py +425 -0
- rememberstack/workers/e2.py +525 -0
- rememberstack/workers/e3.py +434 -0
- rememberstack/workers/forget.py +299 -0
- rememberstack/workers/knowledge_authored.py +146 -0
- rememberstack/workers/knowledge_driver.py +735 -0
- rememberstack/workers/knowledge_fact_sheet.py +123 -0
- rememberstack/workers/knowledge_planner.py +325 -0
- rememberstack/workers/knowledge_writer.py +393 -0
- rememberstack/workers/operations.py +42 -0
- rememberstack/workers/p1.py +234 -0
- rememberstack/workers/p2.py +513 -0
- rememberstack/workers/p2_analytics.py +276 -0
- rememberstack/workers/p3.py +673 -0
- rememberstack/workers/reconcile.py +485 -0
- rememberstack/workers/sync.py +168 -0
- rememberstack-0.1.0.dist-info/METADATA +213 -0
- rememberstack-0.1.0.dist-info/RECORD +186 -0
- rememberstack-0.1.0.dist-info/WHEEL +4 -0
- rememberstack-0.1.0.dist-info/entry_points.txt +2 -0
- rememberstack-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""T0 entity resolution over the alias registry (D17): exact match or mint.
|
|
2
|
+
|
|
3
|
+
T0 is the cheapest cascade tier: an exact match on the normalized
|
|
4
|
+
`llm_canonical` lemma. A miss mints a new canonical entity (the novelty path)
|
|
5
|
+
with its alias, and every resolution — hit or mint — writes the immutable
|
|
6
|
+
mention and an append-only resolution verdict. Higher tiers (T1-T4 blocking
|
|
7
|
+
and adjudication) arrive with the truth-machinery phase.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Final
|
|
11
|
+
import unicodedata
|
|
12
|
+
from uuid import UUID
|
|
13
|
+
from uuid import uuid4
|
|
14
|
+
|
|
15
|
+
from sqlalchemy import bindparam
|
|
16
|
+
from sqlalchemy import JSON
|
|
17
|
+
from sqlalchemy import text
|
|
18
|
+
from sqlalchemy.engine import Engine
|
|
19
|
+
|
|
20
|
+
from rememberstack.model import ClaimForNormalization
|
|
21
|
+
from rememberstack.model import EntityRef
|
|
22
|
+
from rememberstack.model import ResolvedEntity
|
|
23
|
+
|
|
24
|
+
T0_RESOLVER_VERSION: Final = "e3-resolver-t0-2026.07"
|
|
25
|
+
"""The T0-only resolver generation (walking skeleton; cascade tiers follow)."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def normalized_lemma(*, surface: str) -> str:
|
|
29
|
+
"""The registry match key: accent-stripped, lowercased, whitespace-folded."""
|
|
30
|
+
decomposed = unicodedata.normalize("NFKD", surface)
|
|
31
|
+
stripped = "".join(char for char in decomposed if not unicodedata.combining(char))
|
|
32
|
+
return " ".join(stripped.lower().split())
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class EntityRegistry:
|
|
36
|
+
"""Alias-registry resolution and mention/verdict writes for one deployment."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, *, engine: Engine) -> None:
|
|
39
|
+
"""Bind the registry to the spine database."""
|
|
40
|
+
self._engine = engine
|
|
41
|
+
|
|
42
|
+
def resolve_t0(
|
|
43
|
+
self, *, deployment_id: UUID, reference: EntityRef, claim: ClaimForNormalization
|
|
44
|
+
) -> ResolvedEntity:
|
|
45
|
+
"""Resolve one reference by exact lemma match, minting on a miss.
|
|
46
|
+
|
|
47
|
+
One transaction writes the mention, the entity + alias when minted,
|
|
48
|
+
and the append-only T0 verdict (D17: verdicts supersede, never edit).
|
|
49
|
+
"""
|
|
50
|
+
lemma = normalized_lemma(surface=reference.name)
|
|
51
|
+
with self._engine.begin() as connection:
|
|
52
|
+
# serialize concurrent mints of one lemma (Codex review: without
|
|
53
|
+
# this, two workers each mint an entity and facts never collapse):
|
|
54
|
+
connection.execute(_LOCK_LEMMA, {"key": f"{deployment_id}:lemma:{lemma}"})
|
|
55
|
+
existing = (
|
|
56
|
+
connection.execute(
|
|
57
|
+
_SELECT_BY_LEMMA, {"deployment_id": deployment_id, "lemma": lemma}
|
|
58
|
+
)
|
|
59
|
+
.mappings()
|
|
60
|
+
.one_or_none()
|
|
61
|
+
)
|
|
62
|
+
created = existing is None
|
|
63
|
+
entity_id = existing["entity_id"] if existing else uuid4()
|
|
64
|
+
entity_type = existing["type"] if existing else reference.type
|
|
65
|
+
if created:
|
|
66
|
+
connection.execute(
|
|
67
|
+
_INSERT_ENTITY,
|
|
68
|
+
{
|
|
69
|
+
"entity_id": entity_id,
|
|
70
|
+
"deployment_id": deployment_id,
|
|
71
|
+
"type": reference.type,
|
|
72
|
+
"canonical_name": reference.name,
|
|
73
|
+
"normalized_name": lemma,
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
connection.execute(
|
|
77
|
+
_INSERT_ALIAS,
|
|
78
|
+
{
|
|
79
|
+
"alias_id": uuid4(),
|
|
80
|
+
"deployment_id": deployment_id,
|
|
81
|
+
"entity_id": entity_id,
|
|
82
|
+
"alias_text": reference.name,
|
|
83
|
+
"lemma": lemma,
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
mention_id = uuid4()
|
|
87
|
+
connection.execute(
|
|
88
|
+
_INSERT_MENTION,
|
|
89
|
+
{
|
|
90
|
+
"mention_id": mention_id,
|
|
91
|
+
"deployment_id": deployment_id,
|
|
92
|
+
"surface_form": reference.name,
|
|
93
|
+
"lemma": lemma,
|
|
94
|
+
"canonical_name_form": reference.name,
|
|
95
|
+
"emitted_type": reference.type,
|
|
96
|
+
"claim_id": claim.claim_id,
|
|
97
|
+
"chunk_id": claim.chunk_id,
|
|
98
|
+
"doc_id": claim.doc_id,
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
connection.execute(
|
|
102
|
+
_INSERT_DECISION,
|
|
103
|
+
{
|
|
104
|
+
"decision_id": uuid4(),
|
|
105
|
+
"deployment_id": deployment_id,
|
|
106
|
+
"mention_id": mention_id,
|
|
107
|
+
"entity_id": entity_id,
|
|
108
|
+
"is_new_entity": created,
|
|
109
|
+
"features": {"lemma": lemma},
|
|
110
|
+
"resolver_version": T0_RESOLVER_VERSION,
|
|
111
|
+
},
|
|
112
|
+
)
|
|
113
|
+
return ResolvedEntity(
|
|
114
|
+
entity_id=entity_id, created=created, entity_type=entity_type
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def claim_already_normalized(self, *, claim_id: UUID) -> bool:
|
|
118
|
+
"""Whether the claim's facts already landed (evidence-backed replay).
|
|
119
|
+
|
|
120
|
+
The marker is fact evidence, not mentions — a crash between subject
|
|
121
|
+
resolution and the fact write must not strand the claim (Codex
|
|
122
|
+
review). A claim that yielded no facts leaves no marker; its re-run on
|
|
123
|
+
a crash-retry is bounded by the attempt limit and lands idempotently
|
|
124
|
+
(mentions/verdicts are append-only transcript rows either way).
|
|
125
|
+
"""
|
|
126
|
+
return claim_id in self.normalized_claim_ids(claim_ids=(claim_id,))
|
|
127
|
+
|
|
128
|
+
def normalized_claim_ids(self, *, claim_ids: tuple[UUID, ...]) -> frozenset[UUID]:
|
|
129
|
+
"""Return every evidence-backed replay marker in one bounded read."""
|
|
130
|
+
if not claim_ids:
|
|
131
|
+
return frozenset()
|
|
132
|
+
with self._engine.connect() as connection:
|
|
133
|
+
rows = connection.execute(
|
|
134
|
+
_SELECT_NORMALIZED_CLAIMS, {"claim_ids": list(claim_ids)}
|
|
135
|
+
).scalars()
|
|
136
|
+
return frozenset(rows)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
_LOCK_LEMMA = text("SELECT pg_advisory_xact_lock(hashtextextended(:key, 0))")
|
|
140
|
+
|
|
141
|
+
_SELECT_BY_LEMMA = text(
|
|
142
|
+
"""
|
|
143
|
+
SELECT aliases.entity_id, entities.type FROM aliases
|
|
144
|
+
JOIN entities ON entities.deployment_id = aliases.deployment_id
|
|
145
|
+
AND entities.entity_id = aliases.entity_id
|
|
146
|
+
WHERE aliases.deployment_id = :deployment_id
|
|
147
|
+
AND aliases.normalized_lemma = :lemma
|
|
148
|
+
AND entities.status = 'active' -- merged/retired never become endpoints;
|
|
149
|
+
-- redirect-following arrives with merges
|
|
150
|
+
ORDER BY aliases.first_seen
|
|
151
|
+
LIMIT 1
|
|
152
|
+
"""
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
_INSERT_ENTITY = text(
|
|
156
|
+
"""
|
|
157
|
+
INSERT INTO entities (
|
|
158
|
+
entity_id, deployment_id, type, canonical_name, normalized_name
|
|
159
|
+
) VALUES (
|
|
160
|
+
:entity_id, :deployment_id, :type, :canonical_name, :normalized_name
|
|
161
|
+
)
|
|
162
|
+
"""
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
_INSERT_ALIAS = text(
|
|
166
|
+
"""
|
|
167
|
+
INSERT INTO aliases (
|
|
168
|
+
alias_id, deployment_id, entity_id, alias_text, normalized_lemma, provenance
|
|
169
|
+
) VALUES (
|
|
170
|
+
:alias_id, :deployment_id, :entity_id, :alias_text, :lemma, 'llm_canonical'
|
|
171
|
+
)
|
|
172
|
+
"""
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
_INSERT_MENTION = text(
|
|
176
|
+
"""
|
|
177
|
+
INSERT INTO mentions (
|
|
178
|
+
mention_id, deployment_id, surface_form, normalized_lemma,
|
|
179
|
+
canonical_name_form, emitted_type, claim_id, chunk_id, doc_id
|
|
180
|
+
) VALUES (
|
|
181
|
+
:mention_id, :deployment_id, :surface_form, :lemma,
|
|
182
|
+
:canonical_name_form, :emitted_type, :claim_id, :chunk_id, :doc_id
|
|
183
|
+
)
|
|
184
|
+
"""
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
_INSERT_DECISION = text(
|
|
188
|
+
"""
|
|
189
|
+
INSERT INTO resolution_decisions (
|
|
190
|
+
decision_id, deployment_id, mention_id, entity_id, method,
|
|
191
|
+
confidence, is_new_entity, features, resolver_version
|
|
192
|
+
) VALUES (
|
|
193
|
+
:decision_id, :deployment_id, :mention_id, :entity_id, 'T0',
|
|
194
|
+
1.0, :is_new_entity, :features, :resolver_version
|
|
195
|
+
)
|
|
196
|
+
"""
|
|
197
|
+
).bindparams(bindparam("features", type_=JSON))
|
|
198
|
+
|
|
199
|
+
_SELECT_NORMALIZED_CLAIMS = text(
|
|
200
|
+
"""
|
|
201
|
+
SELECT claim_id FROM relation_evidence WHERE claim_id = ANY(:claim_ids)
|
|
202
|
+
UNION
|
|
203
|
+
SELECT claim_id FROM observation_evidence WHERE claim_id = ANY(:claim_ids)
|
|
204
|
+
"""
|
|
205
|
+
)
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""Extension-pack installation (registries §4/§7, D15): enable a pack as a unit.
|
|
2
|
+
|
|
3
|
+
Installing writes the pack's registry rows for one deployment — entity types,
|
|
4
|
+
predicates, and domain/range signatures, all `tier='extension'` with the pack
|
|
5
|
+
id — after verifying every anchor exists (extend-never-fork: an extension
|
|
6
|
+
type MUST declare a registered parent; a pack that forks is refused whole).
|
|
7
|
+
Idempotent: enabling twice is a no-op.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from uuid import UUID
|
|
11
|
+
|
|
12
|
+
from sqlalchemy import text
|
|
13
|
+
from sqlalchemy.engine import Connection
|
|
14
|
+
from sqlalchemy.engine import Engine
|
|
15
|
+
|
|
16
|
+
from rememberstack.core.extension_packs import ExtensionPack
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PackAnchorError(Exception):
|
|
20
|
+
"""A pack type's parent is not registered — extend-never-fork refused it."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PackConflictError(Exception):
|
|
24
|
+
"""A pack name collides with an existing, differently-defined registry row."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def install_pack(*, engine: Engine, deployment_id: UUID, pack: ExtensionPack) -> None:
|
|
28
|
+
"""Enable one pack for one deployment in a single transaction."""
|
|
29
|
+
with engine.begin() as connection:
|
|
30
|
+
_require_anchors(connection=connection, deployment_id=deployment_id, pack=pack)
|
|
31
|
+
_refuse_conflicts(connection=connection, deployment_id=deployment_id, pack=pack)
|
|
32
|
+
connection.execute(
|
|
33
|
+
_UPSERT_PACK,
|
|
34
|
+
{
|
|
35
|
+
"pack_id": pack.pack_id,
|
|
36
|
+
"name": pack.name,
|
|
37
|
+
"description": pack.description,
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
connection.execute(
|
|
41
|
+
_ENABLE_PACK, {"deployment_id": deployment_id, "pack_id": pack.pack_id}
|
|
42
|
+
)
|
|
43
|
+
for entity_type in pack.entity_types:
|
|
44
|
+
connection.execute(
|
|
45
|
+
_INSERT_TYPE,
|
|
46
|
+
{
|
|
47
|
+
"deployment_id": deployment_id,
|
|
48
|
+
"type": entity_type.type,
|
|
49
|
+
"parent_type": entity_type.parent_type,
|
|
50
|
+
"description": entity_type.description,
|
|
51
|
+
"examples": list(entity_type.examples),
|
|
52
|
+
"pack_id": pack.pack_id,
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
for predicate in pack.predicates:
|
|
56
|
+
connection.execute(
|
|
57
|
+
_INSERT_PREDICATE,
|
|
58
|
+
{
|
|
59
|
+
"deployment_id": deployment_id,
|
|
60
|
+
"predicate": predicate.predicate,
|
|
61
|
+
"description": predicate.description,
|
|
62
|
+
"synonyms": list(predicate.synonyms),
|
|
63
|
+
"is_change_prone": predicate.is_change_prone,
|
|
64
|
+
"pack_id": pack.pack_id,
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
for subject_type, object_type in predicate.signatures:
|
|
68
|
+
connection.execute(
|
|
69
|
+
_INSERT_SIGNATURE,
|
|
70
|
+
{
|
|
71
|
+
"deployment_id": deployment_id,
|
|
72
|
+
"predicate": predicate.predicate,
|
|
73
|
+
"subject_type": subject_type,
|
|
74
|
+
"object_type": object_type,
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _require_anchors(
|
|
80
|
+
*, connection: Connection, deployment_id: UUID, pack: ExtensionPack
|
|
81
|
+
) -> None:
|
|
82
|
+
"""Refuse the whole pack if any anchor is not ALREADY registered (D15).
|
|
83
|
+
|
|
84
|
+
Extend-never-fork means every pack type declares a parent that exists in
|
|
85
|
+
the deployment registry BEFORE the pack installs — a pack-local chain
|
|
86
|
+
(Child anchored to another type from the same pack) is refused too
|
|
87
|
+
(Codex review): the DDL's contract is a registered core-side parent.
|
|
88
|
+
Signatures may reference pack types (they install together).
|
|
89
|
+
"""
|
|
90
|
+
pack_types = {entity_type.type for entity_type in pack.entity_types}
|
|
91
|
+
registered = {
|
|
92
|
+
row[0]
|
|
93
|
+
for row in connection.execute(_SELECT_TYPES, {"deployment_id": deployment_id})
|
|
94
|
+
}
|
|
95
|
+
for entity_type in pack.entity_types:
|
|
96
|
+
if entity_type.parent_type not in registered:
|
|
97
|
+
raise PackAnchorError(
|
|
98
|
+
f"pack {pack.pack_id!r} type {entity_type.type!r} anchors to "
|
|
99
|
+
f"{entity_type.parent_type!r}, which is not already registered "
|
|
100
|
+
"(extend-never-fork, D15: pack-local anchors are refused)"
|
|
101
|
+
)
|
|
102
|
+
signature_types = {
|
|
103
|
+
entity_type
|
|
104
|
+
for predicate in pack.predicates
|
|
105
|
+
for pair in predicate.signatures
|
|
106
|
+
for entity_type in pair
|
|
107
|
+
}
|
|
108
|
+
unknown = signature_types - (registered | pack_types)
|
|
109
|
+
if unknown:
|
|
110
|
+
raise PackAnchorError(
|
|
111
|
+
f"pack {pack.pack_id!r} signatures reference unregistered "
|
|
112
|
+
f"types {sorted(unknown)!r}"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _refuse_conflicts(
|
|
117
|
+
*, connection: Connection, deployment_id: UUID, pack: ExtensionPack
|
|
118
|
+
) -> None:
|
|
119
|
+
"""Unit installation (Codex review): an existing row under a pack name
|
|
120
|
+
must BE this pack's row (same pack_id and parent) — anything else fails
|
|
121
|
+
the whole install rather than silently blending registries."""
|
|
122
|
+
type_rows = {
|
|
123
|
+
row[0]: (row[1], row[2])
|
|
124
|
+
for row in connection.execute(
|
|
125
|
+
_SELECT_TYPE_DEFS, {"deployment_id": deployment_id}
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
for entity_type in pack.entity_types:
|
|
129
|
+
existing = type_rows.get(entity_type.type)
|
|
130
|
+
if existing is not None and existing != (entity_type.parent_type, pack.pack_id):
|
|
131
|
+
raise PackConflictError(
|
|
132
|
+
f"type {entity_type.type!r} already registered with "
|
|
133
|
+
f"(parent, pack) = {existing!r}; pack {pack.pack_id!r} "
|
|
134
|
+
"cannot install over it"
|
|
135
|
+
)
|
|
136
|
+
predicate_rows = {
|
|
137
|
+
row[0]: row[1]
|
|
138
|
+
for row in connection.execute(
|
|
139
|
+
_SELECT_PREDICATE_DEFS, {"deployment_id": deployment_id}
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
for predicate in pack.predicates:
|
|
143
|
+
existing_pack = predicate_rows.get(predicate.predicate)
|
|
144
|
+
if existing_pack is not None and existing_pack != pack.pack_id:
|
|
145
|
+
raise PackConflictError(
|
|
146
|
+
f"predicate {predicate.predicate!r} already registered by "
|
|
147
|
+
f"pack {existing_pack!r}; pack {pack.pack_id!r} cannot "
|
|
148
|
+
"install over it"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
_SELECT_TYPES = text(
|
|
153
|
+
"SELECT type FROM entity_types WHERE deployment_id = :deployment_id"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
_UPSERT_PACK = text(
|
|
157
|
+
"""
|
|
158
|
+
INSERT INTO extension_packs (pack_id, name, description)
|
|
159
|
+
VALUES (:pack_id, :name, :description)
|
|
160
|
+
ON CONFLICT (pack_id) DO NOTHING
|
|
161
|
+
"""
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
_ENABLE_PACK = text(
|
|
165
|
+
"""
|
|
166
|
+
INSERT INTO deployment_extension_packs (deployment_id, pack_id)
|
|
167
|
+
VALUES (:deployment_id, :pack_id)
|
|
168
|
+
ON CONFLICT (deployment_id, pack_id) DO NOTHING
|
|
169
|
+
"""
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
_INSERT_TYPE = text(
|
|
173
|
+
"""
|
|
174
|
+
INSERT INTO entity_types (
|
|
175
|
+
deployment_id, type, parent_type, description, examples, tier, pack_id
|
|
176
|
+
) VALUES (
|
|
177
|
+
:deployment_id, :type, :parent_type, :description, :examples,
|
|
178
|
+
'extension', :pack_id
|
|
179
|
+
)
|
|
180
|
+
ON CONFLICT (deployment_id, type) DO NOTHING
|
|
181
|
+
"""
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
_INSERT_PREDICATE = text(
|
|
185
|
+
"""
|
|
186
|
+
INSERT INTO predicates (
|
|
187
|
+
deployment_id, predicate, parent_predicate, description, synonyms,
|
|
188
|
+
is_change_prone, tier, pack_id
|
|
189
|
+
) VALUES (
|
|
190
|
+
:deployment_id, :predicate, 'related_to', :description, :synonyms,
|
|
191
|
+
:is_change_prone, 'extension', :pack_id
|
|
192
|
+
)
|
|
193
|
+
ON CONFLICT (deployment_id, predicate) DO NOTHING
|
|
194
|
+
"""
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
_INSERT_SIGNATURE = text(
|
|
198
|
+
"""
|
|
199
|
+
INSERT INTO predicate_signatures (
|
|
200
|
+
deployment_id, predicate, subject_type, object_type
|
|
201
|
+
) VALUES (
|
|
202
|
+
:deployment_id, :predicate, :subject_type, :object_type
|
|
203
|
+
)
|
|
204
|
+
ON CONFLICT (deployment_id, predicate, subject_type, object_type) DO NOTHING
|
|
205
|
+
"""
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
_SELECT_TYPE_DEFS = text(
|
|
209
|
+
"""
|
|
210
|
+
SELECT type, parent_type, pack_id FROM entity_types
|
|
211
|
+
WHERE deployment_id = :deployment_id
|
|
212
|
+
"""
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
_SELECT_PREDICATE_DEFS = text(
|
|
216
|
+
"""
|
|
217
|
+
SELECT predicate, pack_id FROM predicates
|
|
218
|
+
WHERE deployment_id = :deployment_id
|
|
219
|
+
"""
|
|
220
|
+
)
|