3.7 KiB
R4.2 P0-A TaskPlane Scope Isolation Design
Status: approved candidate design. This document is not evidence of a protected-main merge or production deployment.
Boundary
- Task:
r42-p0a-taskplane-scope-isolation-20260816 - Governed Job:
job-3fd743b51971c6007923699c1233f84c - Repository:
aios/aios - Base:
0eb6e1a864edfdaeef37b438991707a110e21558 - Target branch:
agent/r42-p0a-taskplane-scope-isolation-20260816 - Generation:
1
The implementation reuses this exact boundary. It does not create a replacement Task, Job, branch, generation, or idempotency key.
Model and invariants
TaskPlan gains first-class scope_type, tenant_id, user_id, project_id, conversation_id, and run_id fields. The fields participate in accepted-key validation, parsing, serialization, persistence, and authorization.
Historical rows without scope data normalize to scope_type=legacy; no tenant is inferred. Tenant-scoped tasks require a non-empty tenant_id. System/global resources use an explicit scope_type=system invariant and do not receive a fabricated tenant. A task may add user, project, conversation, and run ownership within its tenant.
Trusted ActorContext
TaskPlane defines a small immutable TaskActorContext used only for authorization. The HTTP authenticator returns an identity. PolicyEngine resolves that identity through server-owned policy configuration into the effective context and capabilities. Request fields such as tenant_id, user_id, group_id, and role are declarations only and never expand authority.
Public create, list, and get paths always use the resolved context. Internal compatibility calls may omit a context and retain explicit legacy behavior, but the HTTP adapter never uses that path.
Persistence
SQLite remains the current runtime. _create_schema() performs an idempotent forward migration that adds the six scope columns and indexes, then backfills only missing scope_type values to legacy.
New writes persist scope in both the canonical task_json representation and first-class columns. Reads accept historical JSON without scope, using legacy columns, but reject a new-row mismatch between JSON and columns. SQL list/get predicates are parameterized and applied before row hydration.
PostgreSQL remains a separate migration target and is not claimed by this change.
Authorization flow
- HTTP bearer authentication yields an identity.
PolicyEngineverifies capability and resolves the identity toTaskActorContext.- Create binds the requested scope to the trusted context and rejects mismatches.
- List/get pass the trusted context to
TaskPlaneStore. - Store applies tenant and ownership predicates in SQL; privileged capabilities may explicitly cross scope.
Tenant actors cannot read legacy tasks, another tenant, or mismatched owned resources. Legacy actors can only read legacy tasks. System actors require explicit privileged capability for cross-scope access.
Failure behavior
Missing tenant scope, forged scope, unknown scope types, JSON/column divergence, and unauthorized list/get operations fail closed with canonical TaskPlane errors. Public get does not reveal whether an out-of-scope task exists.
Verification
Tests exercise real model round trips, SQLite migration/restart, Service/Policy/Store wiring, tenant A versus tenant B list/get denial, ownership filtering, forged create payload rejection, legacy isolation, privileged access, and SQL parameterization. Existing TaskPlane API, RBAC, recovery/security, and security-contract suites remain mandatory.
The candidate is complete only after real TASK_CLAIMED, TASK_HEARTBEAT, exact WriterLease, governed commit, OID convergence, result submission, and TASK_VERIFIED. Protected main and production remain unchanged.