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:
@@ -58,12 +58,14 @@ DEBUG_ADMIN_TOOL_NAMES: tuple[str, ...] = (
|
||||
|
||||
MEETING_RESPONSE_TOOL_NAMES: tuple[str, ...] = ("respond_meeting",)
|
||||
HUMAN_REVIEW_TOOL_NAMES: tuple[str, ...] = ("close_human_review",)
|
||||
SELF_EVOLUTION_TOOL_NAMES: tuple[str, ...] = ("submit_self_evolution_patches",)
|
||||
|
||||
|
||||
COMPANY_COLLABORATION_TOOL_NAMES: tuple[str, ...] = (
|
||||
*COORDINATOR_DEFAULT_TOOL_NAMES,
|
||||
*MEETING_RESPONSE_TOOL_NAMES,
|
||||
*HUMAN_REVIEW_TOOL_NAMES,
|
||||
*SELF_EVOLUTION_TOOL_NAMES,
|
||||
)
|
||||
|
||||
COMPANY_DEBUG_TOOL_NAMES: tuple[str, ...] = DEBUG_ADMIN_TOOL_NAMES
|
||||
@@ -166,6 +168,13 @@ def _is_execute_or_review_work_item(task: object | None) -> bool:
|
||||
return turn_type in {"execute", "review"}
|
||||
|
||||
|
||||
def is_self_evolution_work_item(task: object | None) -> bool:
|
||||
metadata = _task_metadata(task)
|
||||
if _work_item_turn_type(metadata) == "self_evolution":
|
||||
return True
|
||||
return bool(metadata.get("self_evolution_work_item", False))
|
||||
|
||||
|
||||
def _has_active_meeting(task: object | None, runtime_state: dict[str, Any]) -> bool:
|
||||
metadata = _task_metadata(task)
|
||||
peer_wait = dict(metadata.get("peer_wait", {}) or {})
|
||||
@@ -306,6 +315,8 @@ def resolve_allowed_collaboration_tools(
|
||||
allowed.update(MEETING_RESPONSE_TOOL_NAMES)
|
||||
if _human_review_close_allowed(task, state):
|
||||
allowed.update(HUMAN_REVIEW_TOOL_NAMES)
|
||||
if is_self_evolution_work_item(task):
|
||||
allowed.update(SELF_EVOLUTION_TOOL_NAMES)
|
||||
return allowed
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user