Files
aios/docs/task-plane/README.md
T
AIOS Governed Continuation 0e9c43c785 feat(task_plane): enumeration=cross-tenant skip+audit, direct claim remains fail-closed (G4)
Contract: enumeration of cross-tenant tasks must SKIP + record a structured
audit finding (never fail closed); direct claim by task_id stays 403
cross_tenant_denied.

- store: add tenant_skip_audit table + record_tenant_skip_audit(task_id,
  worker_id, task_tenant, worker_tenant, reason) persisting
  {source:tenant, kind:cross_tenant_skip, decision:skip+audit, task_tenant,
  worker_tenant} and appending TASK_TENANT_SKIPPED event; list_tenant_skip_audit
  read-back for testability.
- scheduler: TaskScheduler gains tenant_of_task/worker; next_queued_task skips +
  audits cross-tenant candidates (worker tenant != task tenant) instead of
  selecting them.
- schedule_service: SchedulingService plumbs tenant fns into scheduler;
  schedule/queued_count excludes cross-tenant with skip+audit; claim_governed
  adds fail-closed 403 cross_tenant_denied guard on direct claim (depth defense).
- tests: split test_a23_cross_tenant_denied into enumeration skip+audit,
  same-tenant preference, and direct-claim fail-closed.
- docs: task-plane README cross-tenant enumeration=skip+audit vs direct=fail-closed.

Verified: tests/task_plane + tests/baseline_v1_1/test_auto_claim_r2 -> 157 passed
/ 2 pre-existing failures; full task_plane+baseline_v1_1 -> 246 passed / 29
failed (failure set byte-identical to pre-existing G3 set; no new failures).
2026-09-14 19:12:06 +08:00

3.8 KiB
Raw Blame History

AIOS Task Plane

该模块是与任务发起方、Worker 类型和通知后端解耦的持久化任务控制平面。当前交付仅用于隔离开发与 Shadow 验收;默认 execution_enabled=false,不会修改生产、tag、Baseline 或 Registry。

架构

  • TaskPlaneService:统一服务边界和 HTTP 路由。
  • TaskPlaneStore:SQLite WAL、revision/CAS、串行事务、租约、hash-chained journal、evidence 和 durable outbox。
  • WorkerRegistry / LeaseManager:身份绑定、capability、并发额度、心跳、过期探测后恢复。
  • DelegationEngine:父子 DAG、全局深度、capability/risk/approval/budget 单调约束和幂等重放。
  • PolicyEngine:默认关闭执行;身份、证据、结果摘要和可插拔签名验签均 fail-closed。
  • WorkerAdapter:OpenClaw、Codex、Hermes、A2A 和 systemd Worker 的统一契约。默认实现只返回 SHADOW_VERIFIED,不执行外部命令。
  • DurableOutboxDispatcher:Webhook、NATS JetStream、GPT callback、Dashboard 和 WeChat 的回执式通知接口。没有真实回执时始终保留 NOT_DELIVERED。

状态与恢复

正常路径为 CREATED → QUEUED/CLAIMED → RUNNING → VERIFYING/COMPLETED;失败路径使用 FAILED_RECOVERABLE、FAILED_FINAL、CANCELLED 或 MANUAL_REVIEW_REQUIRED。每次状态事件都进入同一事务中的 append-only hash chain。

租约过期不会自动重放副作用。控制平面必须先调用 Worker probe,只有明确确认原执行已停止,才可将 claim 标记为 EXPIRED 并把任务转为 FAILED_RECOVERABLE。结果必须匹配 task、claim、Worker identity、未过期 lease 和 operation digest;重复结果返回冲突。

安全模型

  • task/worker/claim/message ID 使用 allowlist pattern,拒绝 URL 编码差异、路径分隔符和控制字符。
  • HTTP body 最大 1 MB,非对象 JSON、异常 Content-Length 和非法参数返回 400/413。
  • 内部异常响应不回显路径、payload 或凭据;默认 HTTP access log 不记录原始 request target。
  • secret-like 字段在 durable task、event、evidence 和 outbox 写入前脱敏。
  • 结果签名存在但没有配置验签器时拒绝;摘要、evidence 或 event 被篡改时验收失败。
  • SQLite 文件创建为 0600;新建状态目录使用 0700。

租户语义(cross-tenant)

任务与 Worker 各带租户标识:任务取自 task_plan.metadata.tenant_id(默认 default), Worker 取自 trust_zone(默认 default)。跨租户(cross-tenant)处理语义为:

  • 枚举 = skip + 审计:poll / long_poll / schedule(queued_count)在遍历候选任务时, 对与 Worker 租户不一致的任务执行 SKIP,写入结构化审计 finding {source:"tenant", kind:"cross_tenant_skip", decision:"skip+audit", task_tenant, worker_tenant} (持久化于 tenant_skip_audit 表并在任务事件链追加 TASK_TENANT_SKIPPED),不抛异常、不 fail-closed。 仅当存在同租户候选时才会被选中。
  • 直接读/认领 = fail-closed:按 task_id 直接 governed_claim / claim_governed 跨租户任务时 保持拒绝,返回 403 cross_tenant_denied,不泄露任务内容。

即:Worker 只能“看到”并认领同租户任务;枚举路径让跨租户任务不可见且留有审计,直接认领路径则明确失败。

验收

PYTHONPATH=active-runtime \
  bash scripts/task-plane/run_task_plane_acceptance.sh

PYTHONPATH=active-runtime \
  python3 scripts/task-plane/create_shadow_tasks.py \
    --database reports/task-plane-shadow.sqlite3 \
    --report reports/task-plane-shadow.json

上述脚本只使用本地 SQLite 和 Shadow adapter,不触发真实 OpenClaw、Codex、Hermes、微信或生产执行。