"""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 == ""