chore: hygiene pass — ruff clean, docs storage-model truth, replay /me, failSession log

Audit findings 6.1-6.4 + the audit's remediation plan itself
(docs/superpowers/plans/2026-07-20-full-audit-remediation-plan.md).

- ruff: 34 → 0 (unused imports/f-strings auto-fixed; E702 semicolon lines
  split in test files; one unused local dropped). Suite still 819 green.
- CLAUDE.md + PROJECT_STATE.md no longer claim "no database / settings in
  settings.json": the harness selects filesystem OR PostgreSQL session
  storage (repository.py, server mode), and settings flow through harness
  preferences with the JSON file as fallback only.
- tools/replay: stub /me (SPA boot was parsing the SPA's own HTML as JSON)
  and /runtime/prewarm.
- failSession best-effort persistence now logs its failure server-side
  instead of vanishing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 01:55:41 +02:00
co-authored by Claude Fable 5
parent 1ab0015460
commit c4951e2aa5
21 changed files with 283 additions and 43 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ is not assumed reliable' gains real teeth for the data layer.
import pytest
from sqlalchemy import create_engine, text
from tht.db.connection import can_create_in_schema, make_engine, ping, writable_tables
from tht.db.connection import can_create_in_schema, ping, writable_tables
pytestmark = [pytest.mark.l0]
@@ -9,7 +9,6 @@ Run: pytest -m l2 tests/l2/test_memory_save_one_real.py -s (needs .env + VPN +
"""
from datetime import datetime
from pathlib import Path
from unittest.mock import MagicMock
import pytest
+1 -1
View File
@@ -71,7 +71,7 @@ def test_ablazione_session_manual(l2_env, tmp_path):
["THT_DWH_API_KEY", "THT_VEC_API_KEY", "THT_VEC_WRITE_API_KEY"])
assert env_ok
assert WORKSPACE.exists()
print(f"\n[L2 manual] launch: pi --mode rpc (cwd=harness/)")
print("\n[L2 manual] launch: pi --mode rpc (cwd=harness/)")
print(f"[L2 manual] /nuova-domanda \"{QUESTION}\"")
print("[L2 manual] confirm: sql_final.sql present + ledger has value_grounded/"
"concept_formula_approved. Session dir:", tmp_path)
@@ -25,7 +25,6 @@ def test_ablazione_returns_multiple_columns(l2_env):
non-collapsing aggregation. Requires a built LSH index (tht lsh build)."""
if not WORKSPACE.exists():
pytest.skip(f"per-customer workspace repo not found: {WORKSPACE} (clone tht-workspace-psd)")
from tht.config import LshConfig
try:
from tht.lshindex import load_index, query_index # ported with the lsh build path
except ModuleNotFoundError:
-1
View File
@@ -9,7 +9,6 @@ Pure logic, no DB.
import json
from datetime import date, datetime
from decimal import Decimal
from pathlib import Path
import pytest
+1 -1
View File
@@ -8,7 +8,7 @@ def test_decision_retracted_in_audit_but_marked(tmp_path):
d1 = append_decision(
session, type="table_promoted", subject="phase:4", detail="t1", rationale="r"
)
d2 = append_decision(
append_decision(
session,
type="decision_retracted",
subject="phase:4",
@@ -6,7 +6,6 @@ These tests pin: LIMIT added when absent, respected when present, never added to
non-query statements, and the truncation-detection contract (limit+1 rows).
Pure logic, no DB.
"""
import pytest
import sqlglot
from tht.execute import _inject_limit
-2
View File
@@ -6,9 +6,7 @@ surfaces a candidate formula, the reviewer approves or rejects it (recorded via
concept_formula_approved / concept_formula_rejected), and approved formulas are
part of the schema-linking artifact. The store is frontmatter-YAML + SQL body.
"""
from pathlib import Path
import pytest
from tht.evidence.formula_store import ConceptFormula, retrieve_formula, save_formula
@@ -7,7 +7,6 @@ enforced by the skill prose + gate, validated at L2; this test pins the RECORDIN
contract the gate relies on: free text from "Altro" must round-trip into the
decision rationale and survive persistence, never silently discarded.
"""
from pathlib import Path
from tht.decisions import append_decision, list_decisions
@@ -9,7 +9,6 @@ from datetime import datetime
from tht.config import EligibilityConfig
from tht.mschema.eligibility import classify_all, classify_column, effective_eligibility
from tht.mschema.models import (
Annotations,
ColumnAnnotation,
ColumnPhysical,
PhysicalSchema,
+14 -7
View File
@@ -17,7 +17,8 @@ def test_effective_decisions_excludes_stale_high_phase(tmp_path):
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()
s = tmp_path / "s"
s.mkdir()
_d(s, "phase_approved", "phase:1")
_d(s, "phase_approved", "phase:2")
_d(s, "phase_reopened", "phase:1")
@@ -30,7 +31,8 @@ def test_effective_decisions_excludes_stale_high_phase(tmp_path):
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()
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
@@ -42,7 +44,8 @@ def test_current_phase_after_reopen(tmp_path):
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()
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.
@@ -60,13 +63,15 @@ def test_retracted_decision_excluded_from_effective(tmp_path):
def test_current_phase_starts_at_1(tmp_path):
s = tmp_path / "s"; s.mkdir()
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()
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
@@ -74,7 +79,8 @@ def test_current_phase_clamps_at_max_plus_1(tmp_path):
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()
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")
@@ -97,7 +103,8 @@ def test_effective_keeps_low_phase_after_high_phase_rollback(tmp_path):
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()
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
-1
View File
@@ -6,7 +6,6 @@ Pure-logic tests (no DB needed):
"""
import json
import pytest
from tht.execute.limit import inject_limit_offset
+1 -1
View File
@@ -12,7 +12,7 @@ from tht.mschema.models import (
PhysicalSchema,
TablePhysical,
)
from tht.sqlcheck import CheckResult, validate_sql
from tht.sqlcheck import validate_sql
def _schema() -> PhysicalSchema:
+12 -7
View File
@@ -1,10 +1,10 @@
from pathlib import Path
from tht.taskdoc import generate_task_doc
def test_task_doc_includes_question_and_schema_scope(tmp_path):
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "question.md").write_text("# Domanda\nQuanti pazienti?\n## Assunzioni\n- a")
(s / "schema_linking.json").write_text(
'{"question":"q","candidates":[{"kind":"table","name":"pazienti"}],"joins":[],"excluded":[],"open_questions":[]}'
@@ -17,7 +17,8 @@ def test_task_doc_includes_question_and_schema_scope(tmp_path):
def test_task_doc_never_embeds_full_physical_yaml(tmp_path):
"""physical.yaml e' fatale per un 35B/<200k (~190k token). Mai incorporarlo."""
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "question.md").write_text("q")
# un physical.yaml enorme fuori dalla sessione (come in ChironeWp3: artifacts/mschema/)
(s.parent / "physical.yaml").write_text("x: " + "y" * 800_000)
@@ -27,7 +28,8 @@ def test_task_doc_never_embeds_full_physical_yaml(tmp_path):
def test_task_doc_byte_budget_enforced_on_normal_input(tmp_path):
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "question.md").write_text("q")
doc = generate_task_doc(session_dir=s, phase=1)
assert doc.byte_budget_ok is True
@@ -35,7 +37,8 @@ def test_task_doc_byte_budget_enforced_on_normal_input(tmp_path):
def test_task_doc_byte_budget_violation_flagged(tmp_path):
"""Se un artefatto di sessione e' enorme (input perverso), byte_budget_ok diventa False."""
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "question.md").write_text("q")
(s / "schema_linking.json").write_text("x: " + "y" * 400_000) # ~400KB -> over budget
doc = generate_task_doc(session_dir=s, phase=7)
@@ -43,7 +46,8 @@ def test_task_doc_byte_budget_violation_flagged(tmp_path):
def test_task_doc_carries_phase_header(tmp_path):
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "question.md").write_text("q")
doc = generate_task_doc(session_dir=s, phase=4)
assert "fase 4" in doc.body.lower() or "fase 4" in doc.body
@@ -54,7 +58,8 @@ def test_task_doc_excludes_stale_decisions_post_rollback(tmp_path):
Dopo rollback a F4, una sql_approved:7 stale non appare nel brief delle decisioni."""
from tht.decisions import append_decision
from tht.phase import current_phase
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "question.md").write_text("q")
# simula: lavoro fino a F7, poi rollback a F4
append_decision(s, type="phase_approved", subject="phase:1")
+12 -6
View File
@@ -2,7 +2,8 @@ from tht.teardown import teardown_to_phase
def test_teardown_to_phase_4_deletes_phase5plus_artifacts(tmp_path):
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "schema_linking.json").write_text("{}") # F4 artifact
(s / "cte_plan.json").write_text("[]") # F6 artifact
(s / "ctes").mkdir()
@@ -19,7 +20,8 @@ def test_teardown_to_phase_4_deletes_phase5plus_artifacts(tmp_path):
def test_teardown_to_phase_1_deletes_everything(tmp_path):
"""Rollback a F1 cancella tutti gli artefatti (tutte le fasi > 1)."""
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "question.md").write_text("q") # F3 artifact
(s / "schema_linking.json").write_text("{}") # F4 artifact
(s / "cte_plan.json").write_text("[]") # F6 artifact
@@ -37,7 +39,8 @@ def test_teardown_to_phase_1_deletes_everything(tmp_path):
def test_teardown_to_phase_7_preserves_phase6_artifacts(tmp_path):
"""Rollback a F7 preserva gli artefatti delle fasi <= 7."""
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "schema_linking.json").write_text("{}") # F4 artifact
(s / "cte_plan.json").write_text("[]") # F6 artifact
(s / "ctes").mkdir()
@@ -52,14 +55,16 @@ def test_teardown_to_phase_7_preserves_phase6_artifacts(tmp_path):
def test_teardown_to_phase_on_empty_session_is_noop(tmp_path):
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
report = teardown_to_phase(s, target_phase=4)
assert report.deleted_files == []
def test_teardown_to_phase_nonexistent_artifacts_no_error(tmp_path):
"""Se gli artefatti non esistono (sessione nuova), teardown non fallisce."""
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
report = teardown_to_phase(s, target_phase=1)
assert report.deleted_files == []
@@ -67,7 +72,8 @@ def test_teardown_to_phase_nonexistent_artifacts_no_error(tmp_path):
def test_teardown_deletes_orphaned_cte_files_not_in_current_plan(tmp_path):
"""Il bug degli orfani CTE: dopo un re-derive con piano diverso, i vecchi .sql
non piu' nel piano restano su disco e bloccano finalize. teardown li pulisce."""
s = tmp_path / "sess"; s.mkdir()
s = tmp_path / "sess"
s.mkdir()
(s / "ctes").mkdir()
(s / "ctes" / "old_cte.sql").write_text("SELECT 1") # orphan
(s / "ctes" / "another.sql").write_text("SELECT 2") # orphan
-1
View File
@@ -1,4 +1,3 @@
from pathlib import Path
from tht.workspace import load_workspace, WorkspaceError