diff --git a/docs/architecture/rfcs/harness-selection-dsh-pi-v0.md b/docs/architecture/rfcs/harness-selection-dsh-pi-v0.md index 23c5b704f..a5654c0a5 100644 --- a/docs/architecture/rfcs/harness-selection-dsh-pi-v0.md +++ b/docs/architecture/rfcs/harness-selection-dsh-pi-v0.md @@ -474,13 +474,13 @@ Validation: `tests/capabilities/test_steward_executor_machine_defaults.py`, `tests/capabilities/test_capability_configuration_ui.py`, and `examples/loopx-steward-channel-binding-smoke.py`. -## Steward Team Intake (planned, 2026-09-16) +## Steward Team Intake (2026-09-16) -The steward answers questions today, and since `2026-09-16` its shipped -guidance carries one bounded procedure for a different request: one owner -sentence that asks for a *team* rather than a task. That procedure is guidance, -not machine enforcement, so this section records where the enforced contract -belongs and what it must validate before any implementation lands. +The steward answers questions. Since `2026-09-16` its shipped guidance also +carries one bounded procedure for a different request: one owner sentence that +asks for a *team* rather than a task. This section records the enforced contract +for that intake, which part of it is already shipped, and which part is still +missing. The intake boundary is the canonical governed-proposal owner (`loopx/control_plane/work_items/governed_transition_proposal.py`), not a new @@ -492,37 +492,86 @@ beside the existing one. A command with no second caller, and a builder module with no caller at all, both stay out: this repository keeps an uncalled abstraction in design state until its call site exists. -The proposal payload is validated before anything may be applied, and it names: +A proposal of kind `steward_team_plan_preview` (`steward_team_plan_preview_v0`) +is validated before anything may be applied, and a validated preview names, and +may not invent: -- each lane and the Agent that runs it, resolved from Agents Core already - registers for the Goal; -- that lane's first bounded Todo, with its declared priority, task class and - action kind; -- the quota or cadence envelope that bounds the lanes; +- each lane and the Agent that runs it, resolved from the Agents Core already + registers for the Goal, at most 8 lanes; +- that lane's first bounded Todo, with its declared priority (P0..P3), task + class and action kind; +- the quota envelope that bounds the lanes; - the acceptance signal that ends each lane; - the stop condition that ends the team. -A requested lane that cannot be staffed is a typed gap naming the missing -registration or grant; it is never filled in by inventing an Agent, a Todo -capability, or a lane the machine cannot run. The plan is a preview: it creates -no Todo, registers no Agent, sets no quota, and spends none, and an owner's -confirmation of that exact preview is the only thing that admits an apply. -Apply routes to the canonical owners each effect already has -- Agent -registration, Todo creation, quota or goal policy -- reuses the identities the -preview named, and returns one readback of what exists. It may not widen the -confirmed scope, and a team plan is never settled as if the work were done. - -Delivery is two slices, in this order: - -1. **Preview slice (next).** The typed payload contract and its validator, with - focused tests, and no materializer registered, so a preview cannot apply - even by mistake. -2. **Apply slice.** A materializer for that kind, with its settlement phase and - readback, routed through the owners above. - -What this planned contract does not authorize: the steward still only proposes -and delegates; selecting a steward executor or storing a credential grants none -of these effects; and nothing here widens OS, provider, audience or work-state +A requested lane that cannot be staffed is a typed gap -- `agent_not_registered`, +`capability_not_granted` or `audience_not_authorized` -- and the gap keeps the +work it did not staff under `declined_first_todo`, so the owner sees what was +asked for and what is missing instead of a lane that was quietly filled in or +dropped. A lane that declares a gap may not declare work. The plan is a preview: +the validated payload carries `applies: false`, and an owner's confirmation of +that exact preview is the only thing that admits an apply. Apply routes to the +canonical owners each effect already has -- Agent registration, Todo creation, +quota or goal policy -- reuses the identities the preview named, may not widen +the confirmed scope, and a team plan is never settled as if the work were done. + +Shipped enforcement, in delivery order: + +1. **Contract and validator** (`#4519`, `3acd07697`). The kind, its schema, the + lane limit, the priority and gap vocabularies, public-safe text, and the + refusal to invent staffing. +2. **Chat admission** (`#4522`, `3c8c832cb`). `normalize_agent_response` admits a + preview only when the host supplies `team_plan_context` -- this Goal's + registered Agents and this host's supported advancement action kinds -- and + drops it otherwise, exactly like any other proposal it cannot accept, while + the answer text still reaches the owner. +3. **Apply** (`#4524`, `c159a15b3`). The governed transition owner dispatches the + kind at `PRE_SETTLEMENT`. The apply re-validates the proposal against the + Goal's registered Agents and the shipped advancement action kinds, creates + the first bounded Todo of each *ready* lane through the canonical Todo owner, + creates nothing for a gap lane, and refuses an unknown Goal before any write. + The receipt records the proposal digest, so a replayed settlement reuses the + same lane Todo instead of adding a second row. + +The intake is still inert in production, and this section does not claim +otherwise. Nothing yet supplies `team_plan_context`, so a model-authored preview +is dropped at admission instead of being surfaced for confirmation; the adapter +that supplies the admission facts and the settlement that re-derives them must +stay one contract rather than two; and the apply entry point today is a governed +capability execution journal, so a confirmed Chat preview needs that bridge +before an owner confirmation can materialize lanes. Two further gaps belong with +this work: the published receipt carries the first lane Todo's identity rather +than the identity of every lane it created (the apply result computes the full +`lane_todo_ids` set, and the receipt field set is closed and persisted, so +publishing it is a bounded compatibility change), and a multi-lane preview has +no frontend confirmation surface yet. + +### Relationship to the multi-agent contracts + +The intake is a user-layer affordance over the kernel the multi-agent contracts +already define; it adds no second team runtime. + +- Against `multi_agent_three_layer_minimality_contract_v0` + (`docs/reference/protocols/multi-agent-three-layer-minimality-v0.md`), the + owner's one sentence is the user layer, the steward's bounded procedure is the + preset layer, and lanes, first bounded Todos, quota envelope, acceptance and + stop condition are declared data the kernel mechanics consume. The intake must + not own a runner, panes, per-agent vision budgets or evidence loops; it + materializes goal work lanes through the canonical Todo owner, which is what + keeps a team request from becoming a product-specific runner. +- Against `multi_agent_visible_launcher_v0` + (`docs/reference/protocols/multi-agent-visible-launcher-v0.md`), the launcher + starts visible local panes from a `generic_multi_agent_launch_spec_v0`, and + the intake is the same intent entered from Chat. They join by identity + (`goal_id`, `agent_id`, and the lane's first Todo), not by one calling the + other, and the launcher's own rule applies unchanged to the intake: no leader + agent, hidden scheduler, promotion authority or second source of truth. A plan + that needs visible panes, pane-local A2A ticks or promotion evidence has to + name that as a supported action kind instead of embedding it in the preview. + +What this contract does not authorize: the steward still only proposes and +delegates; selecting a steward executor or storing a credential grants none of +these effects; and nothing here widens OS, provider, audience or work-state authority. ## Steward Channel Readiness by Milestone (2026-09-15) diff --git a/docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md b/docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md index eb26f0f0a..71fd00835 100644 --- a/docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md +++ b/docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md @@ -373,11 +373,11 @@ journal 与配额语义;B 作为上游接口出现时的低成本替代;只 `tests/capabilities/test_capability_configuration_ui.py`,以及 `examples/loopx-steward-channel-binding-smoke.py`。 -## 管家团队入端口径(规划中,2026-09-16) +## 管家团队入端口径(2026-09-16) -管家今天回答问题;自 2026-09-16 起,它的出厂指引里已带一段有界流程,用于另一类请求: -业主一句话要的是**团队**而不是单个任务。那段流程是指引,不是机器强制,因此本节记录被强制 -的契约属于哪里、以及实现落地前必须校验什么。 +管家负责回答问题。自 2026-09-16 起,它的出厂指引里还带了一段有界流程,用于另一类请求: +业主一句话要的是**团队**而不是单个任务。本节记录这条入端口径被强制的契约、其中已经落地的 +部分,以及仍然缺失的部分。 入端口径是既有的受治理提案所有者 (`loopx/control_plane/work_items/governed_transition_proposal.py`),不是新的 CLI 命令, @@ -386,27 +386,63 @@ Chat Turn 也早已把 `response.proposals` 投影成 `proposal.ready` 事件。 **一种新 kind 的提案**,而不是在既有路径旁再开一条入端口。没有第二个调用方的命令、以及 完全没有调用方的 builder 模块都不新增:本仓库要求未获调用的抽象先留在设计态。 -在允许任何落地之前先校验提案载荷,它必须点名: +kind 为 `steward_team_plan_preview`(`steward_team_plan_preview_v0`)的提案,在任何落地 +之前先被校验;校验通过的预览必须点名、且不得编造: -- 每条 lane 及其运行的 Agent,且只能来自 Core 已为该 Goal 注册的 Agent; -- 该 lane 的首个有界 Todo,含其声明优先级、task class 与 action kind; -- 约束这些 lane 的 quota 或节奏包络; +- 每条 lane 及其运行的 Agent,且只能来自 Core 已为该 Goal 注册的 Agent,最多 8 条 lane; +- 该 lane 的首个有界 Todo,含其声明优先级(P0..P3)、task class 与 action kind; +- 约束这些 lane 的 quota 包络; - 结束每条 lane 的验收信号; - 结束整个团队的终止条件。 -配不齐的 lane 是类型化的 gap(缺哪个注册或授予),不允许靠编造 Agent、Todo 能力或本机跑不动 -的 lane 来填。计划是**预览**:不建 Todo、不注册 Agent、不设 quota、不扣额度;只有业主对这 -份确切预览的确认,才允许进入落地。落地只经各 effect 既有的 canonical owner——Agent 注册、 -Todo 创建、quota 或 goal policy——复用预览点名的身份,并返回一份"现在存在什么"的回读;不得 -扩大已确认范围,也不得把团队计划当作工作已完成的结算。 - -按此顺序分两片交付: - -1. **预览片(下一步)**:类型化载荷契约与其校验器,配聚焦测试,且不注册 materializer, - 使预览即使被误用也无法落地。 -2. **落地片**:该 kind 的 materializer,含其结算相位与回读,并按上文经既有 owner 路由。 - -这条规划契约不授权什么:管家仍然只提议与委托;选择管家执行器或存凭据都不带来这些 effect; +配不齐的 lane 是类型化的 gap——`agent_not_registered`、`capability_not_granted` 或 +`audience_not_authorized`——并且该 gap 会把没配上人的那份工作留在 `declined_first_todo` +里,让业主看到"要了什么、缺了什么",而不是一条被悄悄填上或被丢掉的 lane;声明 gap 的 +lane 不得再声明工作。计划是**预览**:校验通过的载荷带 `applies: false`;只有业主对这份确切 +预览的确认,才允许进入落地。落地只经各 effect 既有的 canonical owner——Agent 注册、 +Todo 创建、quota 或 goal policy——复用预览点名的身份,不得扩大已确认范围,也不得把团队计划 +当作工作已完成的结算。 + +按交付顺序,已经落地并受强制的部分: + +1. **契约与校验器**(`#4519`,`3acd07697`):kind、schema、lane 上限、优先级与 gap 词表、 + 公开安全文本,以及"拒绝编造 staffing"的行为。 +2. **聊天准入**(`#4522`,`3c8c832cb`):`normalize_agent_response` 只在宿主提供 + `team_plan_context`(本 Goal 已注册 Agent + 本机支持的 advancement action kind)时才让 + 预览通过;否则与其它无法接受的提案一样被丢弃,而答案正文仍然到达业主。 +3. **落地**(`#4524`,`c159a15b3`):受治理提案所有者在 `PRE_SETTLEMENT` 相位分派该 kind, + 落地时重新按本 Goal 已注册 Agent 与本机 shipment 的 advancement action kind 校验,经 + canonical Todo owner 为每条 **ready** lane 创建首个有界 Todo,gap lane 不创建任何东西, + 未知 Goal 在任何写入前就被拒绝,回执记录 proposal digest,因此重放结算复用同一条 lane + Todo 而不会新增第二行。 + +这条入端口径目前在线上仍是**惰性**的,本节不作相反声明:还没有任何生产调用方传入 +`team_plan_context`,因此模型产出的预览会在准入处被丢弃,而不会浮现给业主确认;提供准入事实 +的适配器与重新推导这些事实的结算必须保持同一份契约而不是两份;而今天的落地入口是受治理能力 +执行 journal,所以被确认的 Chat 预览还需要那座桥,业主确认才能真正建成 lane。另有两处缺口 +属于这条工作线:已发布回执只带第一条 lane Todo 的身份,而不是它创建的全部 lane 身份(apply +结果里算了完整的 `lane_todo_ids`,但回执字段集是封闭且持久化的,发布它是一次有界的兼容性 +变更);以及多 lane 预览还没有前端确认面。 + +### 与 multi-agent 契约的关系 + +这条入端口径是既有 multi-agent 契约所定义内核之上的**用户层**便利:它不新增第二套团队 +runtime。 + +- 对应 `multi_agent_three_layer_minimality_contract_v0` + (`docs/reference/protocols/multi-agent-three-layer-minimality-v0.md`):业主那一句话是用户 + 层,管家那段有界流程是 preset 层,而 lanes、首个有界 Todo、quota 包络、验收与终止条件是 + 内核机制消费的声明数据。入端口径不得拥有 runner、pane、per-agent vision 预算或证据回路; + 它只经 canonical Todo owner 建出 Goal 工作 lane,这正是它不会变成产品专用 runner 的原因。 +- 对应 `multi_agent_visible_launcher_v0` + (`docs/reference/protocols/multi-agent-visible-launcher-v0.md`):launcher 从 + `generic_multi_agent_launch_spec_v0` 启动可见本地 pane,而这条入端口径是同一意图从 Chat + 进入。两者按身份相连(`goal_id`、`agent_id` 与该 lane 的首个 Todo),而不是互相调用; + launcher 自身那条规则对入端口径同样成立:不得成为 leader agent、隐藏调度器、晋升权威或 + 第二真源。需要可见 pane、pane 内 A2A tick 或晋升证据的计划,必须把它声明为受支持的 + action kind,而不是塞进预览里。 + +这条契约不授权什么:管家仍然只提议与委托;选择管家执行器或存凭据都不带来这些 effect; 这里也不会扩大 OS、provider、受众或工作状态权限。 ## 按里程碑看管家通道的就绪度(2026-09-15) diff --git a/loopx/control_plane/work_items/governed_transition_proposal.py b/loopx/control_plane/work_items/governed_transition_proposal.py index c9eb454cc..926a69175 100644 --- a/loopx/control_plane/work_items/governed_transition_proposal.py +++ b/loopx/control_plane/work_items/governed_transition_proposal.py @@ -448,11 +448,14 @@ def validate_steward_team_plan_preview( ) -> dict[str, Any]: """Validate one steward team preview, and refuse to invent its staffing. - The preview is the whole effect of this kind: it creates nothing, so it has - no materializer and no settlement phase. A lane whose Agent this Goal does - not register becomes a typed gap that keeps the work it did *not* staff - under ``declined_first_todo``, so the owner sees what was asked for and what - is missing instead of a lane that was quietly filled in or dropped. + Validation is the same contract at both ends: the Chat admission uses it to + decide whether a preview may be surfaced for confirmation, and the + ``PRE_SETTLEMENT`` apply of that kind calls it again with the host's own + facts before it creates anything, so a proposal cannot become work by + bypassing admission. A lane whose Agent this Goal does not register becomes + a typed gap that keeps the work it did *not* staff under + ``declined_first_todo``, so the owner sees what was asked for and what is + missing instead of a lane that was quietly filled in or dropped. """ plan = _mapping(payload, "steward_team_plan_preview")