fix(evidence): resolve final review findings

This commit is contained in:
2026-08-25 03:03:11 +02:00
parent d4818c8cc3
commit e9c65ef2db
10 changed files with 686 additions and 20 deletions
@@ -1,6 +1,10 @@
"""Migration boundary: legacy formulas become curated evidence or session proposals."""
import hashlib
from tht.evidence import formula_store
from tht.evidence.authoring import normalize_source_text
from tht.evidence.canonical import CuratedEvidence
from tht.evidence.formula_store import ConceptFormula
@@ -13,17 +17,124 @@ def test_reviewed_formula_migration_has_deterministic_provenance_hash():
sources=["Regola clinica approvata dal gruppo pediatrico."],
)
original_source = """---
concept: fascia pediatrica
columns: [clinical.patient.birth_date]
status: reviewed
sources:
- Regola clinica approvata dal gruppo pediatrico.
---
CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END
"""
first = formula_store.legacy_formula_to_curated(
formula, legacy_path="formulas/fascia-pediatrica-1.sql.md",
formula,
legacy_path="formulas/fascia-pediatrica-1.sql.md",
source_content=original_source,
)
second = formula_store.legacy_formula_to_curated(
formula,
legacy_path="formulas/fascia-pediatrica-1.sql.md",
source_content=original_source,
)
assert isinstance(first, CuratedEvidence)
assert isinstance(second, CuratedEvidence)
assert first.provenance.source_sha256 == second.provenance.source_sha256
expected = hashlib.sha256(normalize_source_text(original_source).encode("utf-8")).hexdigest()
assert first.provenance.source_sha256 == f"sha256:{expected}"
def test_reviewed_formula_without_original_source_fails_closed():
formula = ConceptFormula(
concept="fascia pediatrica",
columns=["clinical.patient.birth_date"],
sql="CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
status="reviewed",
sources=["Regola clinica approvata dal gruppo pediatrico."],
)
outcome = formula_store.legacy_formula_to_curated(
formula, legacy_path="formulas/fascia-pediatrica-1.sql.md",
)
assert first is not None
assert second is not None
assert first.provenance.source_sha256 == second.provenance.source_sha256
assert first.provenance.source_sha256.startswith("sha256:")
assert outcome.code == "legacy_formula_requires_manual_review"
assert outcome.problems == ("original_source_required",)
def test_reviewed_formula_without_verified_supporting_excerpts_fails_closed():
formula = ConceptFormula(
concept="fascia pediatrica",
columns=["clinical.patient.birth_date"],
sql="CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
status="reviewed",
sources=["Nota non presente nel sorgente originale."],
)
original_source = """---
concept: fascia pediatrica
columns: [clinical.patient.birth_date]
status: reviewed
sources:
- >-
Nota non presente nel
sorgente originale.
---
CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END
"""
outcome = formula_store.legacy_formula_to_curated(
formula,
legacy_path="formulas/fascia-pediatrica-1.sql.md",
source_content=original_source,
)
assert outcome.code == "legacy_formula_requires_manual_review"
assert outcome.problems == ("supporting_excerpt_unverified",)
def test_reviewed_formula_without_provenance_notes_fails_closed():
formula = ConceptFormula(
concept="fascia pediatrica",
columns=["clinical.patient.birth_date"],
sql="CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
status="reviewed",
sources=[],
)
outcome = formula_store.legacy_formula_to_curated(
formula,
legacy_path="formulas/fascia-pediatrica-1.sql.md",
source_content=formula.dump(),
)
assert outcome.code == "legacy_formula_requires_manual_review"
assert outcome.problems == ("supporting_excerpts_required",)
def test_reviewed_formula_must_match_the_original_source_record():
formula = ConceptFormula(
concept="fascia pediatrica",
columns=["clinical.patient.birth_date"],
sql="CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
status="reviewed",
sources=["Regola clinica approvata dal gruppo pediatrico."],
)
different_source = ConceptFormula(
concept=formula.concept,
columns=formula.columns,
sql="CASE WHEN age < 16 THEN 'pediatrica' ELSE 'adulta' END",
status=formula.status,
sources=formula.sources,
).dump()
outcome = formula_store.legacy_formula_to_curated(
formula,
legacy_path="formulas/fascia-pediatrica-1.sql.md",
source_content=different_source,
)
assert outcome.code == "legacy_formula_requires_manual_review"
assert outcome.problems == ("original_source_mismatch",)
def test_incompatible_reviewed_legacy_formula_fails_closed_with_the_original_record():
@@ -38,3 +38,22 @@ def test_owner_gate_manual_records_the_narrow_inventory_and_qdrant_baseline_prot
assert "with_payload:false" in text
assert "with_payload:true" not in text
assert "must be repeated through the successful `vector inspect` command" in normalized
def test_owner_gate_current_summaries_supersede_stale_pre_inspection_history():
root = Path(__file__).parents[2]
report = (
root / ".superpowers" / "sdd" / "2026-08-24-evidence-restructuring"
/ "task-12-report.md"
).read_text(encoding="utf-8")
state = (root / "PROJECT_STATE.md").read_text(encoding="utf-8")
current_report = report.split("## Historical record", 1)[0]
current_state = state.split("## Modular workflow refactor candidate", 1)[0]
assert "225 passed, 1 warning" in current_report
assert "230 passed, 1 warning" in current_report
assert "authorized read-only PSD" in current_report
assert "35 moveable source documents" in current_report
assert "**225 passed, 1 known pytest deprecation warning**" in current_state
assert "**230 passed, 1 known pytest deprecation warning**" in current_state
+14 -4
View File
@@ -100,7 +100,9 @@ def test_reviewed_legacy_formula_becomes_curated_formula_with_stable_provenance(
)
migrated = formula_store.legacy_formula_to_curated(
formula, legacy_path="formulas/fascia-pediatrica-1.sql.md",
formula,
legacy_path="formulas/fascia-pediatrica-1.sql.md",
source_content=formula.dump(),
)
assert migrated is not None
@@ -114,7 +116,9 @@ def test_reviewed_legacy_formula_becomes_curated_formula_with_stable_provenance(
assert migrated.provenance.supporting_excerpts == tuple(formula.sources)
assert migrated.review_items == ()
assert formula_store.legacy_formula_to_curated(
formula, legacy_path="formulas/fascia-pediatrica-1.sql.md",
formula,
legacy_path="formulas/fascia-pediatrica-1.sql.md",
source_content=formula.dump(),
).id == migrated.id
@@ -124,19 +128,25 @@ def test_reviewed_legacy_formulas_with_the_same_concept_keep_distinct_path_ident
columns=["clinical.patient.birth_date"],
sql="CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
status="reviewed",
sources=["Regola legacy revisionata."],
)
second = ConceptFormula(
concept="fascia pediatrica",
columns=["clinical.patient.birth_date"],
sql="CASE WHEN age < 16 THEN 'pediatrica' ELSE 'adulta' END",
status="reviewed",
sources=["Regola legacy revisionata."],
)
first_migration = formula_store.legacy_formula_to_curated(
first, legacy_path="formulas/fascia-pediatrica-1.sql.md",
first,
legacy_path="formulas/fascia-pediatrica-1.sql.md",
source_content=first.dump(),
)
second_migration = formula_store.legacy_formula_to_curated(
second, legacy_path="formulas/fascia-pediatrica-2.sql.md",
second,
legacy_path="formulas/fascia-pediatrica-2.sql.md",
source_content=second.dump(),
)
assert first_migration is not None
+25 -1
View File
@@ -315,11 +315,35 @@ def test_run_from_config_uses_runtime_identity_workspace_id(monkeypatch, tmp_pat
command.run_from_config(config)
assert calls["init"]["sparse_language"] == "english"
assert callable(calls["init"]["candidate_evaluator"])
assert calls["init"]["candidate_evaluator"] is None
assert calls["run_as_job"]["workspace_id"] == "psd-clinical"
assert calls["run_as_job"]["input_fingerprint"] != calls["run_as_job"]["config_fingerprint"]
@pytest.mark.parametrize("schema_version, source_type", [
(1, "filesystem"),
(1, "http"),
(1, "s3"),
(2, "http"),
(2, "s3"),
])
def test_candidate_evaluation_is_not_required_outside_v2_filesystem_corpora(
schema_version, source_type,
):
import tht.cli.preprocess_cmd as command
cfg = SimpleNamespace(
evidence=SimpleNamespace(
schema_version=schema_version,
source_root=None,
sources=[SimpleNamespace(type=source_type)],
),
language="it",
)
assert command._candidate_evaluator(cfg, vector_store=object(), embedder=object()) is None
def test_preprocess_evidence_gc_json_is_pristine(monkeypatch, tmp_path):
import tht.cli.preprocess_cmd as command
+12 -3
View File
@@ -45,8 +45,19 @@ def _evaluation_workspace_root(cfg) -> Path:
return root
def _requires_candidate_evaluation(cfg) -> bool:
evidence = cfg.evidence
if evidence is None or evidence.schema_version != 2:
return False
return evidence.source_root is not None or any(
source.type == "filesystem" for source in evidence.sources
)
def _candidate_evaluator(cfg, *, vector_store, embedder):
"""Bind candidate publication to the same read-only retrieval evaluator as the CLI."""
if not _requires_candidate_evaluation(cfg):
return None
from tht.evidence.canonical import load_curated_tree
from tht.evidence.evaluation import evaluate_retrieval, load_evaluation_fixture
@@ -77,9 +88,7 @@ def _candidate_evaluator(cfg, *, vector_store, embedder):
def _validate_materialized_curated_corpus(cfg) -> None:
"""Fail closed on a v2 pinned filesystem corpus before any vector write is possible."""
if cfg.evidence is None or cfg.evidence.schema_version != 2:
return
if not any(source.type == "filesystem" for source in cfg.evidence.sources):
if not _requires_candidate_evaluation(cfg):
return
from tht.evidence import validate_workspace_evidence
+35 -4
View File
@@ -8,6 +8,7 @@ from __future__ import annotations
import hashlib
import re
import unicodedata
from dataclasses import dataclass
from pathlib import Path
from typing import Literal
@@ -121,6 +122,7 @@ def legacy_formula_to_curated(
formula: ConceptFormula,
*,
legacy_path: str,
source_content: str | None = None,
) -> CuratedEvidence | LegacyFormulaMigrationFailure | None:
"""Convert one reviewed legacy formula into its deterministic curated counterpart.
@@ -129,10 +131,39 @@ def legacy_formula_to_curated(
"""
if formula.status != "reviewed":
return None
source_notes = tuple(formula.sources) or (
"Legacy formula migrated without a recorded provenance note.",
)
source_sha256 = hashlib.sha256(formula.dump().encode("utf-8")).hexdigest()
problems: list[str] = []
normalized_source: str | None = None
if source_content is None:
problems.append("original_source_required")
else:
from tht.evidence.authoring import normalize_source_text
normalized_source = normalize_source_text(source_content)
try:
original_formula = ConceptFormula.parse(source_content)
except (TypeError, ValidationError, ValueError, yaml.YAMLError):
problems.append("original_source_invalid")
else:
if original_formula != formula:
problems.append("original_source_mismatch")
source_notes = tuple(formula.sources)
if not source_notes:
problems.append("supporting_excerpts_required")
elif normalized_source is not None and any(
unicodedata.normalize("NFC", note.replace("\r\n", "\n").replace("\r", "\n"))
not in normalized_source
for note in source_notes
):
problems.append("supporting_excerpt_unverified")
if problems:
return LegacyFormulaMigrationFailure(
code="legacy_formula_requires_manual_review",
legacy_path=legacy_path,
formula=formula,
problems=tuple(sorted(problems)),
)
assert normalized_source is not None
source_sha256 = hashlib.sha256(normalized_source.encode("utf-8")).hexdigest()
source_file = legacy_path if legacy_path.startswith("source/") else f"source/{legacy_path}"
# The legacy path is the immutable identity of this unit during migration. Keeping
# its full digest avoids a duplicate public ID when the same concept has reviewed