| from figment.focused_repair import ( |
| RepairScope, |
| build_focused_repair_prompt, |
| build_focused_repair_prompts, |
| classify_validation_failures, |
| mandatory_source_card_ids_for_scope, |
| missing_mandatory_source_cards, |
| ) |
|
|
|
|
| def test_classifies_validator_failures_into_focused_scopes_and_fields() -> None: |
| failures = [ |
| "handoff_note_sbar is missing: assessment_observations_only, handoff_request", |
| "missing_info_to_collect does not reference required observations for CHEST-PAIN-ESCALATION-v1", |
| "source_cards not in allowed/retrieved card IDs: WOUND-INFECTION-ESCALATION-v1", |
| "candidate pathway CHEST-PAIN-ESCALATION-v1 is not cited in source_cards", |
| "forbidden clinical language: prescribe", |
| "protocol_urgency routine is below deterministic floor emergency", |
| ] |
|
|
| scopes = classify_validation_failures(failures) |
|
|
| assert [(scope.name, scope.fields) for scope in scopes] == [ |
| ("handoff_note_sbar", ("handoff_note_sbar",)), |
| ("missing_observations", ("missing_info_to_collect", "next_observations_to_collect")), |
| ("citations_and_pathways", ("source_cards", "candidate_protocol_pathways")), |
| ( |
| "forbidden_clinical_language", |
| ( |
| "red_flags", |
| "intake_facts", |
| "candidate_protocol_pathways", |
| "missing_info_to_collect", |
| "next_observations_to_collect", |
| "conflicts_or_uncertainties", |
| "responder_checklist", |
| "do_not_do", |
| "handoff_note_sbar", |
| "responder_plain_language_script", |
| "safety_boundary", |
| ), |
| ), |
| ("protocol_urgency", ("protocol_urgency",)), |
| ] |
| assert scopes[2].failures == ( |
| "source_cards not in allowed/retrieved card IDs: WOUND-INFECTION-ESCALATION-v1", |
| "candidate pathway CHEST-PAIN-ESCALATION-v1 is not cited in source_cards", |
| ) |
|
|
|
|
| def test_schema_failures_extract_missing_and_type_invalid_fields() -> None: |
| scopes = classify_validation_failures( |
| [ |
| "missing required schema keys: red_flags, intake_facts, safety_boundary", |
| "responder_plain_language_script must be a string", |
| "next_observations_to_collect must be a list", |
| ] |
| ) |
|
|
| assert scopes == ( |
| RepairScope( |
| name="schema", |
| fields=( |
| "red_flags", |
| "intake_facts", |
| "next_observations_to_collect", |
| "responder_plain_language_script", |
| "safety_boundary", |
| ), |
| failures=( |
| "missing required schema keys: red_flags, intake_facts, safety_boundary", |
| "responder_plain_language_script must be a string", |
| "next_observations_to_collect must be a list", |
| ), |
| ), |
| ) |
|
|
|
|
| def test_build_focused_repair_prompt_limits_model_to_selected_fields() -> None: |
| scope = RepairScope( |
| name="missing_observations", |
| fields=("missing_info_to_collect", "next_observations_to_collect"), |
| failures=( |
| "missing_info_to_collect does not reference required observations for CHEST-PAIN-ESCALATION-v1", |
| ), |
| ) |
| previous_output = { |
| "protocol_urgency": "emergency", |
| "source_cards": ["CHEST-PAIN-ESCALATION-v1"], |
| "missing_info_to_collect": ["ask anything else that seems relevant"], |
| "next_observations_to_collect": ["keep monitoring"], |
| } |
|
|
| prompt = build_focused_repair_prompt( |
| original_prompt="BASE NAVIGATOR PROMPT", |
| previous_output=previous_output, |
| repair_scope=scope, |
| urgency_floor="emergency", |
| required_observation_targets=[ |
| { |
| "id": "CHEST-PAIN-ESCALATION-v1::required_observation::1", |
| "card_id": "CHEST-PAIN-ESCALATION-v1", |
| "display_text": "chest pain description", |
| "cue_tokens": ["chest", "pain", "description"], |
| } |
| ], |
| ) |
|
|
| assert "BASE NAVIGATOR PROMPT" in prompt |
| assert "Do not return the whole navigator output" in prompt |
| assert "exactly these top-level keys: missing_info_to_collect, next_observations_to_collect" in prompt |
| assert "protocol_urgency" not in prompt.split("PREVIOUS_VALUES_FOR_ALLOWED_FIELDS:", 1)[1] |
| assert "required observations" in prompt |
| assert "CHEST-PAIN-ESCALATION-v1" in prompt |
| assert "CHEST-PAIN-ESCALATION-v1::required_observation::1" in prompt |
| assert "chest pain description" in prompt |
| assert "required_display_text_must_copy_exactly" in prompt |
| assert "Copy every display_text" in prompt |
|
|
|
|
| def test_citation_repair_prompt_names_mandatory_source_cards() -> None: |
| scope = classify_validation_failures( |
| [ |
| "fired rule card STROKE-SIGNS-v1 is not cited in source_cards", |
| "candidate pathway STROKE-SIGNS-v1 is not cited in source_cards", |
| ] |
| )[0] |
|
|
| prompt = build_focused_repair_prompt( |
| original_prompt="BASE NAVIGATOR PROMPT", |
| previous_output={ |
| "source_cards": ["SAFETY-BOUNDARIES-v1"], |
| "candidate_protocol_pathways": [ |
| { |
| "card_id": "SAFETY-BOUNDARIES-v1", |
| "reason_relevant": "Existing pathway.", |
| } |
| ], |
| }, |
| repair_scope=scope, |
| urgency_floor="emergency", |
| ) |
|
|
| assert scope.name == "citations_and_pathways" |
| assert scope.fields == ("source_cards", "candidate_protocol_pathways") |
| assert mandatory_source_card_ids_for_scope(scope) == ("STROKE-SIGNS-v1",) |
| assert "Mandatory source cards: STROKE-SIGNS-v1" in prompt |
| assert "Do not remove any mandatory source card" in prompt |
| assert "exactly these top-level keys: source_cards, candidate_protocol_pathways" in prompt |
|
|
|
|
| def test_citation_repair_rejects_output_that_omits_mandatory_source_card() -> None: |
| scope = classify_validation_failures( |
| ["fired rule card PREG-DANGER-SIGNS-v1 is not cited in source_cards"] |
| )[0] |
|
|
| assert missing_mandatory_source_cards( |
| scope, |
| { |
| "source_cards": ["REFERRAL-SBAR-v1", "SAFETY-BOUNDARIES-v1"], |
| "candidate_protocol_pathways": [ |
| { |
| "card_id": "REFERRAL-SBAR-v1", |
| "reason_relevant": "SBAR handoff.", |
| } |
| ], |
| }, |
| ) == ("PREG-DANGER-SIGNS-v1",) |
| assert missing_mandatory_source_cards( |
| scope, |
| { |
| "source_cards": ["PREG-DANGER-SIGNS-v1", "REFERRAL-SBAR-v1"], |
| "candidate_protocol_pathways": [ |
| { |
| "card_id": "PREG-DANGER-SIGNS-v1", |
| "reason_relevant": "Pregnancy danger sign fired deterministically.", |
| } |
| ], |
| }, |
| ) == () |
|
|
|
|
| def test_forbidden_language_prompt_keeps_safety_boundaries_explicit() -> None: |
| scope = classify_validation_failures(["forbidden clinical language: discharge home"])[0] |
|
|
| prompt = build_focused_repair_prompt( |
| original_prompt="BASE NAVIGATOR PROMPT", |
| previous_output={"responder_checklist": ["Discharge home if symptoms improve."]}, |
| repair_scope=scope, |
| urgency_floor="urgent", |
| ) |
|
|
| assert scope.name == "forbidden_clinical_language" |
| assert "remove or rewrite unsafe clinical language" in prompt |
| assert "diagnose, prescribe, dose, discharge, or override" in prompt |
| assert "urgency_floor" in prompt |
| assert "urgent" in prompt |
|
|
|
|
| def test_build_focused_repair_prompts_groups_failures_with_scope_metadata() -> None: |
| prompts = build_focused_repair_prompts( |
| original_prompt="BASE NAVIGATOR PROMPT", |
| previous_output={"handoff_note_sbar": {"situation": "Chest pain"}}, |
| failures=[ |
| "handoff_note_sbar background is not grounded in confirmed intake or rules", |
| "handoff_note_sbar assessment_observations_only has unsupported high-risk handoff facts: skull", |
| ], |
| urgency_floor="emergency", |
| ) |
|
|
| assert len(prompts) == 1 |
| assert prompts[0].scope == RepairScope( |
| name="handoff_note_sbar", |
| fields=("handoff_note_sbar",), |
| failures=( |
| "handoff_note_sbar background is not grounded in confirmed intake or rules", |
| "handoff_note_sbar assessment_observations_only has unsupported high-risk handoff facts: skull", |
| ), |
| ) |
| assert "ground handoff_note_sbar only in confirmed intake and deterministic rules" in prompts[0].prompt |
|
|