Commit 43c3f0503b1bca667ee96f4912bcf7384ed13597
1 parent
25ee708e
Give test_script verify failures their own blocked_reason
ConsistencyCheckFailedError is now raised by both the PRD-consistency check and the test_script mechanical compile-check (07a0a27), but scheduler._blocked_reason_for still labeled every instance 'prd_consistency_check_failed', misleadingly blaming the PRD for test_script node failures. Distinguishes by node_id substring match (see docstring for the ponytail ceiling/upgrade path). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Showing
2 changed files
with
33 additions
and
0 deletions
src/scheduler.py
| @@ -89,6 +89,25 @@ _ROUTABLE_EXCEPTIONS = tuple(exc for exc, _ in _BLOCKED_REASON_FOR_EXCEPTION) | @@ -89,6 +89,25 @@ _ROUTABLE_EXCEPTIONS = tuple(exc for exc, _ in _BLOCKED_REASON_FOR_EXCEPTION) | ||
| 89 | 89 | ||
| 90 | 90 | ||
| 91 | def _blocked_reason_for(exc): | 91 | def _blocked_reason_for(exc): |
| 92 | + """2026-08-07: `ConsistencyCheckFailedError` used to be raised only by | ||
| 93 | + the PRD-internal-contradiction check, but commit 07a0a27 (2026-08-06) | ||
| 94 | + generalized the same exception/retry machinery to the test_script | ||
| 95 | + mechanical compile-check too -- without this, both show up in the | ||
| 96 | + console as the same misleading 'prd_consistency_check_failed' label | ||
| 97 | + (see docs/todo/validator-test-script-narrative-output-issue.md), even | ||
| 98 | + when the real failure has nothing to do with the PRD. | ||
| 99 | + ponytail: distinguishes by substring-matching the failing node's LLM- | ||
| 100 | + chosen node_id (e.g. 'n_test_script'/'validator_test_script') rather | ||
| 101 | + than a real typed discriminator threaded through verify_fn -- good | ||
| 102 | + enough because every real graph_manifest observed so far names its | ||
| 103 | + test_script node descriptively; ceiling is a node named ambiguously | ||
| 104 | + silently falling back to the PRD label. Upgrade path if that's ever | ||
| 105 | + wrong in practice: have verify_fn return a third 'kind' value instead | ||
| 106 | + of inferring from node_id text.""" | ||
| 107 | + if isinstance(exc, work_graph_orchestrate.ConsistencyCheckFailedError): | ||
| 108 | + if "test_script" in (exc.node_id or ""): | ||
| 109 | + return "test_script_verification_failed" | ||
| 110 | + return "prd_consistency_check_failed" | ||
| 92 | for exc_type, reason in _BLOCKED_REASON_FOR_EXCEPTION: | 111 | for exc_type, reason in _BLOCKED_REASON_FOR_EXCEPTION: |
| 93 | if isinstance(exc, exc_type): | 112 | if isinstance(exc, exc_type): |
| 94 | return reason | 113 | return reason |
src/test_scheduler.py
| @@ -349,6 +349,20 @@ class ConsistencyCheckFailedRoutingTest(RoutableExceptionTestBase): | @@ -349,6 +349,20 @@ class ConsistencyCheckFailedRoutingTest(RoutableExceptionTestBase): | ||
| 349 | self.assertEqual(result["blocked_reason"], "prd_consistency_check_failed") | 349 | self.assertEqual(result["blocked_reason"], "prd_consistency_check_failed") |
| 350 | self._assert_task_blocked(result["task_id"], "prd_consistency_check_failed") | 350 | self._assert_task_blocked(result["task_id"], "prd_consistency_check_failed") |
| 351 | 351 | ||
| 352 | + def test_test_script_node_gets_its_own_reason_not_the_prd_one(self): | ||
| 353 | + # 2026-08-07: same exception class, but raised by the test_script | ||
| 354 | + # mechanical compile-check (docs/todo/validator-test-script- | ||
| 355 | + # narrative-output-issue.md), not the PRD-consistency check -- the | ||
| 356 | + # console must not blame the PRD for an unrelated failure. | ||
| 357 | + result = self._run_with_raising_lifecycle( | ||
| 358 | + scheduler.work_graph_orchestrate.ConsistencyCheckFailedError( | ||
| 359 | + "boom", node_id="validator_test_script" | ||
| 360 | + ) | ||
| 361 | + ) | ||
| 362 | + self.assertEqual(result["final_status"], "blocked_pending_human") | ||
| 363 | + self.assertEqual(result["blocked_reason"], "test_script_verification_failed") | ||
| 364 | + self._assert_task_blocked(result["task_id"], "test_script_verification_failed") | ||
| 365 | + | ||
| 352 | 366 | ||
| 353 | class UnroutedExceptionStillBlocksTaskTest(RoutableExceptionTestBase): | 367 | class UnroutedExceptionStillBlocksTaskTest(RoutableExceptionTestBase): |
| 354 | def test_other_exceptions_block_the_task_instead_of_leaving_it_stuck(self): | 368 | def test_other_exceptions_block_the_task_instead_of_leaving_it_stuck(self): |