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:
mjalmoudy94
2026-10-14 20:57:56 +03:30
co-authored by Claude Sonnet 5.5
parent 36564e3f2b
commit fef5a9deab
43 changed files with 3024 additions and 221 deletions
@@ -14,6 +14,8 @@ cannot both win — the loser sees zero updated rows and gets a
from __future__ import annotations
from datetime import datetime
from sqlalchemy import func, select, update
from sqlalchemy.ext.asyncio import AsyncSession
@@ -489,10 +491,11 @@ class GateResultRepository:
class TaskSpendRepository:
"""Per-task spend, read from the usage ledger (never written here).
"""Spend, read from the usage ledger (never written here).
The scheduler's budget guard needs "how much has this task cost so far",
which the ledger can answer by joining its events to the task's runs. The
The scheduler's budget guards need "how much has this task cost so far"
(joining the ledger's events to the task's runs) and "how much has this
organization spent since a date" (the owner's monthly credit budget). The
ledger stays the source of truth; this only reads it.
"""
@@ -505,3 +508,11 @@ class TaskSpendRepository:
UsageEventRow.run_id.in_(run_ids)
)
return float((await self.session.execute(stmt)).scalar_one())
async def spend_for_org_since(self, organization_id: str, since: datetime) -> float:
"""What the organization's usage has cost since ``since``, over all its tasks."""
stmt = select(func.coalesce(func.sum(UsageEventRow.total_price), 0.0)).where(
UsageEventRow.organization_id == organization_id,
UsageEventRow.created_at >= since,
)
return float((await self.session.execute(stmt)).scalar_one())
@@ -7,7 +7,7 @@ tool calls -> artifacts.
from __future__ import annotations
from sqlalchemy import func, select
from sqlalchemy import exists, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.domain.workforce.models import (
@@ -42,6 +42,7 @@ from app.infrastructure.db.models import (
ThreadRow,
ToolCallRow,
)
from app.infrastructure.db.models_engine import TaskPipelineStateRow
# ---------------------------------------------------------------------------
# Mappers
@@ -200,8 +201,10 @@ class EmployeeRepository:
return [_employee(r) for r in (await self.session.execute(stmt)).scalars()]
async def count_for_org(self, organization_id: str) -> int:
"""The headcount the cap counts: everyone on the team except the fired (archived)."""
stmt = select(func.count()).select_from(EmployeeRow).where(
EmployeeRow.organization_id == organization_id
EmployeeRow.organization_id == organization_id,
EmployeeRow.status != str(EmployeeStatus.ARCHIVED),
)
return int((await self.session.execute(stmt)).scalar_one())
@@ -447,10 +450,18 @@ class TaskRepository:
Cross-tenant by design: the worker is a platform component, not a
tenant. Only ``new``/``triaged`` qualify — anything else is either
already executing or closed, and re-running it would double-bill.
A task that owns a ``task_pipeline_state`` row belongs to
``Scheduler.tick()``. Running it here would double-bill it and mark it
``done`` behind the engine's back, so those tasks are excluded.
"""
has_pipeline_state = exists(
select(TaskPipelineStateRow.task_id).where(TaskPipelineStateRow.task_id == TaskRow.id)
)
stmt = (
select(TaskRow)
.where(TaskRow.status.in_([str(TaskStatus.NEW), str(TaskStatus.TRIAGED)]))
.where(~has_pipeline_state)
.order_by(TaskRow.created_at.asc())
.limit(min(limit, 100))
)