From dd609ae41886ef81a6eae449f9175bd9258a831e Mon Sep 17 00:00:00 2001 From: huashen <2494946808@qq.com> Date: Wed, 2 Sep 2026 17:17:48 +0800 Subject: [PATCH] docs: design ordinary agent loop --- docs/INDEX.md | 9 +- docs/NOW.md | 1 + ...gin-rollout-is-owned-by-the-parent-turn.md | 5 + .../0055-agent-loop-is-an-ordinary-plugin.md | 80 +++++ .../0056-no-revert-promotes-candidate.md | 54 +++ docs/decisions/README.md | 2 + docs/design/ordinary-agent-loop.md | 324 ++++++++++++++++++ ...install-uninstall-turn-boundary-rollout.md | 51 +-- docs/design/plugin-update-entropy-audit.md | 12 +- .../recursive-plugin-self-validation.md | 2 +- docs/projectneed.md | 4 +- 11 files changed, 508 insertions(+), 36 deletions(-) create mode 100644 docs/decisions/0055-agent-loop-is-an-ordinary-plugin.md create mode 100644 docs/decisions/0056-no-revert-promotes-candidate.md create mode 100644 docs/design/ordinary-agent-loop.md diff --git a/docs/INDEX.md b/docs/INDEX.md index f456245e9..9fc6a46aa 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -105,11 +105,11 @@ | Markdown 记忆、退役 Memory2 归档、Akasha | `projectneed` 第 6、8、11~13 节 → [0041](decisions/0041-turn-effects-and-memory-plugins-are-orthogonal.md) → [0052](decisions/0052-compaction-and-markdown-memory-are-ordinary-plugins.md) → [0006](decisions/0006-akasha-v2-is-the-canonical-explicit-memory-engine.md) → [Akasha V2 在线与重放](design/akasha-v2-runtime-migration.md) → [持久化状态地图](design/persistence-state-map.md) | `plugins/markdown_memory/`、`plugins/compaction/`、`plugins/akasha/` | | Compaction 与 Markdown 记忆普通插件化 | `projectneed` CTX-007、MEM-001~MEM-011 → [0052](decisions/0052-compaction-and-markdown-memory-are-ordinary-plugins.md) → [插件化任务合同](design/compaction-markdown-memory-plugin-task-contract.md) → [0030](decisions/0030-session-context-compaction-ledger.md) → [持久化状态地图](design/persistence-state-map.md) | `agent/core/passive_turn.py`、`agent/plugin_composition/`、`session/`、`plugins/compaction/`、`plugins/markdown_memory/` 与任务合同 P01~P09 | | 主动流程、Wake、Drift、调度 | `projectneed` 第 6、9、12~13 节 → [持久化状态地图](design/persistence-state-map.md) → [Wake 最近主动消息上下文](design/wake-recent-delivery-context.md) → [Content / Wake 现有原子能力与第一阶段](design/content-wake-existing-atoms-first-stage.md) → [Content / Wake / Proactive 分层任务合同](design/content-wake-proactive-migration-task-contract.md) → [0040](decisions/0040-wake-duty-gate-lives-in-scoped-react.md) → [0048](decisions/0048-eventmail-keeps-three-mail-lifecycles.md) | `plugins/eventmail/`、`plugins/wake/`、`plugins/drift/`、`plugins/scheduler/`、`plugins/subagent/`、`agent/plugin_composition/timers.py`、`agent/plugin_composition/background_jobs.py` | -| React Core 原子能力、Scheduler/Subagent 非特权插件 | `projectneed` 第 6、9~13 节 → [0034](decisions/0034-turn-is-the-logical-work-unit.md) → [0039](decisions/0039-react-core-atoms-keep-sources-unprivileged.md) → [React Core 与 Scheduler/Subagent 设计](design/react-core-scheduler-subagent.md) → [分阶段任务合同](design/react-core-scheduler-subagent-task-contract.md) → [持久化状态地图](design/persistence-state-map.md) | `agent/looping/core.py`、`agent/core/passive_turn.py`、`agent/lifecycle/`、`agent/scheduler.py`、`agent/background/subagent_manager.py`、`agent/plugin_composition/`、`agent/plugins/snapshot.py` | +| React Core 原子能力、普通 Agent Loop、Scheduler/Subagent 非特权插件 | `projectneed` 第 6、9~13 节 → [0034](decisions/0034-turn-is-the-logical-work-unit.md) → [0039](decisions/0039-react-core-atoms-keep-sources-unprivileged.md) → [0055](decisions/0055-agent-loop-is-an-ordinary-plugin.md) → [普通 Agent Loop 规格](design/ordinary-agent-loop.md) → [React Core 与 Scheduler/Subagent 设计](design/react-core-scheduler-subagent.md) → [分阶段任务合同](design/react-core-scheduler-subagent-task-contract.md) → [持久化状态地图](design/persistence-state-map.md) | `agent/looping/core.py`、`agent/core/passive_turn.py`、`agent/lifecycle/`、`agent/scheduler.py`、`agent/background/subagent_manager.py`、`agent/plugin_composition/`、`agent/plugins/snapshot.py` | | 正式启动、Supervisor、自重启、停止信号 | `projectneed` RUN-001~RUN-004 → [Linux Supervisor 安全自重启提议](design/linux-supervisor-safe-self-restart.md) → [`docker/debug/README.md`](../docker/debug/README.md) | `main.py`、`agent/supervisor.py`、`agent/restart.py`、`agent/tools/agent_restart.py`、`scripts/stop-runtime.sh`、restart Gate 报告 | | 容器、云主机运行适配、Host Bridge、插件 Workload、hua-home迁移 | `projectneed` RUN-013~RUN-016、PLG-017、WSP-005~WSP-006 → [0032](decisions/0032-host-bridge-preserves-host-equivalent-execution.md) → [0053](decisions/0053-plugins-declare-managed-workloads.md) → [Computer 插件与 Workload 合同](design/computer-plugin-workload-task-contract.md) → [容器与 Linux 主机运行适配设计](design/akashic-container-cloud-runtime-adaptation.md) → [Core 与 Host Bridge 安装设计](design/akashic-core-bridge-installer.md) → [非迁移实验合同](design/akashic-container-host-bridge-experiment-contract.md) → [Unified Shell Execution 设计](design/unified-shell-execution.md) → [持久化状态地图](design/persistence-state-map.md) | `agent/plugin_composition/`、`agent/plugins/`、Workload Controller、exact-commit 安装、runtime identity、Supervisor 与隔离实验;正式 profile 迁移前先运行 plan-only 清单并取得独立批准 | | Provider、模型角色、运行时切换、usage、首次配置、模型普通插件化 | `projectneed` RUN-005~RUN-012、ONB-001、CTX-001 → [0050](decisions/0050-model-revision-lives-in-ordinary-plugin.md) → [0054](decisions/0054-model-sync-refreshes-public-capabilities.md) → [模型普通插件与 Provider 组合规格](design/model-plugin-ordinary-capability-spec.md) → [0027](decisions/0027-runtime-models-use-generation-leases.md) → [0028](decisions/0028-model-credentials-live-with-workspace-connections.md) → [现行实现与历史验收基线](design/runtime-model-registry-and-onboarding.md) → [持久化状态地图](design/persistence-state-map.md) | `plugins/models/`、`plugins/opencode_go/`、`agent/plugin_composition/`、`agent/model_runtime/`、`agent/provider.py`、`bootstrap/settings_api.py`、`frontend/chat/src` | -| 插件安装、热重载、自验证、Cordis 迁移、plugin-data、Skill、Drift skill、MCP | `projectneed` 第 6、9~13 节 → [0008](decisions/0008-plugin-runtime-publishes-only-committed-snapshots.md) → [0024](decisions/0024-plugin-self-validation-uses-stable-and-latest.md) → [0026](decisions/0026-plugin-rollout-is-owned-by-the-parent-turn.md) → [插件自更新复杂度审查](design/plugin-update-entropy-audit.md) → [0036](decisions/0036-plugin-composition-keeps-promotion-owner.md) → [0038](decisions/0038-operator-trust-can-publish-offline-plugin-batches.md) → [0042](decisions/0042-plugin-diagnostics-preserve-domain-owners.md) → [0046](decisions/0046-plugin-candidate-validation-is-incremental.md) → [插件 install/uninstall/revert turn 边界发布合同](design/plugin-install-uninstall-turn-boundary-rollout.md) → [插件递归自验证运行时设计](design/recursive-plugin-self-validation.md) → [Cordis 插件迁移能力等价验收](design/cordis-plugin-capability-parity.md) → [插件 v3 最终迁移地图](design/plugin-v3-final-migration-map.md) → [插件 v3 生产替代清单](design/plugin-v3-production-readiness-checklist.md) → [插件 v3 admission/lifecycle 收口合同](design/plugin-v3-admission-lifecycle-closeout-task-contract.md) → [插件 v3 generation metadata 收口合同](design/plugin-v3-generation-metadata-task-contract.md) → [插件 v3 Runtime Inspection 合同](design/plugin-v3-runtime-inspection-task-contract.md) → [插件 v3 committed command catalog 合同](design/plugin-v3-command-catalog-task-contract.md) → [插件组合内核第一阶段任务合同](design/plugin-composition-kernel-task-contract.md) → [插件事件与同步执行能力任务合同](design/plugin-event-executor-task-contract.md) → [插件 TopologyView 任务合同](design/plugin-topology-view-task-contract.md) → [插件 lifecycle 接入点任务合同](design/plugin-lifecycle-seam-task-contract.md) → [Turn committed typed event 合同](design/plugin-turn-committed-event-task-contract.md) → [插件 v3 generation loader 任务合同](design/plugin-v3-loader-task-contract.md) → [插件 stable 原子组装任务合同](design/plugin-stable-atomic-assembly-task-contract.md) → [插件 candidate Root 隔离任务合同](design/plugin-candidate-root-isolation-task-contract.md) → [插件组合结构身份与 revision 任务合同](design/plugin-composition-revision-task-contract.md) → [插件组合 Health/Incident/Validation 任务合同](design/plugin-composition-health-incident-task-contract.md) → [插件 Transform/Observe 事件任务合同](design/plugin-transform-observe-task-contract.md) → [插件 generation 数据根任务合同](design/plugin-data-root-task-contract.md) → [插件 Tool 组合事件任务合同](design/plugin-tool-composition-events-task-contract.md) → [插件 Tool v3 迁移组合 Gate 任务合同](design/plugin-tool-v3-migration-gate-task-contract.md) → [Citation + Meme 纯 v3 组合 Gate](design/plugin-passive-composition-v3-gate-task-contract.md) → [持久化状态地图](design/persistence-state-map.md) | `agent/plugins/base.py`、`agent/plugins/install.py`、`agent/plugins/manager.py`、`agent/plugins/snapshot.py`、`agent/plugins/reload_journal.py`、`agent/plugins/turn_rollout.py`、`agent/plugins/skill_links.py`、`agent/control/runtime.py`、`agent/looping/core.py`、`agent/mcp/client.py`、`agent/plugin_composition/context.py`、`agent/plugin_composition/effect.py`、`bootstrap/app.py`、`utils/process_group.py` | +| 插件安装、热重载、自验证、Cordis 迁移、plugin-data、Skill、Drift skill、MCP | `projectneed` 第 6、9~13 节 → [0008](decisions/0008-plugin-runtime-publishes-only-committed-snapshots.md) → [0024](decisions/0024-plugin-self-validation-uses-stable-and-latest.md) → [0026](decisions/0026-plugin-rollout-is-owned-by-the-parent-turn.md) → [0056](decisions/0056-no-revert-promotes-candidate.md) → [插件自更新复杂度审查](design/plugin-update-entropy-audit.md) → [0036](decisions/0036-plugin-composition-keeps-promotion-owner.md) → [0038](decisions/0038-operator-trust-can-publish-offline-plugin-batches.md) → [0042](decisions/0042-plugin-diagnostics-preserve-domain-owners.md) → [0046](decisions/0046-plugin-candidate-validation-is-incremental.md) → [插件 install/uninstall/revert turn 边界发布合同](design/plugin-install-uninstall-turn-boundary-rollout.md) → [插件递归自验证运行时设计](design/recursive-plugin-self-validation.md) → [Cordis 插件迁移能力等价验收](design/cordis-plugin-capability-parity.md) → [插件 v3 最终迁移地图](design/plugin-v3-final-migration-map.md) → [插件 v3 生产替代清单](design/plugin-v3-production-readiness-checklist.md) → [插件 v3 admission/lifecycle 收口合同](design/plugin-v3-admission-lifecycle-closeout-task-contract.md) → [插件 v3 generation metadata 收口合同](design/plugin-v3-generation-metadata-task-contract.md) → [插件 v3 Runtime Inspection 合同](design/plugin-v3-runtime-inspection-task-contract.md) → [插件 v3 committed command catalog 合同](design/plugin-v3-command-catalog-task-contract.md) → [插件组合内核第一阶段任务合同](design/plugin-composition-kernel-task-contract.md) → [插件事件与同步执行能力任务合同](design/plugin-event-executor-task-contract.md) → [插件 TopologyView 任务合同](design/plugin-topology-view-task-contract.md) → [插件 lifecycle 接入点任务合同](design/plugin-lifecycle-seam-task-contract.md) → [Turn committed typed event 合同](design/plugin-turn-committed-event-task-contract.md) → [插件 v3 generation loader 任务合同](design/plugin-v3-loader-task-contract.md) → [插件 stable 原子组装任务合同](design/plugin-stable-atomic-assembly-task-contract.md) → [插件 candidate Root 隔离任务合同](design/plugin-candidate-root-isolation-task-contract.md) → [插件组合结构身份与 revision 任务合同](design/plugin-composition-revision-task-contract.md) → [插件组合 Health/Incident/Validation 任务合同](design/plugin-composition-health-incident-task-contract.md) → [插件 Transform/Observe 事件任务合同](design/plugin-transform-observe-task-contract.md) → [插件 generation 数据根任务合同](design/plugin-data-root-task-contract.md) → [插件 Tool 组合事件任务合同](design/plugin-tool-composition-events-task-contract.md) → [插件 Tool v3 迁移组合 Gate 任务合同](design/plugin-tool-v3-migration-gate-task-contract.md) → [Citation + Meme 纯 v3 组合 Gate](design/plugin-passive-composition-v3-gate-task-contract.md) → [持久化状态地图](design/persistence-state-map.md) | `agent/plugins/base.py`、`agent/plugins/install.py`、`agent/plugins/manager.py`、`agent/plugins/snapshot.py`、`agent/plugins/reload_journal.py`、`agent/plugins/turn_rollout.py`、`agent/plugins/skill_links.py`、`agent/control/runtime.py`、`agent/looping/core.py`、`agent/mcp/client.py`、`agent/plugin_composition/context.py`、`agent/plugin_composition/effect.py`、`bootstrap/app.py`、`utils/process_group.py` | | Core 领域 Observe 事件 | [Core 领域 Observe 事件合同](design/plugin-domain-observe-events-task-contract.md) → [插件 Transform 与 Observe 事件任务合同](design/plugin-transform-observe-task-contract.md) | `agent/turn_events/observe.py`、`agent/lifecycle/composition.py`、`bus/event_bus.py`、`agent/retrieval/default_pipeline.py` | | 插件 v3 包级 Skill/Drift skill/Dashboard 声明 | [插件 v3 generation loader 任务合同](design/plugin-v3-loader-task-contract.md) → [插件 v3 包级 contribution 任务合同](design/plugin-v3-package-contributions-task-contract.md) | `agent/plugins/composable.py`、`agent/plugins/manager.py`、`agent/plugins/generation.py` | | 插件只读既有 Session 投影 | [持久化状态地图](design/persistence-state-map.md) → [插件 Session Read 组合能力任务合同](design/plugin-session-read-service-task-contract.md) | `agent/plugin_composition/session_read.py`、`agent/plugins/manager.py`、`session/manager.py` | @@ -251,7 +251,9 @@ docs/ │ ├── 0051-web-ui-composes-ordinary-plugin-modules.md │ ├── 0052-compaction-and-markdown-memory-are-ordinary-plugins.md │ ├── 0053-plugins-declare-managed-workloads.md -│ └── 0054-model-sync-refreshes-public-capabilities.md +│ ├── 0054-model-sync-refreshes-public-capabilities.md +│ ├── 0055-agent-loop-is-an-ordinary-plugin.md +│ └── 0056-no-revert-promotes-candidate.md ├── design/ │ ├── akasha-v2-runtime-migration.md │ ├── akashic-channel-client-adapters.md @@ -264,6 +266,7 @@ docs/ │ ├── mobile-long-message-delivery.md │ ├── mobile-browser-lab.md │ ├── mobile-projection-audit.md +│ ├── ordinary-agent-loop.md │ ├── codex-style-same-turn-input-requirements.md │ ├── codex-style-same-turn-input.md │ ├── cordis-plugin-capability-parity.md diff --git a/docs/NOW.md b/docs/NOW.md index 3319dff75..705bdd3b0 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -27,6 +27,7 @@ Session/Message 全身份迁移、配置、Akasha 和 Android 强制全量同步 ## P1 · Agent Harness 抽象收敛 +- [`普通 Agent Loop 规格`](design/ordinary-agent-loop.md) 正在设计评审:目标是 exact snapshot 中只有一个普通插件提供 `REACT`,Core 只保留 Turn、snapshot、提交和发布 owner;先迁出 Tool search、system prompt 等特殊规则,再切换默认 loop,最后从 production artifact commits 迁移外部插件并删除全部旧入口。不得运行 shadow/gray,不得留下兼容壳。 - 目标骨架只使用 `Message`、`Turn`、`Session`:Message 组成 Turn,Turn 归入 Session;`Loop` 表达“输入 Message → 内部 `react` → 输出 Message”。当前从 `AgentLoop._react → PassiveTurnPipeline` 继续向内审查;只有独占权威状态、不变量、控制流、生命周期或真实边界的层才保留。纯转发、重复结果包装、字段复制、内部重复校验和平行模型分批内联、合并或删除;命名使用普通英语和 Python 风格,不再引入 `Unit` 一类没有独立事实的概念。 - Turn 的待审目标是:多次 user 输入可以跨越被中断的执行尝试,最后与唯一 terminal assistant 构成一个完整 Turn;主动投喂、scheduler、spawn 和不依赖 user query 的消息可以各自成为独立 Turn,再由 Turn 组合时间线与 Akasha 节点。当前 SES-007、SES-008、RUN-008、OUT-001、OUT-004 和 OUT-005 的 `logical interaction / execution attempt`、主动送达与 `message_push` 合同仍是权威语义;改变名称、数据库身份或归属前,必须先用 SessionDB 与 runtime 日志证明真实路径,再单独批准规格、数据和迁移,不能借普通 refactor 偷改。 - 接手顺序固定为从内向外的小批次:`ReasonerResult` metadata dict 已类型化、`AfterReasoningResult` 已内联进 `TurnSnapshot`(less-is-more PR62/PR63);剩余先审查重复 input DTO(`BeforeReasoningInput`/`AfterReasoningInput`/`PromptRenderInput` 与 GATE ctx 的平行字段)是否重述同一事实,审查结论写进账本;再完整画出 Message、Turn、Session、interaction 和 attempt 的 owner/写入链,确认非唯一 attempt 的真实频率与恢复用途;最后才评估 proactive、scheduler、spawn 和 `message_push` 怎样由同一组原子能力拼接。每个 PR 只处理一个冗余组,不为未来预建总框架,也不把现有独立实现直接包进新的总抽象。 diff --git a/docs/decisions/0026-plugin-rollout-is-owned-by-the-parent-turn.md b/docs/decisions/0026-plugin-rollout-is-owned-by-the-parent-turn.md index 411561058..45334af81 100644 --- a/docs/decisions/0026-plugin-rollout-is-owned-by-the-parent-turn.md +++ b/docs/decisions/0026-plugin-rollout-is-owned-by-the-parent-turn.md @@ -3,9 +3,14 @@ - 状态:accepted - 日期:2026-08-08 - supersedes:[0024](0024-plugin-self-validation-uses-stable-and-latest.md) 的 Agent 显式 latest/promote/discard 接口;保留其内部双快照与 session 并发机制 +- superseded by:[0056](0056-no-revert-promotes-candidate.md) 只取代第 4 条的 child/parent 成功 Gate - refines:[0008](0008-plugin-runtime-publishes-only-committed-snapshots.md)、[0015](0015-cleanup-does-not-own-turn-or-restart-finality.md) - 关联条款:PLG-010、PLG-012、PLG-013、RUN-007、CTRL-003、ERR-001、TST-001~TST-006 +> 历史说明:第 4 条及其旧验收中的 child/parent 成功 Gate 已由 +> [0056](0056-no-revert-promotes-candidate.md) 取代。当前目标是 install 没有 +> `plugin-revert` 就晋升;以下原文保留决策演进,不得作为现行 Gate。 + ## 背景 0024 证明了父 turn 保持 stable、独立 programmatic child 租用 latest 可以形成真实递归验证。但让 Agent 手工编排 status、promote、discard、uninstall、restart,把 snapshot、lease、manifest、独占 endpoint 和恢复等 Core 状态泄漏给调用者。2026-08-07 Fitbit 更新因此出现调用方绕过 `endpoint_coexistence` Gate,形成 snapshot 与正式 listener 分裂。 diff --git a/docs/decisions/0055-agent-loop-is-an-ordinary-plugin.md b/docs/decisions/0055-agent-loop-is-an-ordinary-plugin.md new file mode 100644 index 000000000..7f3b8dd47 --- /dev/null +++ b/docs/decisions/0055-agent-loop-is-an-ordinary-plugin.md @@ -0,0 +1,80 @@ +# 0055 · Agent Loop 是普通插件 + +- 状态:proposed +- 日期:2026-09-02 +- 关联条款:PLG-001~PLG-017、RUN-001~RUN-012、SES-001~SES-008、OUT-001~OUT-004、CTRL-003、TST-001~TST-008 +- refines:[0039](0039-react-core-atoms-keep-sources-unprivileged.md) +- supersedes:0039 中“Core 自己实现 `react` 算法”的局部归属 + +## 背景 + +0039 已让 Scheduler 和 Subagent 作为普通 v3 插件调用同一条 `react`,但默认被动回复的 +`AgentLoop`、`DefaultReasoner` 和 `PassiveTurnPipeline` 仍由 bootstrap 固定组装。Core 因此仍 +认识 Prompt、Tool search、phase 顺序和默认回答策略。 + +DeepSeek Harness 在 revision `dd6322d604e00eec1ba5e0c8541159906a21094a` 中把完整 +agent loop 放在普通 `@deepseek-ai/dsh-agent-loop` Service 插件。它依赖 agents、sessions、 +llm、tools、systemPrompt 和 sessionProjections,但 Cordis 不为它增加特殊加载权。 + +Akashic 已经有 Context、Service、Inject、Fiber、Effect、exact RuntimeSnapshot、Turn admission、 +Session、Tool、Model、事件和 delivery。继续为被动回复增加 Core phase 会重复这些资产。 + +## 决定 + +1. 默认 `agent-loop` 是普通 pure-v3 插件。它通过 `REACT` 提供现有 `TurnExecutor`,并拥有 + `Message → react → Message` 的完整算法、Prompt 顺序、模型与工具循环。 +2. Core 在 Turn admission 后先取得并绑定 exact RuntimeSnapshot,再从该 snapshot 的 Context + 选择 `REACT`。切换 stable 只影响之后的新 Turn。 +3. `REACT` 是 Core 按 ServiceKey 检查的唯一 required service。每个可运行 Root 必须恰有一个 + provider;缺失、重复或依赖不就绪时在 snapshot 发布前 fail-loud。Core 没有默认实现和 fallback。 +4. `agent-loop` 只使用普通插件可注入的公开 Service。内置发行不授予私有 import、全局 registry、 + 任意 SQL、PluginManager 或 bootstrap 对象。 +5. Core 继续唯一拥有 Turn admission、取消和 terminal,snapshot lease,Session 与 delivery 的 + 提交不变量,以及 generation publication。插件调用这些 owner 提供的窄 Service,不复制 owner。 +6. 不新增 phase 插件类型、`Turn Plan`、`React Kit`、`Turn Capsule` 或另一份总 Context。 + 一个普通插件拥有算法;其他插件通过现有 Service 和 typed event 贡献能力。 +7. 最终删除 bootstrap 固定 `AgentLoop`、Core 的被动 phase 组装和功能名特判。最终状态不保留 + deprecated alias、兼容 wrapper 或双链路。 + +```text +Transport + │ Message + ▼ +ConversationRuntime: admit → bind exact snapshot → require(REACT) + │ + ▼ + ordinary agent-loop plugin + Message → model/tools → Message + │ + ▼ +Core Services: Session commit · delivery · terminal · release +``` + +## 理由 + +Turn、snapshot 和提交是权威边界;回答方法是可替换策略。把前者留在 Core、后者交给普通插件, +每个变化轴只有一个 owner。完整算法放在一个插件内也与 DSH 一致,不会把七个执行阶段误做成七个 +相互知道顺序的特权插件。 + +## 影响 + +- Scheduler、Subagent、Wake 和被动消息继续复用同一个 `REACT`,来源不进入 Core 分支。 +- Tool search、人格、Skill catalog 和 host 提示必须变成普通 Tool/Prompt 能力或删除,不能随算法 + 搬进新插件后继续硬编码。 +- 迁移分批进行,但每批只有一条生产路径;测试使用 base/candidate 离线差分,不运行 shadow。 +- 外部插件先迁到稳定公开入口,最后删除其私有 Core import。 + +## 验收 + +- [ ] 默认 agent loop 由正式 v3 loader 装载,并与外部插件使用同一 `apply(ctx, config)` 权限。 +- [ ] Core 不 import 默认 agent-loop 实现,不出现 phase、tool_search、Veda 或插件 ID 分支。 +- [ ] attached child 在 capability 消费时按 snapshot ID 取得 exact lease;同 Turn 不串用 stable 与 candidate Service,也不 fallback。 +- [ ] 缺失或重复 `REACT` 在 Root readiness 阶段失败,没有 runtime fallback。 +- [ ] `REACT` 返回 `ControlExecutionResult`;只有 `ConversationRuntime` 生成 terminal `TurnResult`。 +- [ ] Session write set、事件顺序、Tool 调用、错误分类、delivery 和取消与批准基线等价。 +- [ ] 旧链、legacy host 和 deprecated 标记全部删除。 + +## 关联设计 + +- [普通 Agent Loop 规格](../design/ordinary-agent-loop.md) +- [React Core、Scheduler 与 Subagent](../design/react-core-scheduler-subagent.md) diff --git a/docs/decisions/0056-no-revert-promotes-candidate.md b/docs/decisions/0056-no-revert-promotes-candidate.md new file mode 100644 index 000000000..7875b0af9 --- /dev/null +++ b/docs/decisions/0056-no-revert-promotes-candidate.md @@ -0,0 +1,54 @@ +# 0056 · 没有 Revert 就晋升 + +- 状态:accepted / not implemented +- 日期:2026-09-02 +- 关联条款:PLG-010、PLG-012、PLG-013、RUN-007、CTRL-003、ERR-001、TST-001~TST-006 +- supersedes:[0026](0026-plugin-rollout-is-owned-by-the-parent-turn.md) 第 4 条中的 child/parent 成功 Gate + +## 背景 + +0026 让 Agent 在旧 stable parent 中安装候选,并用绑定 exact candidate 的 attached child 做业务 +检查。当前 Core 还要求 child 和 parent 都以 `COMPLETED` 结束,等于 Core 又判断了一次 Agent 的 +检查是否合格。 + +维护者确认的 Agent 合同更简单:Agent 如果发现候选有问题,一定在 parent 封口前调用 +`plugin-revert`;不考虑 Agent 忘记 revert 的分支。 + +## 决定 + +1. `plugin-install` 成功建立候选后,默认结果是晋升。 +2. attached child 只负责让 Agent 在 exact candidate 世界中试用能力,不向 Core提交“检查通过”证明。 +3. parent 封口前调用同一操作的 `plugin-revert`,候选丢弃;没有 revert,parent 封口后晋升。 +4. Core 不根据 child/parent 的 `COMPLETED`、`FAILED`、`CANCELLED` 或业务输出猜测是否晋升。 +5. artifact、schema、依赖、Root readiness、generation identity 和 source revision 仍由 Core 在候选 + 建立时校验。这些是结构有效性,不是 Agent 的业务判断。 +6. 不新增确认、超时猜测、忘记提醒或人工批准状态。 + +```text +install candidate + │ + ▼ +Agent uses exact candidate child + │ + ├─ wrong ─▶ plugin-revert ─▶ discard + │ + └─ no revert ─▶ parent seals ─▶ promote +``` + +## 理由 + +Agent 拥有“这个插件能不能完成我的工作”的业务判断;Core 只拥有候选结构和发布事务。默认晋升加 +一个明确的反向动作,比多个成功 Gate 更直接,也不会把插件类别或验证方法写进 Core。 + +## 影响 + +- 现有 `child_checked`、child terminal success Gate 和 parent status Gate 是待删除实现。 +- attached child 的 exact generation/source binding 仍保留,因为它保证 Agent 试用的是候选本身。 +- 进程崩溃恢复必须按“是否已经记录 revert”继续同一决定,不能重新猜业务结果。 + +## 验收 + +- [ ] 没有 `plugin-revert` 时,不论 child/parent terminal status,候选都进入既有晋升事务。 +- [ ] 有 `plugin-revert` 时候选不晋升,stable 不变。 +- [ ] wrong generation/source 的 child 不能冒充 exact candidate,但也不成为新的 promotion Gate。 +- [ ] Core 代码和文档没有 forgotten-revert 分支。 diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 5efb0dbfe..734fd3ae0 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -60,6 +60,8 @@ | [0052](0052-compaction-and-markdown-memory-are-ordinary-plugins.md) | accepted / implementing | Compaction 与 Markdown 记忆是普通插件 | CTX-007、MEM-001~MEM-011、PLG-001~PLG-014、SES-003~SES-005 | | [0053](0053-plugins-declare-managed-workloads.md) | accepted / implementing | 插件声明受管 Workload | RUN-016、PLG-017、WEBUI-008、WSP-006 | | [0054](0054-model-sync-refreshes-public-capabilities.md) | accepted / implementing | 模型同步刷新公共能力目录 | RUN-011、ONB-001、WSP-001 | +| [0055](0055-agent-loop-is-an-ordinary-plugin.md) | proposed | Agent Loop 是普通插件 | PLG-001~PLG-017、RUN-001~RUN-012、SES-001~SES-008、OUT-001~OUT-004 | +| [0056](0056-no-revert-promotes-candidate.md) | accepted / not implemented | 没有 Revert 就晋升 | PLG-010、PLG-012、PLG-013、RUN-007、CTRL-003 | ## 新增规则 diff --git a/docs/design/ordinary-agent-loop.md b/docs/design/ordinary-agent-loop.md new file mode 100644 index 000000000..8895612cc --- /dev/null +++ b/docs/design/ordinary-agent-loop.md @@ -0,0 +1,324 @@ +# 普通 Agent Loop 规格 + +- 状态:proposed +- 日期:2026-09-02 +- 上游:[0055](../decisions/0055-agent-loop-is-an-ordinary-plugin.md)、[0056](../decisions/0056-no-revert-promotes-candidate.md)、[0036](../decisions/0036-plugin-composition-keeps-promotion-owner.md)、[0039](../decisions/0039-react-core-atoms-keep-sources-unprivileged.md) +- 参考:DeepSeek Harness `dd6322d604e00eec1ba5e0c8541159906a21094a` + +## 1. 结论 + +最终不是“整个程序都是一个插件”,也不是“七个 phase 各做一个插件”。边界是: + +```text +┌──────────────────────── Core ────────────────────────┐ +│ 收到 Message · Turn 排队/取消 · exact snapshot │ +│ Session/delivery 提交 · terminal · plugin publication│ +└──────────────────────────┬───────────────────────────┘ + │ require(REACT) + ▼ +┌──────────── ordinary agent-loop plugin ──────────────┐ +│ 读上下文 → 组 Prompt → 调模型/工具 → 形成 Message │ +│ 顺序和循环属于插件;依赖来自普通 Service │ +└───────┬─────────────┬─────────────┬──────────────────┘ + │ │ │ + Prompt plugins Tool plugins Memory plugins ... +``` + +把它当积木盒:Core 是桌子和锁扣,保证一局游戏只有一个 Turn、用同一盒积木、最后只提交一次; +`agent-loop` 是普通玩法说明书;Tool、Prompt、Memory 是它从盒子里拿的平等积木。换玩法说明书不需要 +改桌子。 + +## 2. 现状 + +### 2.1 当前真实调用链 + +```text +Inbound / Control + → ConversationRuntime + → bootstrap.app._execute_control_request + → execute_control_turn(self.agent_loop, event_bus, request) + → AgentLoop.process_direct_message + → AgentLoop._process_with_runtime_admission + → AgentLoop._react + → PassiveTurnPipeline.run +``` + +- `bootstrap/app.py` 的 closure 固定捕获一个全局 `AgentLoop`。 +- `ConversationRuntime` 只保存一个全局 `TurnExecutor` callable。 +- 普通 inbound 到 `AgentLoop` 内才取得 stable snapshot;candidate child 当前也只带 selector/identity,仍由全局 loop 延后取得 lease。 +- `AgentLoop` 固定构造 `DefaultReasoner` 和 `PassiveTurnPipeline`。 +- `PassiveTurnPipeline` 固定组装 before-turn、reasoning、step、after-reasoning 和 after-turn 模块。 + +这条链能工作,但 Core 同时拥有“Turn 怎样安全结束”和“默认 Agent 怎样思考”两个独立变化轴。 + +### 2.2 已有可复用资产 + +| 已有积木 | 当前 owner | 目标用法 | +|---|---|---| +| Context / Service / Inject | composition Core | 选择能力和声明硬依赖 | +| Fiber / Effect | composition Core | 插件生命周期与逆序清理 | +| RuntimeSnapshot lease | plugin runtime | 一个完整 Turn 固定同一 Root | +| `SCOPED_TURNS` | control Core | Scheduler、Subagent、Wake 进入同一 Turn | +| `CHAT_MODELS` | model plugins | exact snapshot 中选择模型 | +| Tool catalog 和 Turn scope | Tool owner | 列出、授权和执行当前 Turn Tool | +| typed events | domain owners | Prompt、Tool、committed Turn 等贡献 | +| Session read/compaction | Session / plugins | 普通插件读取与投影 | +| delivery services | channel Core | 外部发送与 durable finality | +| stable/latest 和 journal | PluginManager | generation 发布与恢复 | + +Scheduler 和 Subagent 已经是普通 v3 插件;Compaction、Markdown memory、model revision 和请求投影 +也已经走普通插件能力。本设计不重做它们。 + +### 2.3 仍写死的特殊功能 + +| 特殊点 | 现状 | 目标 | +|---|---|---| +| 默认 loop | bootstrap 固定全局 `AgentLoop` | 一个普通插件提供 `REACT` | +| phase 列表 | Core 固定 pipeline 模块顺序 | agent-loop 插件私有算法 | +| Tool search | Reasoner 检查精确名字 `tool_search` 并解析返回文本 | 普通 Tool 改 Turn scope;下一 step 从 scope 重建可见 Tool | +| Tool search cache | Core `ToolDiscoveryState` | Tool search 插件自己的普通状态,或证明无用后删除 | +| host 提示 | pipeline 读取 Host Bridge 环境并拼 Prompt | 普通 system-prompt 插件贡献 | +| 人格和身份 | `ContextBuilder` 固定 Veda/identity/behavior | 普通 system-prompt 插件贡献 | +| Skill catalog | `ContextBuilder` 固定拼装 | 普通 Skill 与 `PROMPT_RENDER_EVENT` 贡献 | +| candidate skill 名 | pipeline 写 `_activeSkillNames`,未发现读取者 | 证明零 consumer 后删除 | +| rollout 业务 Gate | Core 检查 child/parent completion | 只记录 `plugin-revert`;没有就晋升 | + +“迁出去”不等于每行代码一个插件。人格、host 提示和 Skill catalog 都是 system prompt 的普通贡献; +Tool search 是一个普通 Tool;循环顺序仍由一个 agent-loop 插件拥有。 + +## 3. DSH 怎样组装 + +DSH 没有把 loop 的每个步骤升级成 Cordis 原语。它只有两层: + +1. Cordis 提供 Context、Service、Inject、Fiber 和 Effect。 +2. 普通 `@deepseek-ai/dsh-agent-loop` 插件注入 agents、sessions、llm、tools、systemPrompt 和 + sessionProjections,并在自己内部拥有 `ReactLoopAgent` 算法。 + +`packages/bundle/base/cordis.patch.yml` 只把 agent-loop 当一行普通配置装入。DSH 当前也没有 +Akashic 这种特殊 `tool_search` loop 分支;搜索词只在模型 catalog 文件中偶然出现。 + +Akashic 不照抄 DSH 的 TypeScript 类型,也不删除已有 generation lease。学习的是所有权:组合内核 +不知道 agent loop 的业务阶段,agent-loop 插件也不拥有发布指针。 + +## 4. 目标合同 + +### 4.1 唯一新选择点 + +```python +REACT = ServiceKey[TurnExecutor]("core.react") +``` + +名字使用已有动词 `react`,callable 复用当前 `TurnExecutor`:输入 `TurnRequest`,返回 +`ControlExecutionResult`。实施 PR 必须先把 `_controlTurnInputSource`、`_controlItemEvent` 等 callable +移出 request metadata,留在 Core task-local 边界;传给普通插件的 request 只含公开、可验证的 +Turn 输入。不能因此新建一份平行 Turn DTO。 + +每个可运行 Root 恰有一个 `REACT` provider。默认 provider 来自仓库内置 `agent-loop`,但它通过 +与外部插件相同的 loader、Context、Fiber 和 Effect 运行。Core 不按插件 ID 自动挂载。 + +`REACT` 进入 Core 的 required-service 集合。snapshot install/publish 在开放 admission 前按 +ServiceKey 检查 zero provider;duplicate provider 继续由通用 composition 冲突检查拒绝。这个检查 +只认识能力,不认识默认插件名字。 + +### 4.2 exact snapshot + +`ConversationRuntime` 的固定顺序是: + +```text +admit Turn + → acquire exact snapshot + → bind lease to Turn context + → snapshot.context.require(REACT) + → await react(request) + → receive ControlExecutionResult + → settle terminal / ACK and build TurnResult + → release lease +``` + +当前 candidate child 只带 `runtime=latest` 和 generation/source metadata,真正的 lease 仍由全局 +loop 延后取得,这是一个待修缺口。目标是在 child capability 消费时解析并核验 exact snapshot ID, +立即用现有 `RuntimeSnapshotStore.acquire(snapshot_id)` 取得 lease,再把 lease 交给 runtime。该 +snapshot 已退役或身份漂移时 child fail-loud,绝不改选 stable。这个失败不成为 promotion Gate: +没有 revert 仍按 0056 晋升。 + +普通 inbound 由 `ConversationRuntime` 取得 current stable。两条来源只在“snapshot 从哪里来”不同, +之后走同一个 `REACT`。plugin snapshot 覆盖完整 react Turn 正是 lease 的用途,不需要特权插件。 + +### 4.3 依赖 + +`agent-loop` 不接收当前宽 `AgentLoopDeps`。迁移时逐项做以下判断: + +1. 已有公开 Service 能表达:直接复用。 +2. 事实 owner 已有但接口是私有对象:只导出最窄行为协议,普通名称优先使用 `SESSION`、`TOOLS`。 +3. 只是转发、缓存或重复字段:删除。 +4. 没有真实 consumer:不预建接口。 + +公共 Service 只提供完成 Turn 所需的操作,不提供任意 SQL、全局 PluginManager 或 bootstrap 对象。 +它们仍由 Core 实现,因此 Session append、Tool grant 和 delivery finality 的不变量不搬进插件。 + +### 4.4 Tool search + +Tool search 继续可以叫 `tool_search`,但名字只属于 Tool 声明,不进入 loop 分支: + +```text +agent-loop asks TOOLS for visible schemas + → model calls any visible Tool + → tool_search changes current Turn tool scope + → next step asks TOOLS again + → newly granted Tool appears +``` + +loop 不解析 Tool 返回文本,不调用 `unlock_names_from_result`,也不维护第二份 visible list。grant 必须 +带 Turn、attempt 和 snapshot identity;执行 owner 继续拒绝未授权 Tool。 + +### 4.5 Prompt + +默认人格、行为、host 提示和 Skill catalog 由普通 system-prompt 插件贡献。当前可复用原子是 +`PROMPT_RENDER_EVENT`,PR 2 先把它从私有 lifecycle 路径公开到稳定插件入口;只有它无法表达真实 +consumer 时才新增窄 Service。agent-loop 只请求“本 Turn 的 system prompt”,不认识各块名字。固定安全指令只有在它是所有 +agent-loop provider 都必须遵守的 Core 不变量时才留在 Core;产品人格不是这类不变量。 + +### 4.6 提交 + +插件可以调用 Session/delivery Service,或发出公开 typed event。各自的 Core owner 仍保证: + +- Session Message 正常路径只追加; +- Turn identity、提交顺序和重复调用防护唯一; +- external delivery 有明确 receipt 和失败语义; +- terminal、ACK、lease release 由 runtime 最终结算。 + +不创建一个包含 messages、effects 和 state 的 `Turn Plan`。这种大 DTO 会复制 Context 和提交状态机。 + +## 5. 非特权证明 + +| 问题 | 合格答案 | +|---|---| +| loader 是否相同 | built-in 与 external 都走 pure-v3 `apply(ctx, config)` | +| Root 权利是否相同 | 都只能 provide/require 公共 Service | +| snapshot 是否相同 | 都被 exact Root 的 Fiber/Effect 生命周期约束 | +| Core 是否认识插件名 | 不认识;只 require `REACT` | +| 缺失是否 fallback | 不 fallback;Root readiness 失败 | +| 私有 import 是否允许 | 不允许;共享 contract checker 阻止 | +| 卸载后是否有壳 | 没有;没有 `REACT` 的 Root 不可发布 | + +## 6. 自更新流程 + +```text +old agent-loop parent + → plugin-install(candidate) + → attached child gets exact candidate Root + → child uses candidate Tool / Skill / loop normally + → wrong: parent calls plugin-revert + → no revert: parent seals, Core promotes candidate +``` + +Core 只检查候选 Root 在结构上可运行,以及 child 真正绑定 exact generation/source。Core 不检查 child +调用了哪类能力,也不把 terminal status 当业务投票。Agent 会在发现问题时调用 revert;设计中没有 +“Agent 忘了”的世界。 + +## 7. 外部插件 + +2026-09-02 对 hua-home 正式 artifact 的只读审查发现:16 个启用 external 插件中,9 个可达插件 +仍有 18 个私有 Core import,主要指向 timer、lifecycle、prompt、tool events 和 turn events。 + +迁移规则: + +- 以 hua-home production artifact commit 为基线;本机 16/16 checkout HEAD 均与生产 artifact + 不同,不能直接拿本机 HEAD 当生产源码。 +- 复用 `/mnt/data/coding/akashic-plugin/plugin-contracts`;它被 21 个 CI workflow 使用。升级现有 + checker 支持 pure-v3 和禁止私有 import,不创建第二个 checker。 +- 接入点存在不代表设计干净。能由更小公开 Service/事件表达的旧入口,标出替换关系,最后与外部 + 插件同批删除。 +- Feishu/QQbot 的 candidate prepare/discard/publish 集成测试改用正式 testkit,不让外部测试依赖 + `PluginManager` 私有构造。 + +## 8. Stacked PR 顺序 + +每个 PR 都是一次完整 owner 切换;不运行 gray 或 shadow。`deprecated` 只作为同一 stack 中待删除 +标记,最终 PR 前必须清零。 + +### PR 0 · 设计(本 PR) + +- 记录现状、目标、owner、外部消费者和验收。 +- 决定 `REACT` 与 no-revert 规则。 +- 不改 runtime,不写正式 workspace,不改外部插件,不部署。 + +### PR 1 · 回退规则 + +- 删除 `child_checked`、child completion Gate 和 parent status Gate。 +- 保留 exact candidate binding、结构 readiness 和发布事务。 +- 崩溃矩阵按是否存在 revert 事实恢复。 + +### PR 2 · 服务边界 + +- 盘点 `AgentLoopDeps` 每项消费者。 +- 复用或公开最窄 Session、Tool、Model 和 delivery Service;把现有 event(包括 `PROMPT_RENDER_EVENT`)移到稳定公开入口。 +- 升级现有跨仓 contract checker。 +- 当前生产链改用这些 Service,行为不变;不新增 `REACT` wrapper。 + +### PR 3 · 工具和提示 + +- Tool search 只通过 Turn scope 生效,删除 Reasoner 的名字和文本解析分支。 +- 人格、host 提示和 Skill catalog 迁入普通 prompt 插件。 +- 删除 `_activeSkillNames` 等零 consumer 状态。 +- 每项切换后删除原 owner,不保留双写。 + +### PR 4 · Agent Loop + +- 默认 `agent-loop` 通过普通 loader provide `REACT`。 +- `ConversationRuntime` 在 exact snapshot 中 resolve 并调用它。 +- 直接删除 bootstrap 全局 `AgentLoop` 和旧被动链;不先包一层 legacy adapter。 +- Scheduler、Subagent、Wake 和 passive 全部证明走同一 Service。 + +### PR 5 · 外部插件 + +- 从 production artifact commits 迁移 9 个可达外部插件。 +- 删除散落的私有 Core import 入口和 legacy host 分支。 +- 升级后的共享 checker 在所有相关插件 CI 通过。 + +### PR 6 · 最终删除 + +- 删除所有 deprecated 标记、兼容壳、旧测试和死文档入口。 +- 全仓、正式 artifact 和 cache 扫描证明零私有 consumer。 +- 完成最终累计 diff、能力等价和恢复演练后停止;部署另行批准。 + +## 9. 等价验收 + +不运行双生产链。每个实施 PR 在隔离 fixture 中分别运行 base commit 与 candidate commit,比较: + +- exact input Message、Turn/Session identity; +- system prompt blocks 和顺序; +- 每 step 可见 Tool、调用参数、结果与 grant; +- provider request、usage、重试和错误分类; +- SessionDB write set 与只追加约束; +- lifecycle / typed event 顺序和 payload; +- attachment、memory、compaction 和 request projection; +- outbound、delivery receipt、ACK、取消和 shutdown; +- snapshot/generation identity、Effect cleanup 和旧 lease 排空。 + +只允许登记 UUID、时间、端口和进程号等非语义差异。代表场景至少包含 passive、同 Turn 输入、 +Tool search、Skill、MCP、附件、provider retry、取消、Scheduler、Subagent、Wake、candidate child、 +plugin-revert、无 revert 晋升和进程崩溃。 + +## 10. 明确不做 + +- 不创建七个 phase 插件。 +- 不创建第二套 Turn、Session、Context、scope、executor 或 publication 模型。 +- 不让普通插件直接写 SQL、切 stable、结算 terminal 或伪造 delivery success。 +- 不用 shadow、gray、runtime flag 或长期 compatibility shell。 +- 不直接修改 plugin cache,不把设计 PR 部署到 hua-home。 +- 不因为某个接入点已有消费者就永久保留不干净的接口。 + +## 11. 风险与停止条件 + +| 风险 | 处理 | +|---|---| +| 同 Turn 混用两代 Service | capability 消费时 acquire exact snapshot ID;任何 selector fallback 或 live registry 回读都阻塞迁移 | +| 提交后崩溃导致重复发送 | 先证明 Session、delivery、ACK 的现有 owner 和幂等边界,不在插件复制 | +| 公共 Service 变成 God object | 每个接口只保留当前真实调用;无 consumer 不添加 | +| 外部源码身份错位 | 只从 production artifact commit 建迁移分支 | +| base/candidate 差分缺口 | 缺少 write set、event 或外部调用证据时不删除旧 owner | +| 最终仍有私有入口 | Core、外部源码、正式 artifact、cache 四层零命中才完成 | + +如果实现发现 `REACT` 之外还必须新增控制原语,先停止并回到本规格说明哪个不可替代事实没有 owner; +不能在实施 PR 中顺手增加。 diff --git a/docs/design/plugin-install-uninstall-turn-boundary-rollout.md b/docs/design/plugin-install-uninstall-turn-boundary-rollout.md index b59b7bca7..806726c6d 100644 --- a/docs/design/plugin-install-uninstall-turn-boundary-rollout.md +++ b/docs/design/plugin-install-uninstall-turn-boundary-rollout.md @@ -1,10 +1,14 @@ # 插件 install/uninstall/revert 与 turn 边界发布设计合同 -- 状态:implemented;Core 与 Skill 已实现,真实 Fitbit source 适配和扩展 crash matrix 记录在 `NOW.md` +- 状态:implemented baseline;[0056](../decisions/0056-no-revert-promotes-candidate.md) 的 no-revert 规则尚未实现 - 日期:2026-08-08 - 基线:`origin/main@31b976d82cbd5766e6450d7e287ceda71d9b7573` - 关联条款:OBJ-002、STA-001~STA-003、CAP-001~CAP-002、ERR-001、RUN-003、PLG-001~PLG-013、BAK-001、TST-001~TST-006 -- 关联决策:[0008](../decisions/0008-plugin-runtime-publishes-only-committed-snapshots.md)、[0015](../decisions/0015-cleanup-does-not-own-turn-or-restart-finality.md)、[0024](../decisions/0024-plugin-self-validation-uses-stable-and-latest.md) +- 关联决策:[0008](../decisions/0008-plugin-runtime-publishes-only-committed-snapshots.md)、[0015](../decisions/0015-cleanup-does-not-own-turn-or-restart-finality.md)、[0024](../decisions/0024-plugin-self-validation-uses-stable-and-latest.md)、[0026](../decisions/0026-plugin-rollout-is-owned-by-the-parent-turn.md)、[0056](../decisions/0056-no-revert-promotes-candidate.md) + +> 本文正文和验收矩阵描述 0056 的已确认目标。当前代码仍是 PR #527 baseline:需要 +> child/parent 正常完成。该差异由普通 Agent Loop stack 的 PR 1 实施,完成前不能把目标当作 +> 运行证据。 ## 1. 用户可见目标 @@ -18,23 +22,23 @@ plugin-revert 撤销当前 turn 最近一次尚未提交的 install/uninstal Agent 不再执行 `plugin-status`、`plugin-promote`、`plugin-discard` 或手工 restart。Core 保留 stable/latest、准备、验证绑定、提交、丢弃、排空、端点切换和恢复等内部机制,但不要求 Agent 理解或编排这些阶段。 -`plugin-install` 成功后,当前 turn 自己仍绑定原 stable;由该 turn 启动的 attached programmatic child 自动绑定刚安装的候选。Agent 根据真实 child 结果和工具轨迹决定:符合目标就正常结束 turn,不符合就先 `plugin-revert`,修改源码后继续递归。 +`plugin-install` 成功后,当前 turn 自己仍绑定原 stable;由该 turn 启动的 attached programmatic child 自动绑定刚安装的候选。Agent 根据真实 child 结果和工具轨迹决定:不符合就先 `plugin-revert`,修改源码后继续递归;没有 revert 就默认晋升。 -当前 turn 正常结束且候选已完成 programmatic 验证时,Core 才在旧 lease 释放后自动切换。下一 turn 自动绑定新 stable。 +parent turn 封口时没有 revert,Core 就在旧 lease 释放后自动切换。下一 turn 自动绑定新 stable。Core 不把 child/parent terminal status 当成第二次业务判断。 ## 2. 当前事实与有意语义变化 当前实现已经具备不可变 artifact、stable/latest pointer、snapshot lease、reload journal、managed service 切换、Channel 切换和失败恢复骨架。普通卸载也已经由 runtime owner 异步等待旧 lease 排空。 -当前 PLG-013、决策 0024 和递归自验证设计要求 Agent 在 `plugin-install` 后显式选择 `runtime=latest`,再根据验证结果执行 promote/discard。改变独占 managed service 或 Channel 的 candidate 被 `endpoint_coexistence` Gate 拒绝,因为同进程 stable/latest 不能安全拥有同一个进程级资源。 +PR #527 代码和 pre-0056 的 PLG-013 文本曾要求 Agent 在 `plugin-install` 后显式选择 `runtime=latest`,再根据验证结果执行 promote/discard。这是历史 baseline,不是现行条款。改变独占 managed service 或 Channel 的 candidate 被 `endpoint_coexistence` Gate 拒绝,因为同进程 stable/latest 不能安全拥有同一个进程级资源。 2026-08-07 Fitbit 事故中,调用者为完成这组内部操作而绕过 coexistence Gate,随后形成 stable snapshot、全局 endpoint 和 admission 分裂。本设计保留 Gate 保护和 programmatic 自验证,改变的是 Agent 可见操作面与最终提交 owner: 1. latest 继续存在,但只由发起 install 的 turn 所创建的 programmatic child 因果继承。 2. 验证失败由 Agent 表达为 `revert`;Core 内部完成 candidate discard。 -3. 验证通过不再需要 promote;当前 turn 正常完成就是对尚未 revert 候选的提交授权。 +3. 验证通过不再需要 promote;没有 `plugin-revert` 就是对候选的提交授权。 4. turn 后的 lease 排空、endpoint 切换、stable/manifest 提交和恢复全部由 Core 拥有。 -5. implementation 必须新建 accepted 决策,明确勘误 0024 的公开 promote/discard 流程,不能抹除历史。 +5. [0056](../decisions/0056-no-revert-promotes-candidate.md) 已记录 accepted 目标并保留 0024/0026 的历史原文。 ## 3. 能力与状态 owner @@ -76,10 +80,10 @@ Fitbit 候选版本安装成功。 从现在开始,本 turn 启动的 programmatic 验证会自动使用新版本。 请执行 programmatic 验证: -- 如果行为和工具轨迹正确,正常结束当前 turn; +- 如果行为和工具轨迹正确,不需要额外提交动作; - 如果不正确,执行 plugin-revert,然后继续修改。 -验证通过并结束当前 turn 后,系统会自动重启 Fitbit 服务。 +没有执行 plugin-revert,parent 封口后系统会自动重启 Fitbit 服务。 下一 turn 使用新版本。你不需要 promote、discard 或 restart。 ``` @@ -99,9 +103,7 @@ Turn T 执行 install S1 Core 记录 `owner_turn_id + candidate_generation_id + source_revision`。child 在创建时冻结候选身份;T 后续安装其他 revision 不得让已经运行的 child 半途换代。 -至少一个绑定当前候选的 attached child 必须正常完成,当前 turn 才能授权提交。child 失败、取消、超时、身份不一致或根本没有运行时,Core 在 turn 结束时取消 pending install,不发布候选。 - -Core 只核对 child 属于当前 parent、绑定 exact generation/source,并且正常完成。Fitbit 领域结果与轨迹是否符合修改目标由 Agent 判断;不符合时 Agent 必须在结束 turn 前执行 `plugin-revert`。 +attached child 只用于业务检查。Core 保证它属于当前 parent,并绑定 exact generation/source;child 是否运行、怎样结束以及输出什么都不是 promotion Gate。Fitbit 领域结果与轨迹是否符合修改目标由 Agent 判断;不符合时 Agent 必须在 parent 封口前执行 `plugin-revert`。 ### 4.3 `plugin-uninstall` @@ -146,7 +148,7 @@ revert 只能撤销本 turn 最近一次 install 或 uninstall,不能回滚上 ## 5. Turn 边界与最终结果 ```text -当前 turn 内 当前 turn 正常结束后 +当前 turn 内 parent turn 封口后 ──────────── ────────────────── install/uninstall 仍可 revert pending operation 封口 父 turn 继续绑定旧 snapshot 等待全部旧 generation lease 归零 @@ -154,16 +156,15 @@ programmatic child 验证候选 切换 endpoint 与 snapshot 旧代码和恢复源必须保留 成功后清理不再引用的旧代码 ``` -只有以下条件同时满足,pending install 才进入 turn 后切换: +pending install 进入 turn 后切换只需要: ```text install 前置检查成功 -AND 当前 candidate 完成真实 attached programmatic 验证 AND 没有 revert -AND parent turn 正常完成 +AND parent turn 已封口 ``` -parent turn interrupted/failed、验证 child 非正常终结或候选身份变化时,Core 自动取消候选,旧 stable 不变。pending uninstall 只有在 parent turn 正常完成且没有 revert 时才执行。 +child/parent 的 terminal status 不改变 install 决定。候选结构在提交时已不再 ready 等发布事务失败,仍按 Core 结构错误处理,不能伪装成 Agent 的业务 revert。pending uninstall 继续只有在 parent turn 正常完成且没有 revert 时才执行;0056 不改变卸载语义。 Core 不主动创建 turn、不主动发送用户消息,也不向 SessionDB 追加伪造对话。用户下一次主动发起 turn 时,runtime 从 journal 与当前 snapshot 派生最近一次相关操作的事实,供 Agent 自然语言回答,不要求 status 或轮询。 @@ -244,7 +245,7 @@ new Channel.start() - admission 暂停后的每条分支必须 resume 或明确 degraded,不能无限等待。 - prepared、等待 lease、旧 endpoint 已停、新 endpoint 已启未提交和恢复途中崩溃,都从 journal、pointer、manifest 和真实进程/Channel identity 恢复。 - 重复安装相同 revision 返回幂等结果,不因 installer 自己创建的 runtime symlink 误判 artifact。 -- operation 在 turn terminal 前被取消或 revert 时不得产生后置 endpoint 效果。 +- install 只有显式 revert 才取消默认晋升;uninstall 在 turn terminal 前被取消或 revert 时不得产生后置 endpoint 效果。 ## 10. 实现任务合同 @@ -256,7 +257,7 @@ Agent 只用 `install/uninstall/revert` 管理插件;install 后的 programmat - [x] Agent 可见插件管理动作只有 install、uninstall、revert。 - [x] 当前 turn 自己保持旧 snapshot;其 attached programmatic child 自动、精确绑定当前 candidate。 -- [x] 没有真实 programmatic 验证、child 非正常结束、parent 非正常结束或已经 revert 时,candidate 不得发布。 +- [ ] 没有 revert 时 candidate 默认发布,不再检查 child/parent terminal status;已经 revert 时不得发布。 - [x] revert 只撤销同一 turn 最近 pending install/uninstall,不能跨 turn 回滚。 - [x] install/uninstall 返回说明已发生、未发生、后续动作和 Agent 下一步。 - [x] 独占 service/Channel 不绕过 coexistence Gate,在隔离验证或 turn 后维护切换中处理。 @@ -283,7 +284,7 @@ client_only_alternative: "只改 Skill 无法关闭 Agent 手工 promote/discard invariants: - "父 turn 从 admission 到 terminal 始终绑定旧 snapshot。" - "attached programmatic child 按 owner turn 精确绑定候选。" - - "未验证、已 revert 或非正常终结的候选不得发布。" + - "install candidate 默认发布,只有同一 parent 的 plugin-revert 取消。" - "独占 endpoint 先停 admission,再等待全部旧 lease 归零。" - "endpoint、snapshot、pointer 和 manifest 共同提交或恢复。" - "plugin-data、SessionDB/messages 和 memory 不因插件管理减少。" @@ -335,12 +336,12 @@ forbidden_effects: validation: - "plugin install/uninstall/revert、hot reload、service host、ChannelHost 与 control targeted tests" - "CLI/Skill 合同测试,证明 Agent 可见管理面只有三个动作" - - "parent/child lineage、candidate 冻结、无验证、revert、取消和超时测试" + - "parent/child lineage、candidate 冻结、无 child、child/parent 各终态和 revert 测试" - "故障注入:每个 crash point、admission resume、恢复失败、相同 revision 重装和 cleanup residue" - "semantic mutants:coexistence 绕过、提前提交、pointer-only rollback、跨 turn revert、错误 child 继承" - "python docker/debug/gate.py run --base origin/main" - "一次性 workspace 中真实 fixture;Fitbit 外部 Gate 单独固定依赖与 revision" -rollback: "实现前基线 bundle;实现提交可整体 revert。运行事务在 turn commit 前取消 pending,在切换失败时恢复并重新验证 previous。" +rollback: "实现前基线 bundle;实现提交可整体 revert。install 运行事务只由同 turn plugin-revert 取消,在切换失败时恢复并重新验证 previous。" worktree_writer: "Codex /root" handoff_head: "PR branch; exact head recorded at delivery" external_revisions: [] @@ -363,8 +364,10 @@ schema_lineages: | install 后父 turn | 父继续使用 S0;返回明确要求 programmatic 验证 | | attached programmatic child | 自动绑定 S1,真实 tool/Skill/trace 来自同一 candidate identity | | programmatic 失败后 revert | S1不发布,S0始终不变;修复后可 install S2继续递归 | -| 没有 programmatic 就结束 | pending install自动取消,下一 turn仍是 S0 | -| 验证通过且正常结束 | turn 后排空 S0并提交 S1;下一 turn自动使用 S1 | +| 没有 programmatic 就封口 | 没有 revert,turn 后排空 S0并提交 S1 | +| child failed/cancelled | 没有 revert,仍提交 S1;Core 不替 Agent 投票 | +| parent failed/cancelled | 没有 revert,仍提交 S1;恢复按 journal 继续 | +| 任意检查后 revert | S1 不发布,S0 始终不变 | | pending uninstall 后 revert | manifest、代码、能力和 plugin-data均保持原样 | | uninstall 正常提交 | turn 后停止 endpoint、移除能力和代码;plugin-data保留 | | 跨 turn revert | 明确失败,不改变当前 stable;需要修复后 install或明确 uninstall | diff --git a/docs/design/plugin-update-entropy-audit.md b/docs/design/plugin-update-entropy-audit.md index 7f8fcca26..4d5b47196 100644 --- a/docs/design/plugin-update-entropy-audit.md +++ b/docs/design/plugin-update-entropy-audit.md @@ -2,9 +2,9 @@ ## 1. 结论 -插件自更新不需要新的特权插件,也不需要第二套 Root 切换系统。现有链路已经拥有安全迁移所需的状态和边界。本次让实现重新符合决策 0026 的通用 child 完成合同,并删除没有业务调用者的新设计。 +插件自更新不需要新的特权插件,也不需要第二套 Root 切换系统。现有链路已经拥有安全迁移所需的状态和边界。PR #527 曾让实现重新符合决策 0026 的 child/parent 完成合同;维护者随后用 [0056](../decisions/0056-no-revert-promotes-candidate.md) 收紧为“没有 `plugin-revert` 就晋升”。完成 Gate 现为待删除实现,不是长期合同。 -## 2. 现有链路 +## 2. PR #527 已实现基线 ```text source ── immutable artifact ── candidate generation ── latest snapshot @@ -13,7 +13,7 @@ stable snapshot ── parent Turn ── attached child ─────── │ │ └──────── 旧请求继续使用 lease └── 检查 exact candidate -parent 完成 ── stable 指针切换 ── journal 完成 ── 旧 lease 排空 +child/parent 正常完成且没有 revert ── stable 指针切换 ── journal 完成 ── 旧 lease 排空 ``` DeepSeek Harness 提供了复杂度基线。它用普通配置行装配插件,依赖可用性决定 Fiber 激活顺序(`packages/bundle/base/cordis.patch.yml`);Service 绑定当前 Fiber,Fiber 卸载时逆序清理 Effect(`vendor/cordis/src/reflect.ts`、`vendor/cordis/src/fiber.ts`)。`agent-loop` 也只是依赖 agents、sessions、llm、tools、systemPrompt 与 sessionProjections 的普通 Service 插件(`packages/core/agent-loop/src/index.ts`)。 @@ -58,7 +58,7 @@ DSH 没有跨进程旧 Root 续跑:reload 会停止旧 Fiber,进程崩溃会 原实现把候选检查写成“成功使用候选拥有的 Tool 或 Skill”。这让 UI、Channel、模型、Job 等普通插件被类别歧视,也迫使 Core 理解业务证明。 -修正后,Core 只确认 exact attached child 正常完成。Agent 在旧 stable 的 parent 中决定 child 做什么检查;检查失败时先执行 `plugin-revert`,没有 revert 且 parent 正常完成就是提交授权。检查使用候选公开的普通能力,新增插件类别不需要修改 Core。 +PR #527 修正后,Core 只确认 exact attached child 正常完成,不再判断 Tool/Skill 类别。0056 进一步删除这项完成判断:Agent 在旧 stable 的 parent 中决定 child 做什么检查;检查失败时执行 `plugin-revert`,没有 revert 就是提交授权。Core 只保留 exact generation/source binding 和结构 readiness。 ## 6. 未改动和未知 @@ -75,7 +75,7 @@ DSH 没有跨进程旧 Root 续跑:reload 会停止旧 Fiber,进程崩溃会 | 重复面 | 真实消费者 | 处理 | |---|---:|---| | `begin_publish(admission_gated=...)` | 0 次读取 | 删除形参与两处无效传值 | -| `TurnPluginRollout._tasks` | 只有同一个 `_resolution_task` | 删除集合;shutdown 取消并等待唯一 task | +| `TurnPluginRollout._tasks` | 只有同一个 `_resolution_task` | 删除集合;shutdown 等待唯一 task 完成,不取消已封口发布 | | rollout terminal `items` | Tool/Skill evidence 删除后为 0 | 从内部 callback 删除,不改变 Turn items 的保存和输出 | | `RuntimeSnapshotStore.stable` | Core 仅四个测试断言 | 统一使用 `current`,不保留别名 | @@ -102,6 +102,6 @@ shell-safety 的测试实例化 `RuntimeSnapshotStore`,都没有读取 `.stabl ## 8. 验收 - 代码净差异不再包含上述五组新接口。 -- exact child 检查不依赖插件类别或 TurnItem。 +- exact child 不依赖插件类别或 TurnItem;0056 实施后也不再提供 completion Gate。 - 相关插件安装、热重载、候选隔离、lease 与 journal 测试通过。 - PR 记录第二轮熵审查发现、保留理由和实际删除项。 diff --git a/docs/design/recursive-plugin-self-validation.md b/docs/design/recursive-plugin-self-validation.md index d2271aaa0..0d1ec972f 100644 --- a/docs/design/recursive-plugin-self-validation.md +++ b/docs/design/recursive-plugin-self-validation.md @@ -8,7 +8,7 @@ ## 1. 结论 -> 2026-08-08 勘误:本文保留 stable/latest、session lane、attached cancellation 和真实 child trace 的机制说明,但不再定义 Agent 操作。当前合同见 [插件 install/uninstall/revert 与 turn 边界发布](plugin-install-uninstall-turn-boundary-rollout.md):父 turn 只执行 install/uninstall/revert,attached child 自动继承候选,正常 terminal 后由 Core 提交。本文后续出现的显式 `--runtime latest`、promote/discard 仅是历史设计,不得用于当前 Agent 流程。 +> 2026-09-02 勘误:本文只保留 stable/latest、session lane、attached cancellation 和真实 child trace 的历史机制说明,不再定义 Agent 操作。当前目标见 [0056](../decisions/0056-no-revert-promotes-candidate.md) 和 [turn 边界发布合同](plugin-install-uninstall-turn-boundary-rollout.md):父 turn 只执行 install/uninstall/revert,attached child 自动继承候选;没有 revert 就晋升。本文后续出现的 terminal success Gate、显式 `--runtime latest`、promote/discard 都是历史设计,不得用于当前 Agent 流程。 原实现不能在写完插件的同一 turn 内证明插件可用,不是因为缺少一条等待命令,而是因为候选能力、执行机会和验收反馈没有形成闭环:当前 turn 绑定旧 snapshot,程序化子 turn 又被两层全局执行锁挡在父 turn 后面。实现已移除跨 session 整轮互斥,并接通 stable/latest、runtime selector、attached cancellation 和候选管理接口;父 turn 现在能在自己结束前取得隔离 latest 的真实行为结果,再决定 promote 或 discard。 diff --git a/docs/projectneed.md b/docs/projectneed.md index edae5728a..e7d0dc036 100644 --- a/docs/projectneed.md +++ b/docs/projectneed.md @@ -715,13 +715,13 @@ Core 只负责通用传输、认证、revision、generation lease、调度、取 普通请求只租用已验证的 stable;latest 仍是 Core 内部候选,但只由发起 install 的 parent turn 所创建的 attached programmatic child 因果继承。父 turn 保持旧 stable;detached child、其他 turn 和没有匹配 generation/source identity 的请求不得取得候选。Agent 不手工选择 latest 或调用 promote/discard。 -install 成功只表示候选可验证。至少一个匹配当前候选的 attached child 正常完成、没有 revert 且 parent 正常结束时,Core 才在 lease 释放后自动提交;无验证、child/parent 非正常终结或身份漂移必须丢弃。Core 只检查 child 的因果归属、generation/source identity 和正常终态,不要求某类插件、Tool 或 Skill 提供特制证明;parent 负责判断本次检查是否满足业务目标,检查失败必须在 parent terminal 前执行 `plugin-revert`。独占 managed service 使用 Core 分配的隔离端口和 plugin-data 副本;插件必须声明并读取 `validation_port_env`,否则 fail-loud。Channel 正式 ownership 只在 turn 后切换。cache artifact 按 source revision/tree digest 不可变保存,旧代码保留到提交、readiness、恢复检查和 lease 排空完成。 +install 成功表示候选已经通过结构检查并默认等待晋升。attached child 只让 Agent 在匹配 generation/source identity 的 exact candidate 中做业务检查,不向 Core 提交成功证明;Agent 发现问题时必须在 parent 封口前执行 `plugin-revert`。parent 封口时没有 revert,Core 就在 lease 释放后自动提交,不根据 child/parent 的完成、失败或取消终态重新判断。设计中不考虑 Agent 忘记 revert 的分支。独占 managed service 使用 Core 分配的隔离端口和 plugin-data 副本;插件必须声明并读取 `validation_port_env`,否则 fail-loud。Channel 正式 ownership 只在 turn 后切换。cache artifact 按 source revision/tree digest 不可变保存,旧代码保留到提交、readiness、恢复检查和 lease 排空完成。 外部 operator 已经独立承担信任判断时,可以在 Supervisor 与 Runtime 均停止后使用名称明确的 trusted batch 入口,把完整 commit SHA 指向的 pure-v3 artifact 直接发布为 stable/latest。Runtime 消费 plugin-home 的整个生命周期都必须独占该 home 的 publication lock;trusted batch 必须先取得 supervisor/runtime 两把 workspace 生命周期锁,再取得同一 publication lock,拒绝 active turn、分支 ref、未知 batch 字段和非 v3 static manifest。回执必须写明 `programmaticValidation=bypassed_by_operator_trust`,不得伪造行为验证成功。在线安装、Agent 自改进和普通 `plugin-install` 继续无例外地走 candidate + attached programmatic child。 ### PLG-014 新插件使用开放组合能力并保留 Core 晋升 -新插件只通过 generation Root 下的 Context、Service、Inject、Fiber 和 Effect 组合能力;Job、Channel、Prompt、Tool、UI、MCP、存储和外部效果由各自领域 Service 定义,Core 不维护新的固定插件能力总表。`inject` 只表达 Fiber 激活所必需的硬依赖;可选能力由使用点查询,或由不阻塞 Root readiness 的嵌套 Fiber 承载。listener、后台 task 和其他注册随所属 Fiber 逆序回收,依赖消失、重启和卸载后不得残留。 +新插件只通过 generation Root 下的 Context、Service、Inject、Fiber 和 Effect 组合能力;Job、Channel、Tool、UI、MCP、存储和外部效果由各自领域 Service 定义,Prompt 通过公开 typed event 贡献,Core 不维护新的固定插件能力总表。`inject` 只表达 Fiber 激活所必需的硬依赖;可选能力由使用点查询,或由不阻塞 Root readiness 的嵌套 Fiber 承载。listener、后台 task 和其他注册随所属 Fiber 逆序回收,依赖消失、重启和卸载后不得残留。 通用事件只有五种 dispatch 合同:`emit` 同步串行并立即传播失败;`serial` 逐个等待且只有显式 `Bail` 可以短路;`parallel` 只接收异步 listener,并发执行、等待全部 settle 后聚合失败;`transform` 按注册顺序把同类型 immutable payload 显式变换成下一份;`observe` 调用全部 observer、等待异步 settle,并把普通失败隔离成 Incident 而不改写最终事实。listener 只使用同一 generation 内稳定注册顺序,不增加 priority、listener dependency DAG 或通用 waterfall。同步并发由有界 Executor Service 执行插件显式提交的纯同步任务;工作线程不得取得 Context、Fiber 或 Core 权限。