From 7158b55d5b7c0c84ecd9c751fb3497aba36d8c35 Mon Sep 17 00:00:00 2001 From: Trecek Date: Thu, 11 Jun 2026 07:51:59 -0700 Subject: [PATCH] fix: replace open_kitchen substring marker with JSON field check in failure predicates The substring '--- INGREDIENTS TABLE ---' is injected by a hook formatter on Claude Code and is absent on Codex, causing silent failure-detection blindness on the Codex backend. The JSON field 'ingredients_table' is present in all open_kitchen responses regardless of backend. Updated both the orchestrator prompt (cli/_prompts_orchestrator.py) and the food truck prompt (fleet/_prompts.py) failure predicates to use the JSON field check. Tests in test_orchestrator_prompt_contract.py renamed and inverted to assert the new behavior; new test added to test_food_truck_prompt.py for the food truck prompt. Refs: T5-P1-A2-WP1 Co-Authored-By: Claude Fable 5 --- src/autoskillit/cli/_prompts_orchestrator.py | 4 ++-- src/autoskillit/fleet/_prompts.py | 4 ++-- tests/cli/test_orchestrator_prompt_contract.py | 8 ++++---- tests/fleet/test_food_truck_prompt.py | 17 +++++++++++++++++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/autoskillit/cli/_prompts_orchestrator.py b/src/autoskillit/cli/_prompts_orchestrator.py index 1dd5c789b4..21c66c39f1 100644 --- a/src/autoskillit/cli/_prompts_orchestrator.py +++ b/src/autoskillit/cli/_prompts_orchestrator.py @@ -155,8 +155,8 @@ def _build_orchestrator_prompt( - classify_fix: "error:" line present in output FAILURE PREDICATE — open_kitchen: - If the open_kitchen response contains `"success": false` OR does not - contain the substring `--- INGREDIENTS TABLE ---`: + If the open_kitchen response contains `"success": false` OR `"ingredients_table"`: null + (field absent or null in the JSON response): 1. Extract and print the value of "user_visible_message" from the JSON response verbatim (fall back to the raw response text if parsing fails). diff --git a/src/autoskillit/fleet/_prompts.py b/src/autoskillit/fleet/_prompts.py index af09b134fa..810a23d0bf 100644 --- a/src/autoskillit/fleet/_prompts.py +++ b/src/autoskillit/fleet/_prompts.py @@ -201,8 +201,8 @@ def _build_food_truck_prompt( - classify_fix: "error:" line present in output FAILURE PREDICATE — open_kitchen: - If the open_kitchen response contains `"success": false` OR does not - contain the substring `--- INGREDIENTS TABLE ---`: + If the open_kitchen response contains `"success": false` OR `"ingredients_table"`: null + (field absent or null in the JSON response): 1. Emit the sentinel block with success=false and reason="open_kitchen_failed". 2. End the session. diff --git a/tests/cli/test_orchestrator_prompt_contract.py b/tests/cli/test_orchestrator_prompt_contract.py index 19b47270fe..e9954acd0a 100644 --- a/tests/cli/test_orchestrator_prompt_contract.py +++ b/tests/cli/test_orchestrator_prompt_contract.py @@ -42,13 +42,13 @@ def test_prompt_open_kitchen_predicate_forbids_askuserquestion(self): section = prompt[idx : idx + 500] assert "DO NOT call AskUserQuestion" in section - def test_open_kitchen_predicate_uses_substring_not_json_field_dispatch(self): - """Negative: the predicate block must NOT use JSON-field dispatch phrasing.""" + def test_open_kitchen_predicate_uses_json_field_not_substring(self): + """The predicate block must use JSON-field check, not substring marker.""" prompt = _get_prompt() idx = prompt.index("FAILURE PREDICATE — open_kitchen") section = prompt[idx : idx + 500] - assert "json.loads" not in section.lower() - assert "parsed[" not in section + assert "--- INGREDIENTS TABLE ---" not in section + assert "ingredients_table" in section class TestStep0ToolPredicateCoverage: diff --git a/tests/fleet/test_food_truck_prompt.py b/tests/fleet/test_food_truck_prompt.py index 7009176b2c..cf4402aff5 100644 --- a/tests/fleet/test_food_truck_prompt.py +++ b/tests/fleet/test_food_truck_prompt.py @@ -157,3 +157,20 @@ def test_food_truck_prompt_no_caller_instructions_section_when_empty(): caller_instructions="", ) assert "CALLER INSTRUCTIONS" not in prompt + + +def test_food_truck_open_kitchen_predicate_uses_json_field_not_substring(): + """Food truck predicate must use JSON-field check, not substring marker.""" + prompt = _build_food_truck_prompt( + recipe="test-recipe", + task="Test task", + ingredients={}, + mcp_prefix=DIRECT_PREFIX, + dispatch_id="test-dispatch", + campaign_id="test-campaign", + l3_timeout_sec=300, + ) + idx = prompt.index("FAILURE PREDICATE — open_kitchen") + section = prompt[idx : idx + 500] + assert "--- INGREDIENTS TABLE ---" not in section + assert "ingredients_table" in section