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:
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,7 +6,6 @@ Pure-logic tests (no DB needed):
|
||||
"""
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from tht.execute.limit import inject_limit_offset
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,4 +1,3 @@
|
||||
from pathlib import Path
|
||||
|
||||
from tht.workspace import load_workspace, WorkspaceError
|
||||
|
||||
|
||||
Reference in New Issue
Block a user