Files
ThothII/harness/tests/test_freetext_interpretation.py
marcopanandClaude Fable 5 c4951e2aa5 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>
2026-07-20 01:55:41 +02:00

62 lines
2.6 KiB
Python

"""L1: free-text interpretation -- rationale capture contract (spec D13, §4.6).
D13 instructs the model to evaluate Altro/Rifiuta/steering free text in context,
act on it, re-ask if ambiguous (instead of defaulting), and record the user's
words in the decision rationale. The actual interpretation is model behavior
enforced by the skill prose + gate, validated at L2; this test pins the RECORDING
contract the gate relies on: free text from "Altro" must round-trip into the
decision rationale and survive persistence, never silently discarded.
"""
from tht.decisions import append_decision, list_decisions
def test_altro_freetext_recorded_in_rationale(tmp_path):
s = tmp_path / "sess"
s.mkdir()
# The gate, on receiving Altro text, appends a decision whose rationale carries
# the user's words verbatim (not discarded, not paraphrased away).
append_decision(
s,
type="table_promoted",
subject="phase:4",
detail="ablazione",
rationale="Utente (Altro): 'ablazione' va cercato anche in patologia, non solo nel flag",
)
decisions = list_decisions(s)
assert len(decisions) == 1
assert "patologia" in decisions[0].rationale # user text preserved
assert "Altro" in decisions[0].rationale
def test_freetext_survives_persistence_roundtrip(tmp_path):
s = tmp_path / "sess"
s.mkdir()
user_text = "Rifiuto: la join corretta e' su id_episodio, non id_ricovero"
append_decision(s, type="join_modified", subject="phase:4",
detail="t1.id_ricovero", rationale=user_text)
append_decision(s, type="table_excluded", subject="phase:4",
detail="dimessi", rationale="ok") # a second, unrelated decision
reloaded = list_decisions(s)
assert reloaded[0].rationale == user_text # exact preservation
assert reloaded[1].rationale == "ok"
def test_multiline_steering_text_preserved(tmp_path):
s = tmp_path / "sess"
s.mkdir()
steering = "Considera solo il 2025.\nEscludi i day-hospital.\nUsa la data di dimissione."
append_decision(s, type="concept_clarified", subject="phase:1",
detail="periodo", rationale=steering)
assert list_decisions(s)[0].rationale == steering
# the multi-line steering is intact, including newlines
assert "\n" in list_decisions(s)[0].rationale
def test_empty_rationale_is_allowed(tmp_path):
# a decision can legitimately have no free text (e.g. a clean promote).
s = tmp_path / "sess"
s.mkdir()
append_decision(s, type="table_promoted", subject="phase:4", detail="pazienti")
assert list_decisions(s)[0].rationale == ""