fix(company): make delivery-review self-evolution survive the hardened runtime
Approving (or sending feedback on) the delivery review card runs employee self-evolution as company work items, but the finalizer required the turn's FINAL chat text to be bare JSON. The hardened native runtime appends a verification status line to every final text and the manager dispatch guard displaces the final message with a justification, so every reflection died with invalid_self_evolution_json even when the model produced valid patches on all three attempts, and the FAILED items polluted the delivered run's terminal verdict. - Add a submit_self_evolution_patches tool as the authoritative result channel (exposed only on self-evolution work items, approval-exempt). The text parser becomes a fallback that scans fenced blocks and balanced JSON objects, and retry feedback now carries the concrete parse failure plus the tool instruction. - Settle abandoned reflections as CANCELLED (self_evolution_abandoned) and exclude kind=self_evolution from run-lifecycle settlement so an opt-in reflection can never dirty a delivered run. - Claim the review card with a consuming CAS before spawning (duplicate approve/feedback replies answer idempotently instead of re-entering), bound the reflection run with a 40-minute deadline that cancels leftover self-evolution items, and hand the claim back to pending when the consumed run crashes mid-flight. Verified live on real runs: the unfixed code failed the approve path in 90s with zero patches recorded; with the fix both the approve and the feedback paths recorded patches end-to-end (CEO->COO and CEO->CMO cascades, zero retries, human feedback reflected in patch content). tests/: 1924 passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3369,7 +3369,11 @@ class CompanyCollaborationTests(unittest.IsolatedAsyncioTestCase):
|
||||
)
|
||||
self.assertEqual(retry.status, TaskStatus.PENDING)
|
||||
self.assertEqual(task.metadata["self_evolution_patch_retry_count"], 1)
|
||||
self.assertIn("strict JSON", task.context_snapshot["self_evolution_patch_retry_feedback"])
|
||||
# Retry feedback points the agent at the authoritative tool channel.
|
||||
self.assertIn(
|
||||
"submit_self_evolution_patches",
|
||||
task.context_snapshot["self_evolution_patch_retry_feedback"],
|
||||
)
|
||||
save_task.assert_awaited_once()
|
||||
|
||||
task.metadata["self_evolution_patch_retry_count"] = 2
|
||||
@@ -3377,8 +3381,10 @@ class CompanyCollaborationTests(unittest.IsolatedAsyncioTestCase):
|
||||
task,
|
||||
TaskResult(status=TaskStatus.DONE, content="still not json", artifacts={}),
|
||||
)
|
||||
self.assertEqual(failed.status, TaskStatus.FAILED)
|
||||
self.assertEqual(task.status, TaskStatus.FAILED)
|
||||
# Abandoned reflection settles CANCELLED so it cannot pollute the
|
||||
# delivered run's terminal verdict (the error record keeps the why).
|
||||
self.assertEqual(failed.status, TaskStatus.CANCELLED)
|
||||
self.assertEqual(task.status, TaskStatus.CANCELLED)
|
||||
self.assertEqual(task.metadata["self_evolution_error"]["attempts"], 3)
|
||||
|
||||
async def test_self_evolution_work_item_retries_patch_for_wrong_employee(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user