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,400 @@
|
|
|
1
|
+
"""The lifecycle eval pack (WP-3.7, D22/D35): the D54 economy, guarded.
|
|
2
|
+
|
|
3
|
+
Three layers of protection over the currency/count machinery, and ONE gate:
|
|
4
|
+
`run_lifecycle_suite` checks the standing invariants AND re-runs every
|
|
5
|
+
planted canary, folds both into a single verdict, and records it — a green
|
|
6
|
+
lifecycle row in `eval_runs` means the whole pack held.
|
|
7
|
+
|
|
8
|
+
- **Invariants** — properties that must hold on the deployment state: the
|
|
9
|
+
currency cache agrees with its ledger (transactional, always checkable),
|
|
10
|
+
cached counts agree with a recompute, closure records agree with fact
|
|
11
|
+
state, and no fact is both closed and under an open flag. Count and
|
|
12
|
+
closure checks can lag legitimately while reconciliation is mid-flight,
|
|
13
|
+
so they run only when the pipeline is QUIESCENT — a busy deployment
|
|
14
|
+
defers them (visibly) instead of alarming falsely.
|
|
15
|
+
- **The flag-rate metric** — `support_withdrawn` flags per superseding
|
|
16
|
+
extractor generation, the live rollout canary (lifecycle §4). A
|
|
17
|
+
non-extractor basis bump (converter/blockizer/structurer) surfaces under
|
|
18
|
+
the unchanged extractor key — its spike is visible in the absolute
|
|
19
|
+
`flags_raised` jump, and each flag records its full basis coordinates
|
|
20
|
+
for exact attribution; a per-coordinate rate refinement is a D22
|
|
21
|
+
follow-up measurement.
|
|
22
|
+
- **Planted canaries** — every `restore_support` verdict plants a D35
|
|
23
|
+
canary (`spine/review.py`). The guard is the FACT's support, not the
|
|
24
|
+
original claim row: a fixed extractor legitimately re-derives the
|
|
25
|
+
content as a NEW claim (immutability) and the old one flips non-current
|
|
26
|
+
— the canary must pass then, and fail only when the fact's current
|
|
27
|
+
support silently vanishes again.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from uuid import UUID
|
|
31
|
+
from uuid import uuid4
|
|
32
|
+
|
|
33
|
+
from sqlalchemy import bindparam
|
|
34
|
+
from sqlalchemy import JSON
|
|
35
|
+
from sqlalchemy import text
|
|
36
|
+
from sqlalchemy import TextClause
|
|
37
|
+
from sqlalchemy.engine import Connection
|
|
38
|
+
from sqlalchemy.engine import Engine
|
|
39
|
+
|
|
40
|
+
from rememberstack.eval.harness import EvalHarness
|
|
41
|
+
from rememberstack.model import CanaryCase
|
|
42
|
+
from rememberstack.model import EvalSuite
|
|
43
|
+
from rememberstack.model import LifecycleReport
|
|
44
|
+
from rememberstack.spine.lifecycle import CURRENCY_CACHE_MISMATCH_SQL
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def run_lifecycle_suite(
|
|
48
|
+
*, engine: Engine, deployment_id: UUID, component_version: str
|
|
49
|
+
) -> LifecycleReport:
|
|
50
|
+
"""The single lifecycle gate: invariants + planted canaries, one verdict.
|
|
51
|
+
|
|
52
|
+
All invariant reads share one REPEATABLE READ snapshot (no torn view
|
|
53
|
+
across statements). When reconcile/finalization work is in flight, the
|
|
54
|
+
count/closure checks defer to the next quiescent run — recorded on the
|
|
55
|
+
report, never silently skipped. The flag rate is a metric to watch
|
|
56
|
+
(its alarm threshold is an operations decision), never part of the
|
|
57
|
+
verdict.
|
|
58
|
+
"""
|
|
59
|
+
violations: dict[str, tuple[str, ...]] = {}
|
|
60
|
+
with engine.connect().execution_options(
|
|
61
|
+
isolation_level="REPEATABLE READ"
|
|
62
|
+
) as connection:
|
|
63
|
+
quiescent = (
|
|
64
|
+
connection.execute(
|
|
65
|
+
_COUNT_INFLIGHT_RECONCILES, {"deployment_id": deployment_id}
|
|
66
|
+
).scalar_one()
|
|
67
|
+
== 0
|
|
68
|
+
)
|
|
69
|
+
for name, requires_quiescence, statement in _INVARIANTS:
|
|
70
|
+
if requires_quiescence and not quiescent:
|
|
71
|
+
continue # legitimately lagging mid-flight: deferred, visibly
|
|
72
|
+
offenders = tuple(
|
|
73
|
+
str(value)
|
|
74
|
+
for value in connection.execute(
|
|
75
|
+
statement, {"deployment_id": deployment_id}
|
|
76
|
+
).scalars()
|
|
77
|
+
)
|
|
78
|
+
if offenders:
|
|
79
|
+
violations[name] = offenders
|
|
80
|
+
canary_failures = tuple(
|
|
81
|
+
f"{case.canary_id}: {case.description}"
|
|
82
|
+
for case in _load_canaries(
|
|
83
|
+
connection=connection, deployment_id=deployment_id
|
|
84
|
+
)
|
|
85
|
+
if not _canary_holds(connection=connection, case=case)
|
|
86
|
+
)
|
|
87
|
+
flag_rates = flag_rate_by_extractor(engine=engine, deployment_id=deployment_id)
|
|
88
|
+
report = LifecycleReport(
|
|
89
|
+
passed=not violations and not canary_failures,
|
|
90
|
+
quiescent=quiescent,
|
|
91
|
+
violations=violations,
|
|
92
|
+
canary_failures=canary_failures,
|
|
93
|
+
flag_rate_by_extractor=flag_rates,
|
|
94
|
+
)
|
|
95
|
+
with engine.begin() as connection:
|
|
96
|
+
connection.execute(
|
|
97
|
+
_RECORD_RUN,
|
|
98
|
+
{
|
|
99
|
+
"eval_run_id": uuid4(),
|
|
100
|
+
"deployment_id": deployment_id,
|
|
101
|
+
"component_version": component_version,
|
|
102
|
+
"metrics": report.model_dump(mode="json"),
|
|
103
|
+
"passed": report.passed,
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
return report
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def flag_rate_by_extractor(
|
|
110
|
+
*, engine: Engine, deployment_id: UUID
|
|
111
|
+
) -> dict[str, dict[str, float]]:
|
|
112
|
+
"""`support_withdrawn` flags per superseding extractor generation.
|
|
113
|
+
|
|
114
|
+
The rollout canary (lifecycle §4): a spike right after an upgrade means
|
|
115
|
+
the new generation is failing to re-derive what the corpus still says.
|
|
116
|
+
Shape (a starting point, D22): per generation V —
|
|
117
|
+
``flags_raised`` (flags whose diff names V as the superseding
|
|
118
|
+
generation), ``current_claims`` (V's live corpus coverage), and
|
|
119
|
+
``flag_rate = flags / (flags + current_claims)`` — a saturating
|
|
120
|
+
proportion that reads 1.0 when a generation only withdraws and never
|
|
121
|
+
re-derives, and falls toward 0 as its coverage dominates.
|
|
122
|
+
"""
|
|
123
|
+
with engine.connect() as connection:
|
|
124
|
+
coverage = {
|
|
125
|
+
row["extractor_version"]: row["current_claims"]
|
|
126
|
+
for row in connection.execute(
|
|
127
|
+
_COUNT_GENERATION_COVERAGE, {"deployment_id": deployment_id}
|
|
128
|
+
).mappings()
|
|
129
|
+
}
|
|
130
|
+
flags = {
|
|
131
|
+
row["to_version"]: row["flags"]
|
|
132
|
+
for row in connection.execute(
|
|
133
|
+
_COUNT_FLAGS, {"deployment_id": deployment_id}
|
|
134
|
+
).mappings()
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
version: {
|
|
138
|
+
"current_claims": float(coverage.get(version, 0)),
|
|
139
|
+
"flags_raised": float(flags.get(version, 0)),
|
|
140
|
+
"flag_rate": (
|
|
141
|
+
flags.get(version, 0)
|
|
142
|
+
/ (flags.get(version, 0) + coverage.get(version, 0))
|
|
143
|
+
if flags.get(version, 0) or coverage.get(version, 0)
|
|
144
|
+
else 0.0
|
|
145
|
+
),
|
|
146
|
+
}
|
|
147
|
+
for version in sorted({*coverage, *flags})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def register_lifecycle_evaluator(*, harness: EvalHarness, engine: Engine) -> None:
|
|
152
|
+
"""Bind the planted-canary evaluator for standalone harness runs.
|
|
153
|
+
|
|
154
|
+
The same guard `run_lifecycle_suite` applies — kept registered so the
|
|
155
|
+
generic harness surface can re-run the lifecycle canaries alongside
|
|
156
|
+
other suites.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
def _evaluate(case: CanaryCase) -> bool:
|
|
160
|
+
with engine.connect() as connection:
|
|
161
|
+
return _canary_holds(connection=connection, case=case)
|
|
162
|
+
|
|
163
|
+
harness.register_evaluator(suite=EvalSuite.LIFECYCLE, evaluator=_evaluate)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _canary_holds(*, connection: Connection, case: CanaryCase) -> bool:
|
|
167
|
+
"""The planted guard: the fact's CURRENT support has not vanished again.
|
|
168
|
+
|
|
169
|
+
Immutability means a fixed extractor re-derives the content as a new
|
|
170
|
+
claim row and the restored one legitimately flips non-current — so the
|
|
171
|
+
guard checks the fact, not the original claim: at least one
|
|
172
|
+
current-testimony claim still supports it (the restored one or a
|
|
173
|
+
re-derived successor).
|
|
174
|
+
"""
|
|
175
|
+
fact_kind = case.expected.get("fact_kind")
|
|
176
|
+
fact_id = case.expected.get("fact_id")
|
|
177
|
+
if not isinstance(fact_kind, str) or not isinstance(fact_id, str):
|
|
178
|
+
return False # a malformed canary never passes silently
|
|
179
|
+
statement = (
|
|
180
|
+
_RELATION_HAS_CURRENT_SUPPORT
|
|
181
|
+
if fact_kind == "relation"
|
|
182
|
+
else _OBSERVATION_HAS_CURRENT_SUPPORT
|
|
183
|
+
)
|
|
184
|
+
return bool(
|
|
185
|
+
connection.execute(statement, {"fact_id": UUID(fact_id)}).scalar_one_or_none()
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _load_canaries(
|
|
190
|
+
*, connection: Connection, deployment_id: UUID
|
|
191
|
+
) -> tuple[CanaryCase, ...]:
|
|
192
|
+
"""The deployment's planted lifecycle canaries."""
|
|
193
|
+
rows = connection.execute(
|
|
194
|
+
_SELECT_LIFECYCLE_CANARIES, {"deployment_id": deployment_id}
|
|
195
|
+
).mappings()
|
|
196
|
+
return tuple(
|
|
197
|
+
CanaryCase(
|
|
198
|
+
canary_id=row["canary_id"],
|
|
199
|
+
suite=EvalSuite.LIFECYCLE,
|
|
200
|
+
description=row["description"],
|
|
201
|
+
input=row["input"],
|
|
202
|
+
expected=row["expected"],
|
|
203
|
+
)
|
|
204
|
+
for row in rows
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
_INVARIANTS: tuple[tuple[str, bool, TextClause], ...] = (
|
|
209
|
+
(
|
|
210
|
+
# the D33 pattern's contract: the ledger is truth, the flag is cache.
|
|
211
|
+
# Transactional (event + cache flip commit together), so it is
|
|
212
|
+
# checkable at ANY moment. A claim with no events must sit at the
|
|
213
|
+
# schema's initial state (current) — a false flag with an empty
|
|
214
|
+
# ledger is exactly the corruption this exists to catch.
|
|
215
|
+
"currency_cache_matches_ledger",
|
|
216
|
+
False,
|
|
217
|
+
text(CURRENCY_CACHE_MISMATCH_SQL),
|
|
218
|
+
),
|
|
219
|
+
(
|
|
220
|
+
# D54: BOTH cached counts must equal a recompute (quiescent only —
|
|
221
|
+
# a mid-flight reconcile legitimately lags between transactions)
|
|
222
|
+
"relation_counts_match_recompute",
|
|
223
|
+
True,
|
|
224
|
+
text(
|
|
225
|
+
"""
|
|
226
|
+
SELECT r.relation_id
|
|
227
|
+
FROM relations r
|
|
228
|
+
WHERE r.deployment_id = :deployment_id
|
|
229
|
+
AND (r.evidence_count <> (
|
|
230
|
+
SELECT count(DISTINCT e.doc_id)
|
|
231
|
+
FROM relation_evidence e
|
|
232
|
+
JOIN claims cl ON cl.claim_id = e.claim_id
|
|
233
|
+
WHERE e.relation_id = r.relation_id
|
|
234
|
+
AND e.stance = 'supports'
|
|
235
|
+
AND cl.is_current_testimony)
|
|
236
|
+
OR r.contradict_count <> (
|
|
237
|
+
SELECT count(DISTINCT e.doc_id)
|
|
238
|
+
FROM relation_evidence e
|
|
239
|
+
JOIN claims cl ON cl.claim_id = e.claim_id
|
|
240
|
+
WHERE e.relation_id = r.relation_id
|
|
241
|
+
AND e.stance = 'contradicts'
|
|
242
|
+
AND cl.is_current_testimony))
|
|
243
|
+
"""
|
|
244
|
+
),
|
|
245
|
+
),
|
|
246
|
+
(
|
|
247
|
+
"observation_counts_match_recompute",
|
|
248
|
+
True,
|
|
249
|
+
text(
|
|
250
|
+
"""
|
|
251
|
+
SELECT o.observation_id
|
|
252
|
+
FROM observations o
|
|
253
|
+
WHERE o.deployment_id = :deployment_id
|
|
254
|
+
AND (o.evidence_count <> (
|
|
255
|
+
SELECT count(DISTINCT e.doc_id)
|
|
256
|
+
FROM observation_evidence e
|
|
257
|
+
JOIN claims cl ON cl.claim_id = e.claim_id
|
|
258
|
+
WHERE e.observation_id = o.observation_id
|
|
259
|
+
AND e.stance = 'supports'
|
|
260
|
+
AND cl.is_current_testimony)
|
|
261
|
+
OR o.contradict_count <> (
|
|
262
|
+
SELECT count(DISTINCT e.doc_id)
|
|
263
|
+
FROM observation_evidence e
|
|
264
|
+
JOIN claims cl ON cl.claim_id = e.claim_id
|
|
265
|
+
WHERE e.observation_id = o.observation_id
|
|
266
|
+
AND e.stance = 'contradicts'
|
|
267
|
+
AND cl.is_current_testimony))
|
|
268
|
+
"""
|
|
269
|
+
),
|
|
270
|
+
),
|
|
271
|
+
(
|
|
272
|
+
# every mechanical retraction record is real, both shapes: a live
|
|
273
|
+
# retraction adjudication's fact must actually be closed
|
|
274
|
+
"retraction_records_match_closures",
|
|
275
|
+
True,
|
|
276
|
+
text(
|
|
277
|
+
"""
|
|
278
|
+
SELECT a.relation_id
|
|
279
|
+
FROM relation_adjudications a
|
|
280
|
+
JOIN relations r ON r.relation_id = a.relation_id
|
|
281
|
+
WHERE a.deployment_id = :deployment_id
|
|
282
|
+
AND a.outcome = 'retracted_source_removal'
|
|
283
|
+
AND a.superseded_by IS NULL
|
|
284
|
+
AND r.valid_until IS NULL
|
|
285
|
+
AND r.invalidated_at IS NULL
|
|
286
|
+
UNION ALL
|
|
287
|
+
SELECT a.observation_id
|
|
288
|
+
FROM observation_adjudications a
|
|
289
|
+
JOIN observations o ON o.observation_id = a.observation_id
|
|
290
|
+
WHERE a.deployment_id = :deployment_id
|
|
291
|
+
AND a.outcome = 'retracted_source_removal'
|
|
292
|
+
AND a.superseded_by IS NULL
|
|
293
|
+
AND o.invalidated_at IS NULL
|
|
294
|
+
"""
|
|
295
|
+
),
|
|
296
|
+
),
|
|
297
|
+
(
|
|
298
|
+
# the §4 fork must never merge, both shapes: a fact under an open
|
|
299
|
+
# flag is the reviewer's to decide — it can never also have been
|
|
300
|
+
# mechanically retracted or invalidated behind the reviewer's back
|
|
301
|
+
"flagged_facts_never_closed",
|
|
302
|
+
True,
|
|
303
|
+
text(
|
|
304
|
+
"""
|
|
305
|
+
SELECT r.relation_id
|
|
306
|
+
FROM relations r
|
|
307
|
+
JOIN review_queue q
|
|
308
|
+
ON q.candidate ->> 'fact_id' = r.relation_id::text
|
|
309
|
+
WHERE r.deployment_id = :deployment_id
|
|
310
|
+
AND q.item_kind = 'support_withdrawn'
|
|
311
|
+
AND q.status IN ('pending', 'deferred')
|
|
312
|
+
AND (r.invalidated_at IS NOT NULL
|
|
313
|
+
OR EXISTS (
|
|
314
|
+
SELECT 1 FROM relation_adjudications a
|
|
315
|
+
WHERE a.relation_id = r.relation_id
|
|
316
|
+
AND a.outcome = 'retracted_source_removal'
|
|
317
|
+
AND a.superseded_by IS NULL))
|
|
318
|
+
UNION ALL
|
|
319
|
+
SELECT o.observation_id
|
|
320
|
+
FROM observations o
|
|
321
|
+
JOIN review_queue q
|
|
322
|
+
ON q.candidate ->> 'fact_id' = o.observation_id::text
|
|
323
|
+
WHERE o.deployment_id = :deployment_id
|
|
324
|
+
AND q.item_kind = 'support_withdrawn'
|
|
325
|
+
AND q.status IN ('pending', 'deferred')
|
|
326
|
+
AND o.invalidated_at IS NOT NULL
|
|
327
|
+
"""
|
|
328
|
+
),
|
|
329
|
+
),
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
_COUNT_INFLIGHT_RECONCILES = text(
|
|
333
|
+
"""
|
|
334
|
+
SELECT count(*) FROM processing_state
|
|
335
|
+
WHERE deployment_id = :deployment_id
|
|
336
|
+
AND stage = 'reconcile'
|
|
337
|
+
AND status IN ('pending', 'running', 'failed')
|
|
338
|
+
"""
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
_COUNT_GENERATION_COVERAGE = text(
|
|
342
|
+
"""
|
|
343
|
+
SELECT extractor_version, count(*) AS current_claims
|
|
344
|
+
FROM claims
|
|
345
|
+
WHERE deployment_id = :deployment_id AND is_current_testimony
|
|
346
|
+
GROUP BY extractor_version
|
|
347
|
+
"""
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
_COUNT_FLAGS = text(
|
|
351
|
+
"""
|
|
352
|
+
SELECT q.candidate -> 'diff' ->> 'to_extractor_version' AS to_version,
|
|
353
|
+
count(*) AS flags
|
|
354
|
+
FROM review_queue q
|
|
355
|
+
WHERE q.deployment_id = :deployment_id
|
|
356
|
+
AND q.item_kind = 'support_withdrawn'
|
|
357
|
+
GROUP BY q.candidate -> 'diff' ->> 'to_extractor_version'
|
|
358
|
+
"""
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
_RELATION_HAS_CURRENT_SUPPORT = text(
|
|
362
|
+
"""
|
|
363
|
+
SELECT 1 FROM relation_evidence e
|
|
364
|
+
JOIN claims cl ON cl.claim_id = e.claim_id
|
|
365
|
+
WHERE e.relation_id = :fact_id
|
|
366
|
+
AND e.stance = 'supports'
|
|
367
|
+
AND cl.is_current_testimony
|
|
368
|
+
LIMIT 1
|
|
369
|
+
"""
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
_OBSERVATION_HAS_CURRENT_SUPPORT = text(
|
|
373
|
+
"""
|
|
374
|
+
SELECT 1 FROM observation_evidence e
|
|
375
|
+
JOIN claims cl ON cl.claim_id = e.claim_id
|
|
376
|
+
WHERE e.observation_id = :fact_id
|
|
377
|
+
AND e.stance = 'supports'
|
|
378
|
+
AND cl.is_current_testimony
|
|
379
|
+
LIMIT 1
|
|
380
|
+
"""
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
_SELECT_LIFECYCLE_CANARIES = text(
|
|
384
|
+
"""
|
|
385
|
+
SELECT canary_id, description, input, expected
|
|
386
|
+
FROM canary_cases
|
|
387
|
+
WHERE deployment_id = :deployment_id AND suite = 'lifecycle'
|
|
388
|
+
"""
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
_RECORD_RUN = text(
|
|
392
|
+
"""
|
|
393
|
+
INSERT INTO eval_runs (
|
|
394
|
+
eval_run_id, deployment_id, suite, component_version, metrics, passed
|
|
395
|
+
) VALUES (
|
|
396
|
+
:eval_run_id, :deployment_id, 'lifecycle', :component_version,
|
|
397
|
+
:metrics, :passed
|
|
398
|
+
)
|
|
399
|
+
"""
|
|
400
|
+
).bindparams(bindparam("metrics", type_=JSON))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Persistence boundary for the WP-7.2 portable scale report."""
|
|
2
|
+
|
|
3
|
+
from typing import Final
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import bindparam
|
|
8
|
+
from sqlalchemy import JSON
|
|
9
|
+
from sqlalchemy import text
|
|
10
|
+
from sqlalchemy.engine import Engine
|
|
11
|
+
|
|
12
|
+
from rememberstack.model import OperationalScaleReport
|
|
13
|
+
|
|
14
|
+
OPERATIONAL_SCALE_VERSION: Final = "operational-scale-2026.07"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def record_operational_scale_report(
|
|
18
|
+
*,
|
|
19
|
+
engine: Engine,
|
|
20
|
+
deployment_id: UUID,
|
|
21
|
+
report: OperationalScaleReport,
|
|
22
|
+
component_version: str = OPERATIONAL_SCALE_VERSION,
|
|
23
|
+
) -> UUID:
|
|
24
|
+
"""Append one complete provider-neutral scale report to eval history."""
|
|
25
|
+
eval_run_id = uuid4()
|
|
26
|
+
with engine.begin() as connection:
|
|
27
|
+
connection.execute(
|
|
28
|
+
_INSERT_RUN,
|
|
29
|
+
{
|
|
30
|
+
"eval_run_id": eval_run_id,
|
|
31
|
+
"deployment_id": deployment_id,
|
|
32
|
+
"component_version": component_version,
|
|
33
|
+
"metrics": report.model_dump(mode="json"),
|
|
34
|
+
"passed": report.passed,
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
return eval_run_id
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_INSERT_RUN = text(
|
|
41
|
+
"""
|
|
42
|
+
INSERT INTO eval_runs (
|
|
43
|
+
eval_run_id, deployment_id, suite, component_version, metrics, passed
|
|
44
|
+
) VALUES (
|
|
45
|
+
:eval_run_id, :deployment_id, 'operational', :component_version,
|
|
46
|
+
:metrics, :passed
|
|
47
|
+
)
|
|
48
|
+
"""
|
|
49
|
+
).bindparams(bindparam("metrics", type_=JSON))
|