feat(integration): wire v2 workstreams together (engine policy/budget, sandbox, tenant checks, order route, docs)
Engine honours org policy and monthly budget, legacy runner skips engine tasks, sandbox session provisioned on demand, project fallback for threads, reviewer fallback, tenant checks on project ids, memory reflection respects grants, expiry worker, MCP pipeline_status, login redirect, sharing settings, e2e smoke. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
36564e3f2b
commit
fef5a9deab
@@ -302,11 +302,12 @@ class ManagerTurns:
|
||||
raise ValidationError("این نقش قابل استخدام نیست.")
|
||||
if not tier:
|
||||
raise ValidationError("سطح کارمند را انتخاب کنید.")
|
||||
# The owner's per-org policy may lower the global cap, never raise it.
|
||||
policy = await self.grants.get_org_policy(organization_id)
|
||||
cap = min(settings.engine_max_employees, policy.max_employees)
|
||||
count = await self.employees.count_for_org(organization_id)
|
||||
if count >= settings.engine_max_employees:
|
||||
raise ConflictError(
|
||||
f"سقف تعداد کارمندان ({settings.engine_max_employees}) پر شده است."
|
||||
)
|
||||
if count >= cap:
|
||||
raise ConflictError(f"سقف تعداد کارمندان ({cap}) پر شده است.")
|
||||
granted = await self.grants.granted_models(organization_id=organization_id)
|
||||
# A hire is an explicit request for a tier: no silent downgrade, or the
|
||||
# manager would think it hired a strong employee and got a fast one.
|
||||
@@ -529,12 +530,14 @@ class ManagerTurns:
|
||||
if draft.kind in {k.value for k in TaskKind}
|
||||
else TaskKind.FEATURE
|
||||
)
|
||||
# A thread made by the web UI has no project; the org's project is the fallback.
|
||||
project_id = thread.project_id or await self._project_id_for(organization_id)
|
||||
task = await self.tasks.create(
|
||||
Task(
|
||||
organization_id=organization_id,
|
||||
title=draft.title[:200],
|
||||
description=draft.description,
|
||||
project_id=thread.project_id,
|
||||
project_id=project_id,
|
||||
thread_id=thread.id,
|
||||
created_by_user_id=user_id,
|
||||
)
|
||||
@@ -544,9 +547,11 @@ class ManagerTurns:
|
||||
TaskPipelineState(
|
||||
task_id=task.id,
|
||||
organization_id=organization_id,
|
||||
project_id=thread.project_id,
|
||||
project_id=project_id,
|
||||
pipeline=resolve_pipeline(
|
||||
manager_choice=draft.pipeline or None, task_kind=kind
|
||||
manager_choice=draft.pipeline or None,
|
||||
task_kind=kind,
|
||||
has_active_reviewer=await self._has_active_reviewer(organization_id),
|
||||
),
|
||||
kind=kind,
|
||||
risk=Risk.MEDIUM,
|
||||
@@ -719,6 +724,7 @@ class ManagerTurns:
|
||||
pipeline = resolve_pipeline(
|
||||
manager_choice=assign.pipeline or None,
|
||||
task_kind=state.kind if state else None,
|
||||
has_active_reviewer=await self._has_active_reviewer(task.organization_id),
|
||||
)
|
||||
first_stage = (
|
||||
Stage.PENDING_ANALYSIS
|
||||
@@ -1067,6 +1073,13 @@ class ManagerTurns:
|
||||
return employee.project_id
|
||||
return None
|
||||
|
||||
async def _has_active_reviewer(self, organization_id: str) -> bool:
|
||||
"""A review stage needs an active reviewer to act on it, or the task dead-ends."""
|
||||
return any(
|
||||
e.role_key is RoleKey.REVIEWER and e.is_available
|
||||
for e in await self.employees.list_for_org(organization_id)
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"BOOTSTRAP_TIERS",
|
||||
|
||||
Reference in New Issue
Block a user