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
+19
View File
@@ -9,6 +9,10 @@ background job's.
The one hard requirement is **idempotency**. A cron job that runs twice for the
same period must not bill twice, so a period that already has an invoice is
skipped rather than re-created.
The same pass also expires memory lessons past their date (``expire_memories``).
That is housekeeping, not billing, but this is the one scheduled job every
deployment runs, and expiry needs no model provider.
"""
from __future__ import annotations
@@ -20,6 +24,7 @@ from app.domain.billing.models import Invoice, InvoiceStatus
from app.infrastructure.repositories.audit import UsageLedgerRepository
from app.infrastructure.repositories.billing import InvoiceRepository
from app.infrastructure.repositories.identity import OrganizationRepository
from app.infrastructure.repositories.memory import MemoryRepository
from app.workers.base import WorkerOutcome, worker_session
#: A period shorter than this is almost certainly a bug (a stray manual run),
@@ -99,6 +104,20 @@ class BillingWorker:
return outcome
async def expire_memories(self) -> WorkerOutcome:
"""Soft-delete organization lessons whose expiry has passed.
Pinned entries never expire, and rules and preferences carry no expiry at
all, so only disposable lessons are touched. The sweep covers every
organization at once, which is what a platform-wide schedule wants.
"""
outcome = WorkerOutcome(name=self.name)
async with worker_session() as session:
expired = await MemoryRepository(session).expire(None)
outcome.processed = expired
outcome.note(f"memory: {expired} expired lesson(s) soft-deleted")
return outcome
@staticmethod
def _naive_utc(value: datetime) -> datetime:
"""Normalise a timestamp to naive UTC for comparison.