fix(approvals): accept top-level lead reasoning for create

This commit is contained in:
Abhimanyu Saharan
2026-02-13 11:48:32 +00:00
parent 3a15a2759e
commit 55fc9f8fe8
3 changed files with 26 additions and 3 deletions

View File

@@ -394,10 +394,13 @@ async def create_approval(
_actor: ActorContext = ACTOR_DEP, _actor: ActorContext = ACTOR_DEP,
) -> ApprovalRead: ) -> ApprovalRead:
"""Create an approval for a board.""" """Create an approval for a board."""
payload_dict = payload.payload
if payload_dict is None and isinstance(payload.lead_reasoning, str) and payload.lead_reasoning.strip():
payload_dict = {"reason": payload.lead_reasoning.strip()}
task_ids = normalize_task_ids( task_ids = normalize_task_ids(
task_id=payload.task_id, task_id=payload.task_id,
task_ids=payload.task_ids, task_ids=payload.task_ids,
payload=payload.payload, payload=payload_dict,
) )
task_id = task_ids[0] if task_ids else None task_id = task_ids[0] if task_ids else None
if payload.status == "pending": if payload.status == "pending":
@@ -411,7 +414,7 @@ async def create_approval(
task_id=task_id, task_id=task_id,
agent_id=payload.agent_id, agent_id=payload.agent_id,
action_type=payload.action_type, action_type=payload.action_type,
payload=payload.payload, payload=payload_dict,
confidence=payload.confidence, confidence=payload.confidence,
rubric_scores=payload.rubric_scores, rubric_scores=payload.rubric_scores,
status=payload.status, status=payload.status,

View File

@@ -6,7 +6,7 @@ from datetime import datetime
from typing import Literal, Self from typing import Literal, Self
from uuid import UUID from uuid import UUID
from pydantic import model_validator from pydantic import AliasChoices, Field as PydanticField, model_validator
from sqlmodel import Field, SQLModel from sqlmodel import Field, SQLModel
ApprovalStatus = Literal["pending", "approved", "rejected"] ApprovalStatus = Literal["pending", "approved", "rejected"]
@@ -49,9 +49,18 @@ class ApprovalCreate(ApprovalBase):
agent_id: UUID | None = None agent_id: UUID | None = None
# Back-compat + ergonomics: some clients send lead reasoning as a top-level
# field (`reasoning` / `lead_reasoning`) rather than nesting under payload.reason.
lead_reasoning: str | None = PydanticField(
default=None,
validation_alias=AliasChoices("lead_reasoning", "reasoning", "leadReasoning"),
)
@model_validator(mode="after") @model_validator(mode="after")
def validate_lead_reasoning(self) -> Self: def validate_lead_reasoning(self) -> Self:
"""Ensure each approval request includes explicit lead reasoning.""" """Ensure each approval request includes explicit lead reasoning."""
if isinstance(self.lead_reasoning, str) and self.lead_reasoning.strip():
return self
payload = self.payload payload = self.payload
if isinstance(payload, dict): if isinstance(payload, dict):
reason = payload.get("reason") reason = payload.get("reason")

View File

@@ -38,6 +38,17 @@ def test_approval_create_requires_lead_reasoning() -> None:
) )
def test_approval_create_accepts_top_level_reasoning_alias() -> None:
model = ApprovalCreate.model_validate(
{
"action_type": "task.update",
"confidence": 80,
"reasoning": "Lead says OK.",
},
)
assert model.lead_reasoning == "Lead says OK."
def test_approval_create_accepts_nested_decision_reason() -> None: def test_approval_create_accepts_nested_decision_reason() -> None:
model = ApprovalCreate.model_validate( model = ApprovalCreate.model_validate(
{ {