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
@@ -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))
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user