diff --git a/docs/features/client-headless-event-envelope-v1.md b/docs/features/archive/client-headless-event-envelope-v1.md similarity index 98% rename from docs/features/client-headless-event-envelope-v1.md rename to docs/features/archive/client-headless-event-envelope-v1.md index 09c39a2e..049e02cb 100644 --- a/docs/features/client-headless-event-envelope-v1.md +++ b/docs/features/archive/client-headless-event-envelope-v1.md @@ -5,7 +5,7 @@ topics: ["client-cli", "host-orchestration", "headless-protocol", "issue-135"] todo_ids: ["CCLI-003", "CCLI-004"] created: 2026-03-02 updated: 2026-03-02 -status: active +status: archived mode: openspec --- @@ -79,3 +79,4 @@ mode: openspec - Slice B implementation PR: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/145` - Review thread fixed by this follow-up: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/145#discussion_r2871270479` - Related owner comment acknowledged after fix: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/145#issuecomment-3983060182` +- Archive target: `openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/` diff --git a/docs/features/client-host-orchestration-doc-baseline.md b/docs/features/archive/client-host-orchestration-doc-baseline.md similarity index 96% rename from docs/features/client-host-orchestration-doc-baseline.md rename to docs/features/archive/client-host-orchestration-doc-baseline.md index b6cd0915..d1a1f006 100644 --- a/docs/features/client-host-orchestration-doc-baseline.md +++ b/docs/features/archive/client-host-orchestration-doc-baseline.md @@ -5,7 +5,7 @@ topics: ["client-cli", "host-orchestration", "headless-protocol", "issue-135"] todo_ids: ["CCLI-001", "CCLI-002"] created: 2026-03-02 updated: 2026-03-02 -status: active +status: archived mode: openspec --- @@ -60,3 +60,4 @@ mode: openspec ### Review and Merge Gate Links - Intent PR: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/141` +- Archive target: `openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/` diff --git a/docs/features/client-external-control-plane-v1.md b/docs/features/client-external-control-plane-v1.md new file mode 100644 index 00000000..256cc8ab --- /dev/null +++ b/docs/features/client-external-control-plane-v1.md @@ -0,0 +1,64 @@ +--- +change_ids: ["client-external-control-plane-v1"] +doc_kind: feature +topics: ["client-cli", "host-orchestration", "control-plane", "issue-135"] +todo_ids: ["CCLI-005", "CCLI-006"] +created: 2026-03-02 +updated: 2026-03-02 +status: active +mode: openspec +--- + +# Feature: client-external-control-plane-v1 + +## Scope + +落实 Issue #135 的 Slice C:为 `client/` 增加外部结构化 control plane v1,优先采用 `--control-stdin` 作为本地宿主入口,并覆盖审批、MCP、skills 与 session status 的最小控制面。 + +## OpenSpec Artifacts + +- Proposal: `openspec/changes/client-external-control-plane-v1/proposal.md` +- Design: `openspec/changes/client-external-control-plane-v1/design.md` +- Specs: + - `openspec/changes/client-external-control-plane-v1/specs/client-host-orchestration/spec.md` +- Tasks: `openspec/changes/client-external-control-plane-v1/tasks.md` + +## TODO Coverage + +- `CCLI-005` +- `CCLI-006` + +## Evidence + +### Commands + +- `git fetch origin` +- `git worktree add .worktrees/client-external-control-plane-v1 -b codex/client-external-control-plane-v1 origin/main` +- `openspec list` +- `openspec validate client-external-control-plane-v1 --type change --strict --json --no-interactive` +- `./scripts/ci/check_governance_evidence_truth.sh` + +### Results + +- `git fetch origin`: confirmed `origin/main` includes merged Slice B via PR `#145`. +- `git worktree add .worktrees/client-external-control-plane-v1 -b codex/client-external-control-plane-v1 origin/main`: created an isolated Slice C workspace from `origin/main` commit `bc39bc0`. +- `openspec list`: confirms Slice A / Slice B have been archived out of the active change list, and the new Slice C kickoff change is recognized as `0/7 tasks`. +- `openspec validate client-external-control-plane-v1 --type change --strict --json --no-interactive`: passed (`1/1` change valid, `0` issues). +- `./scripts/ci/check_governance_evidence_truth.sh`: passed after the Slice C feature evidence block and prior-slice archive moves were synchronized. + +### Behavior Verification + +- Happy path: the planned Slice C contract narrows v1 external control to `--control-stdin`, preserving the landed headless event envelope from Slice B as the read-only observation channel. +- Error branch: Slice C keeps unknown action ids and unsupported MCP operations on the structured error path; it does not permit fallback to prompt text or undocumented CLI-only verbs such as `mcp:unload`. + +### Risks and Rollback + +- Risk: adding a second stdin consumer beside `chat` / `script` can create framing ambiguity if control and prompt input are not cleanly separated. +- Rollback: drop the Slice C kickoff change and keep the repository at the merged Slice B baseline where headless remains read-only. + +### Review and Merge Gate Links + +- Slice A intent gate (merged): `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/141` +- Slice B implementation gate (merged): `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/145` +- Slice C docs-only intent PR: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/148` +- Slice C spec-fold review thread: `https://github.com/zts212653/Deterministic-Agent-Runtime-Engine/pull/148#discussion_r2872038646` diff --git a/docs/todos/2026-03-02_client_cli_host_orchestration_master_todo.md b/docs/todos/2026-03-02_client_cli_host_orchestration_master_todo.md index cf03ec5a..8d8b7513 100644 --- a/docs/todos/2026-03-02_client_cli_host_orchestration_master_todo.md +++ b/docs/todos/2026-03-02_client_cli_host_orchestration_master_todo.md @@ -16,13 +16,14 @@ mode: openspec ## 认领声明(Claim Ledger) -> 当前状态:Slice A 的 docs-only intent PR 已于 2026-03-02 合入 `main`。 -> Slice B 的实现与验证已经在 `codex/client-headless-event-envelope-v1` 完成,当前待提交/评审;Slice C/D 尚未开始。 +> 当前状态:Slice A / Slice B 已于 2026-03-02 完成并合入 `main`,当前进入 Slice C kickoff。 +> Slice C 负责外部 control plane v1;Slice D 继续承担 capability discovery 与宿主级回归测试。 | Claim ID | TODO Scope | Owner | Status | Declared At | Expires At | OpenSpec Change | Notes | |---|---|---|---|---|---|---|---| -| CLM-20260302-CCLI-A | CCLI-001~CCLI-002 | bouillipx | done | 2026-03-02 | 2026-03-09 | `client-host-orchestration-doc-baseline` | Slice A: docs baseline、OpenSpec artifacts 与 intent PR payload 已经随 PR `#141` 合入 `main`。 | -| CLM-20260302-CCLI-B | CCLI-003~CCLI-004 | bouillipx | active | 2026-03-02 | 2026-03-09 | `client-headless-event-envelope-v1` | Slice B: 显式 headless 模式、versioned event envelope v1、相关测试与执行证据。 | +| CLM-20260302-CCLI-A | CCLI-001~CCLI-002 | bouillipx | done | 2026-03-02 | 2026-03-09 | `client-host-orchestration-doc-baseline` | Slice A: docs baseline 已随 PR `#141` 合入 `main`,待归档到 `openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/`。 | +| CLM-20260302-CCLI-B | CCLI-003~CCLI-004 | bouillipx | done | 2026-03-02 | 2026-03-09 | `client-headless-event-envelope-v1` | Slice B: headless event envelope v1 已随 PR `#145` 合入 `main`,待归档到 `openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/`。 | +| CLM-20260302-CCLI-C | CCLI-005~CCLI-006 | bouillipx | active | 2026-03-02 | 2026-03-09 | `client-external-control-plane-v1` | Slice C: 建立 `--control-stdin` v1 基线、MCP/approval/status/skills 外部控制 contract 与 docs-only intent PR payload。 | ## 切片规划 @@ -37,12 +38,12 @@ mode: openspec | ID | Priority | Status | Gap ID | Planned OpenSpec Change | Task | Owner | Evidence | Last Updated | |---|---|---|---|---|---|---|---|---| -| CCLI-001 | P0 | done | CCLI-GAP-006 | `client-host-orchestration-doc-baseline` | 更新 `client/DESIGN.md`,新增“宿主编排 / headless / control plane / capability discovery / 错误语义”章节,作为后续实现唯一设计输入。 | bouillipx | `client/DESIGN.md`;`openspec/changes/client-host-orchestration-doc-baseline/design.md` | 2026-03-02 | -| CCLI-002 | P0 | done | CCLI-GAP-006 | `client-host-orchestration-doc-baseline` | 更新 `client/README.md`,明确当前 `--output json` 是 legacy automation schema,补充后续宿主协议模式的兼容说明。 | bouillipx | `client/README.md`;`docs/features/client-host-orchestration-doc-baseline.md` | 2026-03-02 | -| CCLI-003 | P1 | done | CCLI-GAP-001 | `client-headless-event-envelope-v1` | 为 `client` 设计并实现显式 headless 模式,定义禁止 prompt / 禁止内联审批 / 只输出协议帧的行为边界。 | bouillipx | `client/main.py`;`client/session.py`;`tests/integration/test_client_cli_flow.py`;`docs/features/client-headless-event-envelope-v1.md` | 2026-03-02 | -| CCLI-004 | P1 | done | CCLI-GAP-002 | `client-headless-event-envelope-v1` | 设计并实现 versioned event envelope(至少含 `schema_version`、`run_id`、`seq`、`event`、`data`),并定义与现有 JSON 输出的兼容策略。 | bouillipx | `client/render/headless.py`;`tests/unit/test_client_cli.py`;`tests/integration/test_client_cli_flow.py`;`docs/features/client-headless-event-envelope-v1.md` | 2026-03-02 | -| CCLI-005 | P1 | todo | CCLI-GAP-003 | `client-external-control-plane-v1` | 设计外部控制协议入口(如 `control-stdin` 或 loopback RPC),覆盖 approvals / MCP / skills / status 的结构化控制。 | TBD | `client/main.py`;`client/runtime/action_client.py`;相关 OpenSpec design/specs/tasks | 2026-03-02 | -| CCLI-006 | P2 | todo | CCLI-GAP-005 | `client-external-control-plane-v1` | 将当前 canonical MCP actions(首批为 `mcp:list/reload/show-tool`)接入外部 control plane,并明确运行中生效与错误处理语义。CLI 层 `unload` 待后续补 canonical action 后再纳入宿主协议面。 | TBD | `client/commands/mcp.py`;相关集成测试 | 2026-03-02 | +| CCLI-001 | P0 | done | CCLI-GAP-006 | `client-host-orchestration-doc-baseline` | 更新 `client/DESIGN.md`,新增“宿主编排 / headless / control plane / capability discovery / 错误语义”章节,作为后续实现唯一设计输入。 | bouillipx | `client/DESIGN.md`;`openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/` | 2026-03-02 | +| CCLI-002 | P0 | done | CCLI-GAP-006 | `client-host-orchestration-doc-baseline` | 更新 `client/README.md`,明确当前 `--output json` 是 legacy automation schema,补充后续宿主协议模式的兼容说明。 | bouillipx | `client/README.md`;`docs/features/archive/client-host-orchestration-doc-baseline.md` | 2026-03-02 | +| CCLI-003 | P1 | done | CCLI-GAP-001 | `client-headless-event-envelope-v1` | 为 `client` 设计并实现显式 headless 模式,定义禁止 prompt / 禁止内联审批 / 只输出协议帧的行为边界。 | bouillipx | `client/main.py`;`client/session.py`;`tests/integration/test_client_cli_flow.py`;`docs/features/archive/client-headless-event-envelope-v1.md` | 2026-03-02 | +| CCLI-004 | P1 | done | CCLI-GAP-002 | `client-headless-event-envelope-v1` | 设计并实现 versioned event envelope(至少含 `schema_version`、`run_id`、`seq`、`event`、`data`),并定义与现有 JSON 输出的兼容策略。 | bouillipx | `client/render/headless.py`;`tests/unit/test_client_cli.py`;`tests/integration/test_client_cli_flow.py`;`docs/features/archive/client-headless-event-envelope-v1.md` | 2026-03-02 | +| CCLI-005 | P1 | todo | CCLI-GAP-003 | `client-external-control-plane-v1` | 设计外部控制协议入口(如 `control-stdin` 或 loopback RPC),覆盖 approvals / MCP / skills / status 的结构化控制。 | bouillipx | `client/main.py`;`client/runtime/action_client.py`;相关 OpenSpec design/specs/tasks | 2026-03-02 | +| CCLI-006 | P2 | todo | CCLI-GAP-005 | `client-external-control-plane-v1` | 将当前 canonical MCP actions(首批为 `mcp:list/reload/show-tool`)接入外部 control plane,并明确运行中生效与错误处理语义。CLI 层 `unload` 待后续补 canonical action 后再纳入宿主协议面。 | bouillipx | `client/commands/mcp.py`;相关集成测试 | 2026-03-02 | | CCLI-007 | P2 | todo | CCLI-GAP-004 | `client-capability-discovery-and-host-tests` | 将 `actions:list` 提升到 CLI 宿主协议面,并定义启动握手或显式查询命令。 | TBD | `dare_framework/transport/interaction/resource_action.py`;`client/main.py`;相关文档 | 2026-03-02 | | CCLI-008 | P1 | todo | CCLI-GAP-006 | `client-capability-discovery-and-host-tests` | 新增 headless 协议稳定性、外部控制、能力发现三组集成测试,并回写 README / 设计文档中的验证锚点。 | TBD | `tests/integration/test_client_cli_flow.py`;新增协议测试文件 | 2026-03-02 | diff --git a/openspec/changes/client-headless-event-envelope-v1/.openspec.yaml b/openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/.openspec.yaml similarity index 100% rename from openspec/changes/client-headless-event-envelope-v1/.openspec.yaml rename to openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/.openspec.yaml diff --git a/openspec/changes/client-headless-event-envelope-v1/design.md b/openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/design.md similarity index 100% rename from openspec/changes/client-headless-event-envelope-v1/design.md rename to openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/design.md diff --git a/openspec/changes/client-headless-event-envelope-v1/proposal.md b/openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/proposal.md similarity index 100% rename from openspec/changes/client-headless-event-envelope-v1/proposal.md rename to openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/proposal.md diff --git a/openspec/changes/client-headless-event-envelope-v1/specs/client-host-orchestration/spec.md b/openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/specs/client-host-orchestration/spec.md similarity index 100% rename from openspec/changes/client-headless-event-envelope-v1/specs/client-host-orchestration/spec.md rename to openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/specs/client-host-orchestration/spec.md diff --git a/openspec/changes/client-headless-event-envelope-v1/tasks.md b/openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/tasks.md similarity index 100% rename from openspec/changes/client-headless-event-envelope-v1/tasks.md rename to openspec/changes/archive/2026-03-02-client-headless-event-envelope-v1/tasks.md diff --git a/openspec/changes/client-host-orchestration-doc-baseline/.openspec.yaml b/openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/.openspec.yaml similarity index 100% rename from openspec/changes/client-host-orchestration-doc-baseline/.openspec.yaml rename to openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/.openspec.yaml diff --git a/openspec/changes/client-host-orchestration-doc-baseline/design.md b/openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/design.md similarity index 100% rename from openspec/changes/client-host-orchestration-doc-baseline/design.md rename to openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/design.md diff --git a/openspec/changes/client-host-orchestration-doc-baseline/proposal.md b/openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/proposal.md similarity index 100% rename from openspec/changes/client-host-orchestration-doc-baseline/proposal.md rename to openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/proposal.md diff --git a/openspec/changes/client-host-orchestration-doc-baseline/specs/client-host-orchestration/spec.md b/openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/specs/client-host-orchestration/spec.md similarity index 100% rename from openspec/changes/client-host-orchestration-doc-baseline/specs/client-host-orchestration/spec.md rename to openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/specs/client-host-orchestration/spec.md diff --git a/openspec/changes/client-host-orchestration-doc-baseline/tasks.md b/openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/tasks.md similarity index 100% rename from openspec/changes/client-host-orchestration-doc-baseline/tasks.md rename to openspec/changes/archive/2026-03-02-client-host-orchestration-doc-baseline/tasks.md diff --git a/openspec/changes/client-external-control-plane-v1/.openspec.yaml b/openspec/changes/client-external-control-plane-v1/.openspec.yaml new file mode 100644 index 00000000..fd79bfc5 --- /dev/null +++ b/openspec/changes/client-external-control-plane-v1/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-03-02 diff --git a/openspec/changes/client-external-control-plane-v1/design.md b/openspec/changes/client-external-control-plane-v1/design.md new file mode 100644 index 00000000..d3e70583 --- /dev/null +++ b/openspec/changes/client-external-control-plane-v1/design.md @@ -0,0 +1,98 @@ +## Context + +`client/` 现在已经具备宿主可读的 headless 事件面,但控制仍然留在进程内: + +- CLI slash 命令直接调用本地 handler; +- `TransportActionClient` 直接通过 `DirectClientChannel.ask(...)` 进入 action/control; +- 宿主无法在同一 headless run 外部发送结构化审批或 MCP 请求。 + +Issue #135 的 Slice C 需要补足“写路径”,但不应同时引入网络暴露面或新命名体系。因此 v1 设计收敛到: + +1. 控制输入来自 `--control-stdin`; +2. 动作 id 继续复用 canonical `resource:action`; +3. CLI 只做协议桥接,不重写运行时 action 语义。 + +## Goals / Non-Goals + +**Goals:** + +- 为 headless `run/script` 提供可选的 `--control-stdin` 本地控制入口。 +- 定义最小 command/result/error envelope 和请求关联规则。 +- 覆盖 approvals、MCP、skills 和 status 的最小宿主控制面。 +- 对未知 action、非法参数、运行时失败给出结构化错误。 + +**Non-Goals:** + +- 本次不实现 loopback RPC 或远程访问。 +- 本次不实现 capability discovery 握手;`actions:list` 留给 Slice D。 +- 本次不把 CLI-only `mcp unload` 暴露为宿主协议动作。 +- 本次不改变 Slice B 已落地的 headless event envelope 格式。 + +## Decisions + +### Decision 1: v1 只支持 `--control-stdin` + +- 宿主在启动 headless `run/script` 时显式打开 `--control-stdin`。 +- stdin 按“一行一个 JSON command frame”解析,不与 `chat` prompt 复用。 +- 未启用 `--control-stdin` 时,headless 保持 Slice B 的只读事件语义。 + +### Decision 2: command/result/error envelope 与 headless event envelope 分离 + +- command frame 顶层字段至少包含: + - `schema_version` + - `id` + - `action` + - `params` +- result frame 顶层字段至少包含: + - `schema_version` + - `id` + - `ok` + - `result` + - `error` +- 结果帧不复用 `event` 字段,避免把控制往返混入只读事件流语义。 + +### Decision 3: v1 action 范围收敛到现有 canonical surface + +- 首批桥接 action: + - `approvals:list` + - `approvals:poll` + - `approvals:grant` + - `approvals:deny` + - `approvals:revoke` + - `mcp:list` + - `mcp:reload` + - `mcp:show-tool` + - `skills:list` + - `status:get` +- `status:get` 由 CLI session state 提供结构化快照;其余 action 复用现有运行时 handler。 +- `mcp:unload` 继续留在 CLI 命令面,不进入 v1 协议基线。 + +### Decision 4: 错误必须结构化且不可回落为 prompt UX + +- JSON 解析失败、未知 action、缺失参数、运行时 handler 失败都返回 `ok=false` 的结构化 error。 +- 启用了 `--control-stdin` 的 headless 会话不得把控制错误打印成仅人类可读的交互提示。 +- 审批已超时或 session 已结束时,control 响应仍需给出确定性错误码。 + +## Risks / Trade-offs + +- [Risk] 同一个进程同时消费 task stdin 和 control stdin 会产生边界混淆。 + → Mitigation: v1 仅允许 `run/script` 使用 `--control-stdin`,不让 `chat` 混入这条路径。 + +- [Risk] `status:get` 不是现有 runtime canonical action。 + → Mitigation: 明确它是 CLI 宿主协议层动作,由 session state 投影,不伪装成 transport runtime action。 + +- [Risk] MCP 命令面当前支持 `unload`,但 canonical action 不支持。 + → Mitigation: v1 只桥接 `mcp:list/reload/show-tool`,把 `unload` 明确留给后续 canonicalization。 + +## Migration Plan + +1. 在 CLI 参数层增加 `--control-stdin`,限定其只作用于 headless `run/script`。 +2. 增加 control frame 解析/分发/响应逻辑,并桥接现有 approvals、skills、MCP 和 status handler。 +3. 为 happy path、unknown action、handler failure、session-lifecycle edge case 增加测试。 +4. 回写 feature evidence、TODO ledger 与 OpenSpec task 状态。 + +## Open Questions + +- control result/error 是否输出到 stdout 还是 stderr,才能兼容宿主同时读取事件流与控制响应? +- `status:get` 的最小返回字段是否应包含 `mode/status/running/active_task/pending_approvals`? +- `mcp:show-tool` 与现有 `/mcp inspect` 的输出投影是否需要完全一致,还是先只保证结构化字段稳定? diff --git a/openspec/changes/client-external-control-plane-v1/proposal.md b/openspec/changes/client-external-control-plane-v1/proposal.md new file mode 100644 index 00000000..5e3e6d66 --- /dev/null +++ b/openspec/changes/client-external-control-plane-v1/proposal.md @@ -0,0 +1,39 @@ +## Why + +Slice B 已经把 `client/` 推进到显式 headless + versioned event envelope v1,但宿主仍然只能“看”事件,不能对活跃 run 发回结构化控制。Issue #135 的下一段缺口因此不在事件读取,而在本地 control plane。 + +当前 `main` 的阻塞点有两类: + +1. 审批、MCP、skills 与 session status 仍主要暴露为 CLI 内部 slash 命令或 `TransportActionClient` 直接调用,宿主没有外部稳定入口。 +2. `client/` 还没有定义控制命令帧、结果帧、错误帧和运行中生效语义,导致 headless 会话只能 fail-fast,不能被宿主接管。 + +Slice C 先收敛到 `--control-stdin` 这一条本地控制面:避免提前引入 loopback 端口和身份边界,同时复用已有 canonical action ids 与现有运行时语义。 + +## What Changes + +- 为 `client run` / `client script` 的 headless 会话引入 `--control-stdin` 控制面入口。 +- 定义 control command/result/error envelope v1,明确 `schema_version`、请求关联 `id`、`action`、`params`、`ok`、`result`、`error` 字段。 +- 将 approvals、MCP、skills 与 status 的最小控制面桥接到外部宿主协议。 +- 为控制往返、错误路径和运行中生效语义增加测试与执行证据。 + +## Capabilities + +### Modified Capabilities + +- `client-host-orchestration`: 把“planned control plane”推进为首个 landed 外部控制入口。 +- `transport-channel`: 复用 canonical `resource:action` 语义作为 CLI 控制桥接目标,而不是引入新的 dotted 命名。 + +## Impact + +- 影响文件: + - `client/main.py` + - `client/runtime/action_client.py` + - `client/commands/mcp.py` + - `tests/unit/test_client_cli.py` + - `tests/integration/test_client_cli_flow.py` + - `docs/features/client-external-control-plane-v1.md` + - `openspec/changes/client-external-control-plane-v1/**` +- 不包含: + - loopback RPC / `--control-port` + - capability discovery 握手 + - `mcp:unload` canonicalization diff --git a/openspec/changes/client-external-control-plane-v1/specs/client-host-orchestration/spec.md b/openspec/changes/client-external-control-plane-v1/specs/client-host-orchestration/spec.md new file mode 100644 index 00000000..e80a5d05 --- /dev/null +++ b/openspec/changes/client-external-control-plane-v1/specs/client-host-orchestration/spec.md @@ -0,0 +1,42 @@ +## MODIFIED Requirements + +### Requirement: Host control is provided through a structured local control plane +The system SHALL provide a structured local control plane for host-orchestrated sessions. + +- v1 MUST support a local `--control-stdin` entry for headless `run/script`. +- The control plane MUST accept deterministic actions for approvals, MCP management, skills discovery, and session status. +- Control responses MUST use structured success/error payloads with request correlation. +- The control plane MUST preserve existing approval and action semantics rather than bypassing them. + +#### Scenario: Host grants approval through structured control +- **GIVEN** a running headless session emits an approval-pending event +- **WHEN** the host submits a structured approval grant command +- **THEN** the client resolves the approval through the deterministic approval action path +- **AND** the result is returned as a structured control response + +#### Scenario: Unknown control action fails structurally +- **GIVEN** a host sends a control command with an unsupported action id +- **WHEN** the client validates the command frame +- **THEN** the client returns a structured error response +- **AND** it does not fall back to prompt-oriented or natural-language command parsing + +## ADDED Requirements + +### Requirement: Control plane v1 reuses canonical action identifiers +The system SHALL expose control-plane actions using canonical stable identifiers. + +- MCP control in v1 MUST be limited to `mcp:list`, `mcp:reload`, and `mcp:show-tool`. +- CLI-only verbs without canonical actions, including `mcp:unload`, MUST NOT be advertised as v1 host protocol actions. +- Approvals and skills actions MUST reuse their existing `resource:action` identifiers. + +#### Scenario: Host reloads MCP providers through canonical action id +- **GIVEN** the host needs to refresh MCP provider paths for a running headless session +- **WHEN** it sends action `mcp:reload` through the control plane +- **THEN** the client reuses the current MCP reload runtime path +- **AND** the response is returned as a structured control result + +#### Scenario: Host requests CLI session status +- **GIVEN** a headless session is running under the CLI host protocol +- **WHEN** the host sends action `status:get` +- **THEN** the client returns a structured snapshot of current session state +- **AND** the response is correlated to the request id diff --git a/openspec/changes/client-external-control-plane-v1/tasks.md b/openspec/changes/client-external-control-plane-v1/tasks.md new file mode 100644 index 00000000..e84e1d0b --- /dev/null +++ b/openspec/changes/client-external-control-plane-v1/tasks.md @@ -0,0 +1,15 @@ +## 1. Control-Stdin Entry + +- [ ] 1.1 为 headless `run/script` 增加 `--control-stdin` 参数,并定义与 interactive / legacy 输入模式的兼容边界。 +- [ ] 1.2 建立 command/result/error frame 解析与响应逻辑,保证请求 `id` 相关联且错误结构化返回。 + +## 2. Action Bridging + +- [ ] 2.1 将 `approvals:list/poll/grant/deny/revoke` 暴露到外部 control plane。 +- [ ] 2.2 将 `mcp:list/reload/show-tool` 与 `skills:list` 暴露到外部 control plane,并显式排除 `mcp:unload`。 +- [ ] 2.3 提供 `status:get` 的结构化会话快照返回。 + +## 3. Verification And Evidence + +- [ ] 3.1 增加 `control-stdin` happy path、unknown action、handler failure、session edge case 的测试。 +- [ ] 3.2 验证 Slice B headless event envelope 未被控制面引入回归破坏,并回写 `docs/features/client-external-control-plane-v1.md` 的 Evidence 区块。 diff --git a/openspec/specs/client-host-orchestration/spec.md b/openspec/specs/client-host-orchestration/spec.md new file mode 100644 index 00000000..6fa7f695 --- /dev/null +++ b/openspec/specs/client-host-orchestration/spec.md @@ -0,0 +1,78 @@ +# client-host-orchestration Specification + +## Purpose +Define the stable host-orchestration contract for `client/`, including explicit mode separation, headless event semantics, the planned structured local control plane, and capability discovery boundaries. + +## Requirements + +### Requirement: Client host orchestration modes are explicitly separated +The system SHALL distinguish between interactive CLI behavior, legacy automation JSON output, and host-orchestrated headless execution. + +- Interactive mode MAY use prompts and inline human approval UX. +- Legacy automation JSON MAY keep the current `log/event/result` line schema for backward compatibility. +- Headless host orchestration MUST be an explicit mode boundary and MUST NOT rely on prompt text or inline human approval interactions. +- `chat` MUST remain interactive and MUST reject headless-only flags. +- Incompatible headless/legacy flag combinations MUST fail with a deterministic parameter error instead of silently falling back. + +#### Scenario: Headless mode does not depend on prompt UX +- **GIVEN** the client is started in host-orchestrated headless mode +- **WHEN** a task execution requires runtime observation or control +- **THEN** the client does not emit `dare>` style prompts +- **AND** it does not require inline approval input from stdout/stdin prompt UX + +#### Scenario: Invalid flag combinations are rejected deterministically +- **GIVEN** a caller combines headless-only execution with incompatible legacy output flags +- **WHEN** argument parsing runs +- **THEN** the client exits with a deterministic parameter error +- **AND** it does not silently fall back to interactive or legacy automation output + +### Requirement: Headless event envelope v1 is versioned and distinct from legacy automation JSON +The system SHALL emit a versioned event envelope for headless mode that is distinct from the current legacy automation JSON schema. + +- Headless mode MUST be entered explicitly rather than inferred from legacy `--output json`. +- Each headless frame MUST include `schema_version`, correlation metadata, an event name, and structured event data. +- The envelope MUST provide stable event ordering semantics within a run. +- Legacy automation JSON MUST remain available without requiring hosts to parse the headless envelope. + +#### Scenario: Host starts a headless run without prompt UX +- **GIVEN** the host starts `client run` or `client script` in headless mode +- **WHEN** the task begins execution +- **THEN** the client emits structured protocol frames instead of prompt text +- **AND** no inline approval input is requested from the terminal + +#### Scenario: Host receives correlated versioned frames +- **GIVEN** a host consumes headless stdout from a running client session +- **WHEN** lifecycle, tool, or approval-pending events are emitted +- **THEN** each frame includes a version identifier and correlation fields for the active run +- **AND** the host can order frames without parsing human-readable log strings + +#### Scenario: Approval demand fails structurally before external control is available +- **GIVEN** a headless session reaches an approval gate before an external control plane is available +- **WHEN** the client cannot resolve the approval interactively +- **THEN** the client reports the condition through structured protocol output +- **AND** the session ends with a non-zero failure instead of prompting for terminal input + +### Requirement: Host control is provided through a structured local control plane +The system SHALL provide a structured local control plane for host-orchestrated sessions. + +- The control plane MUST accept deterministic actions for approvals, MCP management, skills discovery, and session status. +- Control responses MUST use structured success/error payloads. +- The control plane MUST preserve existing approval and action semantics rather than bypassing them. + +#### Scenario: Host grants approval through structured control +- **GIVEN** a running headless session emits an approval-pending event +- **WHEN** the host submits a structured approval grant command +- **THEN** the client resolves the approval through the deterministic approval action path +- **AND** the result is returned as a structured control response + +### Requirement: Host capabilities are discoverable without hardcoded matrices +The system SHALL provide a deterministic capability discovery surface for host orchestration. + +- Hosts MUST be able to discover supported actions for the active client/runtime. +- Capability discovery MAY be exposed via an explicit action such as `actions:list` or an equivalent startup handshake. + +#### Scenario: Host queries supported actions +- **GIVEN** a host needs to determine whether the client supports dynamic MCP reload +- **WHEN** it requests capability discovery +- **THEN** the client returns a structured list of supported actions +- **AND** the host does not need to infer support by parsing help text or natural-language output