feat: redesign persisted session summaries
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
"""Tests for `tht session documents --json` and build_documents."""
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from tht.config import DatabaseConfig
|
||||
from tht.session.store import build_documents, create_session
|
||||
from tht.decisions import DecisionRecord
|
||||
from tht.session.models import SessionSnapshot
|
||||
from tht.session.store import build_documents, build_snapshot_documents, create_session
|
||||
from tht.cli.session_cmd import session_app
|
||||
|
||||
|
||||
@@ -38,6 +41,184 @@ def test_build_documents_includes_existing_artifacts_only(tmp_path):
|
||||
assert "validation_report" not in docs # not written
|
||||
|
||||
|
||||
def _decision(seq: int, type_: str, subject: str, detail: str = "", rationale: str = ""):
|
||||
return DecisionRecord(
|
||||
seq=seq,
|
||||
ts=datetime(2026, 1, 1),
|
||||
type=type_,
|
||||
subject=subject,
|
||||
detail=detail,
|
||||
rationale=rationale,
|
||||
)
|
||||
|
||||
|
||||
def _rich_session(tmp_path):
|
||||
manifest = create_session("domanda originale", _db(), tmp_path)
|
||||
session_dir = tmp_path / manifest.id
|
||||
question = (
|
||||
"# Domanda\n\n"
|
||||
"Conta i pazienti con **ablazione**.\n\n"
|
||||
"## Assunzioni\n\n"
|
||||
"- Considera solo gli adulti\n"
|
||||
"- Usa l'ultima procedura disponibile\n"
|
||||
)
|
||||
report = (
|
||||
"# Validation report\n\n"
|
||||
"## Parsing e read-only\n\n- parsing: OK\n\n"
|
||||
"## Preview / aggregato\n\n"
|
||||
"- tempo di esecuzione: 12 ms (limit 10)\n\n"
|
||||
"| count |\n|---|\n| 42 |\n\n"
|
||||
"## Note del reviewer\n\nRisultato verificato.\n"
|
||||
)
|
||||
decisions = [
|
||||
_decision(1, "concept_clarified", "paziente attivo", "`flag_attivo = TRUE`"),
|
||||
_decision(2, "memory_promotion_declined", "paziente attivo", "seq:1"),
|
||||
_decision(
|
||||
3,
|
||||
"concept_clarified",
|
||||
"ablazione valida",
|
||||
"Procedura con stato **completato**.",
|
||||
"Definizione approvata dal reviewer.",
|
||||
),
|
||||
_decision(4, "memory_promoted", "ablazione valida", "seq:3"),
|
||||
_decision(5, "phase_approved", "phase:1"),
|
||||
_decision(6, "table_promoted", "fact_procedure"),
|
||||
_decision(7, "column_promoted", "fact_procedure.status"),
|
||||
_decision(8, "column_excluded", "fact_procedure.note", "Non pertinente"),
|
||||
]
|
||||
(session_dir / "question.md").write_text(question)
|
||||
(session_dir / "sql_final.sql").write_text("SELECT COUNT(*) FROM fact_procedure")
|
||||
(session_dir / "validation_report.md").write_text(report)
|
||||
(session_dir / "schema_linking.json").write_text(
|
||||
'{"question":"q","candidates":[],"joins":[],"excluded":[],"open_questions":[]}'
|
||||
)
|
||||
(session_dir / "review_decisions.jsonl").write_text(
|
||||
"\n".join(d.model_dump_json() for d in decisions) + "\n"
|
||||
)
|
||||
return manifest, session_dir, question, report, decisions
|
||||
|
||||
|
||||
def test_build_documents_projects_the_approved_summary_order_and_sections(tmp_path):
|
||||
manifest, session_dir, _question, _report, _decisions = _rich_session(tmp_path)
|
||||
|
||||
docs = build_documents(manifest, session_dir)
|
||||
|
||||
assert [doc["key"] for doc in docs] == [
|
||||
"question",
|
||||
"sql",
|
||||
"preview",
|
||||
"revised_question",
|
||||
"assumptions",
|
||||
"memories",
|
||||
"schema_linking",
|
||||
"validation_report",
|
||||
"decisions",
|
||||
]
|
||||
by_key = {doc["key"]: doc for doc in docs}
|
||||
assert by_key["preview"]["format"] == "markdown"
|
||||
assert "tempo di esecuzione: 12 ms" in by_key["preview"]["content"]
|
||||
assert "| 42 |" in by_key["preview"]["content"]
|
||||
assert "Parsing e read-only" not in by_key["preview"]["content"]
|
||||
assert by_key["revised_question"]["content"] == (
|
||||
"# Domanda\n\nConta i pazienti con **ablazione**."
|
||||
)
|
||||
assert by_key["assumptions"]["content"] == (
|
||||
"## Assunzioni\n\n- Considera solo gli adulti\n"
|
||||
"- Usa l'ultima procedura disponibile"
|
||||
)
|
||||
assert "Preview / aggregato" not in by_key["validation_report"]["content"]
|
||||
assert "Parsing e read-only" in by_key["validation_report"]["content"]
|
||||
assert "Note del reviewer" in by_key["validation_report"]["content"]
|
||||
|
||||
|
||||
def test_build_documents_groups_memories_and_filters_summary_decisions(tmp_path):
|
||||
manifest, session_dir, _question, _report, _decisions = _rich_session(tmp_path)
|
||||
|
||||
by_key = {doc["key"]: doc for doc in build_documents(manifest, session_dir)}
|
||||
memories = json.loads(by_key["memories"]["content"])
|
||||
|
||||
assert memories == [
|
||||
{
|
||||
"status": "approved",
|
||||
"subject": "ablazione valida",
|
||||
"detail": "Procedura con stato **completato**.",
|
||||
"rationale": "Definizione approvata dal reviewer.",
|
||||
},
|
||||
{
|
||||
"status": "declined",
|
||||
"subject": "paziente attivo",
|
||||
"detail": "`flag_attivo = TRUE`",
|
||||
"rationale": "",
|
||||
},
|
||||
]
|
||||
decisions = by_key["decisions"]["content"]
|
||||
assert "column_excluded" in decisions
|
||||
for hidden in (
|
||||
"concept_clarified",
|
||||
"memory_promoted",
|
||||
"memory_promotion_declined",
|
||||
"phase_approved",
|
||||
"table_promoted",
|
||||
"column_promoted",
|
||||
):
|
||||
assert hidden not in decisions
|
||||
|
||||
|
||||
def test_build_documents_keeps_validation_report_when_it_has_no_preview(tmp_path):
|
||||
manifest = create_session("q", _db(), tmp_path)
|
||||
session_dir = tmp_path / manifest.id
|
||||
report = "# Validation report\n\n## Parsing\n\n- OK\n"
|
||||
(session_dir / "validation_report.md").write_text(report)
|
||||
|
||||
by_key = {doc["key"]: doc for doc in build_documents(manifest, session_dir)}
|
||||
|
||||
assert "preview" not in by_key
|
||||
assert by_key["validation_report"]["content"] == report.strip()
|
||||
|
||||
|
||||
def test_memory_projection_does_not_repeat_detail_as_marker_rationale(tmp_path):
|
||||
manifest = create_session("q", _db(), tmp_path)
|
||||
session_dir = tmp_path / manifest.id
|
||||
decisions = [
|
||||
_decision(1, "concept_clarified", "termine", "Definizione in **Markdown**."),
|
||||
_decision(
|
||||
2,
|
||||
"memory_promoted",
|
||||
"termine",
|
||||
"seq:1",
|
||||
"Definizione in **Markdown**.",
|
||||
),
|
||||
]
|
||||
(session_dir / "review_decisions.jsonl").write_text(
|
||||
"\n".join(d.model_dump_json() for d in decisions) + "\n"
|
||||
)
|
||||
|
||||
by_key = {doc["key"]: doc for doc in build_documents(manifest, session_dir)}
|
||||
|
||||
assert json.loads(by_key["memories"]["content"])[0] == {
|
||||
"status": "approved",
|
||||
"subject": "termine",
|
||||
"detail": "Definizione in **Markdown**.",
|
||||
"rationale": "",
|
||||
}
|
||||
|
||||
|
||||
def test_filesystem_and_snapshot_builders_share_the_same_projection(tmp_path):
|
||||
manifest, session_dir, question, report, decisions = _rich_session(tmp_path)
|
||||
snapshot = SessionSnapshot(
|
||||
manifest=manifest,
|
||||
artifacts={
|
||||
"question": question,
|
||||
"schema_linking": (session_dir / "schema_linking.json").read_text(),
|
||||
"sql_final": (session_dir / "sql_final.sql").read_text(),
|
||||
"validation_report": report,
|
||||
},
|
||||
decisions=decisions,
|
||||
)
|
||||
|
||||
assert build_snapshot_documents(snapshot) == build_documents(manifest, session_dir)
|
||||
|
||||
|
||||
def test_cli_documents_json(tmp_path, monkeypatch):
|
||||
m = create_session("q", _db(), tmp_path)
|
||||
from tht.cli import session_cmd
|
||||
|
||||
Reference in New Issue
Block a user