Thoth (tht) è il prodotto, PSD è il cliente. Nessun riferimento al contesto
clinico nel codice.
Rinomine:
- comando+package nsp→tht (dir nsp/→tht/, 46 import, pyproject entry point)
- gate nsp-gate.js→tht-gate.js (+ rewrite token, relayIfNspFails→relayIfThtFails)
- workspace chirone.{example,test}.yaml→tht.{example,test}.yaml (generici)
- env THOTH_→THT_ (19 var) + NSP_ stragglers (NSP_HARNESS_ROOT, NSP_SESSION)
- commenti/docstring chirone/psdwp3/policlinico neutralizzati ('the reference
implementation', 'the DWH')
Aggiunto [tool.setuptools.packages.find] include=['tht*'] (necessario: l'auto-
discovery rompeva con tht/ + workspaces/ come top-level multipli).
.env operatore aggiornato in-place (prefissi THT_, valori preservati, gitignored).
Verifica: pytest 109 passed, npm test 14 pass, tht phase meta --json OK, zero
residui nsp/THOTH_/NSP_/chirone nel package.
106 lines
4.4 KiB
Python
106 lines
4.4 KiB
Python
from tht.decisions import append_decision, DecisionRecord
|
|
from tht.phase import current_phase, effective_decisions
|
|
|
|
|
|
def _d(session, dtype, subject, **kw) -> DecisionRecord:
|
|
"""Helper: appendi una decisione e ritorna la record (per leggere .seq)."""
|
|
return append_decision(
|
|
session, type=dtype, subject=subject,
|
|
detail=kw.get("detail", ""), rationale=kw.get("rationale", ""),
|
|
retracts=kw.get("retracts"),
|
|
)
|
|
|
|
|
|
def test_effective_decisions_excludes_stale_high_phase(tmp_path):
|
|
"""Dopo un phase_reopened a fase bassa, una decisione di fase alta e' stale (excluded).
|
|
|
|
Sequenza: approva 1, approva 2, reopen a 1, poi table_promoted:4.
|
|
current_phase diventa 1 (il reopen). La table_promoted:4 e' di fase 4 > 1 -> stale.
|
|
"""
|
|
s = tmp_path / "s"; s.mkdir()
|
|
_d(s, "phase_approved", "phase:1")
|
|
_d(s, "phase_approved", "phase:2")
|
|
_d(s, "phase_reopened", "phase:1")
|
|
_d(s, "table_promoted", "phase:4") # stale: fase 4 > current_phase(1)
|
|
assert current_phase(s) == 1
|
|
eff = effective_decisions(s)
|
|
types = [d.type for d in eff]
|
|
assert "table_promoted" not in types # la stale di fase 4 e' esclusa
|
|
|
|
|
|
def test_current_phase_after_reopen(tmp_path):
|
|
"""Il fold su audit-excluding-retracted gestisce correttamente il reopen e le ri-approvazioni."""
|
|
s = tmp_path / "s"; s.mkdir()
|
|
_d(s, "phase_approved", "phase:1")
|
|
_d(s, "phase_approved", "phase:2")
|
|
assert current_phase(s) == 3 # dopo 2 approvazioni -> fase 3
|
|
_d(s, "phase_reopened", "phase:1")
|
|
assert current_phase(s) == 1
|
|
_d(s, "phase_approved", "phase:1") # ri-approva dopo reopen (legittima, NON stale)
|
|
assert current_phase(s) == 2
|
|
|
|
|
|
def test_retracted_decision_excluded_from_effective(tmp_path):
|
|
"""Una decisione ritirata (decision_retracted) e' esclusa dalla vista effective."""
|
|
s = tmp_path / "s"; s.mkdir()
|
|
d1 = _d(s, "table_promoted", "phase:4", detail="t1")
|
|
_d(s, "decision_retracted", "phase:4", retracts=d1.seq)
|
|
# senza approvazioni di fase, current_phase=1; table_promoted:4 e' gia' > 1.
|
|
# Ma anche a fase 4 raggiunta, la ritirata NON deve ricomparire.
|
|
_d(s, "phase_approved", "phase:1")
|
|
_d(s, "phase_approved", "phase:2")
|
|
_d(s, "phase_approved", "phase:3")
|
|
_d(s, "phase_approved", "phase:4")
|
|
assert current_phase(s) == 5
|
|
eff = effective_decisions(s)
|
|
seqs = [d.seq for d in eff]
|
|
assert d1.seq not in seqs # la ritirata e' esclusa
|
|
types = [d.type for d in eff]
|
|
assert "decision_retracted" not in types # il marker stesso non conta
|
|
|
|
|
|
def test_current_phase_starts_at_1(tmp_path):
|
|
s = tmp_path / "s"; s.mkdir()
|
|
assert current_phase(s) == 1
|
|
|
|
|
|
def test_current_phase_clamps_at_max_plus_1(tmp_path):
|
|
"""Dopo tutte le approvazioni, current_phase = max_phase + 1."""
|
|
s = tmp_path / "s"; s.mkdir()
|
|
for n in range(1, 9):
|
|
_d(s, "phase_approved", f"phase:{n}")
|
|
assert current_phase(s) == 9 # max_phase(8) + 1
|
|
|
|
|
|
def test_effective_keeps_low_phase_after_high_phase_rollback(tmp_path):
|
|
"""Rollback a F4 NON invalida le decisioni delle fasi 1-3 (che restano <= current_phase)."""
|
|
s = tmp_path / "s"; s.mkdir()
|
|
_d(s, "concept_clarified", "phase:1", detail="x")
|
|
_d(s, "question_rewritten", "phase:3", detail="q")
|
|
_d(s, "table_promoted", "phase:4", detail="t")
|
|
_d(s, "sql_approved", "phase:7", detail="sql") # sara' stale dopo rollback
|
|
for n in range(1, 8):
|
|
_d(s, "phase_approved", f"phase:{n}")
|
|
assert current_phase(s) == 8
|
|
# ora rollback a F4
|
|
_d(s, "phase_reopened", "phase:4")
|
|
assert current_phase(s) == 4
|
|
eff = effective_decisions(s)
|
|
types_subjects = [(d.type, d.subject) for d in eff]
|
|
# le decisioni di fase <= 4 restano
|
|
assert ("concept_clarified", "phase:1") in types_subjects
|
|
assert ("question_rewritten", "phase:3") in types_subjects
|
|
assert ("table_promoted", "phase:4") in types_subjects
|
|
# la decisione di fase 7 (sql_approved) e' ora stale -> esclusa
|
|
assert ("sql_approved", "phase:7") not in types_subjects
|
|
|
|
|
|
def test_effective_decisions_no_reopen_returns_all_non_retracted(tmp_path):
|
|
"""Senza reopen e senza retract, effective = tutte le decisioni (della fase corrente)."""
|
|
s = tmp_path / "s"; s.mkdir()
|
|
_d(s, "concept_clarified", "phase:1", detail="x")
|
|
_d(s, "phase_approved", "phase:1")
|
|
assert current_phase(s) == 2
|
|
eff = effective_decisions(s)
|
|
assert len(eff) == 2
|