Bläddra i källkod

docs: 新增Agentic脚本构建技术方案

SamLee 1 dag sedan
förälder
incheckning
7e19528fb9
1 ändrade filer med 1335 tillägg och 0 borttagningar
  1. 1335 0
      智能创作构建系统技术方案.md

+ 1335 - 0
智能创作构建系统技术方案.md

@@ -0,0 +1,1335 @@
+# 智能创作构建系统技术方案
+
+> 文档定位:基于当前 `agent` 通用框架,把上一代固定 Workflow 脚本构建迁移为由主 Agent 动态规划、专业 Worker 执行、独立 Validator 验收的业务 Host 技术方案
+> 对应产品方案:`智能创作构建系统产品方案.md`
+> 事实基线:2026-07-18 的当前仓库、上一代真实代码、只读数据库样本和线上只读接口
+> 实施边界:本文规定后端、数据、工具、Agent 角色、验证、发布与恢复;HTML 仅作语义参考,不实施前端
+> 目标代码位置:当前仓库新增独立业务包 `script_build_host/`,不得把脚本领域对象写进 `agent/agent/orchestration`
+
+## 0. 结论和不可变约束
+
+### 0.1 目标
+
+新系统对业务方继续接收上一代相同的选题、人设、策略和数据源输入,继续产出上一代可读取的正式结构化脚本。变化的是生成过程:固定 Round、Branch 和预编排 Workflow 退出控制面,主 Agent 根据宏观 Root 目标持续检查任务树、动态拆任务、派发专业 Worker、读取 Validator 报告并决定接受、返修、重试、修订或阻塞。
+
+系统最终必须同时满足三类正确性:
+
+1. **业务合同正确**:旧输入可以启动,旧详情接口可以读到正式 Paragraph、Element 和关联。
+2. **Agent 因果正确**:父 Attempt 只消费创建时冻结的子任务 ACCEPT Decision,所有正式结果都能闭合到验证和证据。
+3. **发布正确**:Root ACCEPT 只代表候选完成;只有正式结果发布到 `branch_id=0` 且旧读取链路回读校验成功,`script_build_record.status` 才能变成 `success`。
+
+### 0.2 硬约束
+
+- `TaskLedger` 是 Mission 状态唯一真源,不复制 Root、Task、Attempt、Validation 或 Decision 状态到业务表。
+- 框架的 `FileSystemTaskStore`、`FileSystemArtifactStore`、`FileSystemTraceStore` 长期保留;首版部署限定单机、单活 Agent Host。本文不规划 SQL TaskStore。
+- MySQL 只保存旧脚本业务对象、冻结输入、候选正文版本和发布记录。
+- `ArtifactSnapshot` 只保存清单、引用和摘要,不保存大段脚本正文;正文由业务 Artifact Repository 保存。
+- 上一代工具通过 Adapter/Gateway 复用,不把旧项目模块直接导入新业务包。
+- Planner 不直接创作正文,不替代 Validator,不写正式结果。
+- Worker 不创建嵌套 Agent;四类取数能力是 Planner 派发的专业 Worker preset。
+- Validator 不修改候选,不进行未冻结的外部取数,只检查当前 Attempt 的固定 Snapshot。
+- 模型不能选择或伪造 `script_build_id`、`branch_id`、`root_trace_id`、`task_id`、`attempt_id` 和 `spec_version`;这些身份来自受保护运行上下文。
+- `branch_id=0` 只允许 Host Publisher 写入,任何 Agent 工具都不能直接发布。
+- 不新增 Script、Workflow、Round、Branch、页面节点等通用框架领域对象。
+- 本期不改 HTML 和 visualization;现有 HTML 只用于校正交互语义。
+
+## 1. 事实基线与证据边界
+
+### 1.1 上一代代码
+
+上一代真实代码位于:
+
+```text
+/home/sam/image_article_comprehension/aiddit/pattern/patter_from_global_and_build
+```
+
+审查基线为分支 `script_build_refactor_0709`、提交 `cc0e2a5`。真实业务入口、Prompt、工具、ORM、模板和静态脚本均来自该目录;本文不根据工具 Schema 数量猜测运行能力。
+
+上一代的核心运行逻辑是:
+
+```text
+创建 ScriptBuildRecord
+  → 拼接初始用户消息
+  → 主 Agent 读取 Topic/Persona/Strategy
+  → 模型自行生成 script_direction
+  → 固定轮次/分支/任务计划推进
+  → Implementer 写 Paragraph/Element/Link
+  → Evaluator/决策逻辑迭代
+  → branch 结果合并到 branch_id=0
+  → status=success
+```
+
+需要保留的是输入、工具能力和输出合同,不保留固定执行骨架。
+
+### 1.2 真实数据库样本:Script Build 454
+
+通过上一代只读数据库会话核验,`@@session.tx_read_only=1`。样本 454 的事实如下:
+
+| 项目 | 真实值 |
+|---|---:|
+| script_build_id | 454 |
+| execution_id | 401 |
+| topic_build_id | 1189 |
+| topic_id | 1352 |
+| status | success |
+| paragraph_count | 5 |
+| element_count | 11 |
+| script_direction 字符数 | 1827 |
+| build_workflow | NULL |
+| Paragraph 行数 | 5 |
+| Element 行数 | 11 |
+| Paragraph-Element 关联行数 | 13 |
+| Round 行数 | 2 |
+| Branch 行数 | 4 |
+| DataDecision 行数 | 4 |
+| MultipathDecision 行数 | 0 |
+| TaskPlanStep 行数 | 20 |
+| DomainInfo 行数 | 3 |
+| Event 行数 | 194 |
+
+样本说明:旧系统即使最终只交付 5 个段落和 11 个元素,过程也产生大量 Round、Branch、PlanStep 和 Event。新系统不照搬这些过程表,而用通用 Ledger 表达任务、尝试、验证和决策;旧 Paragraph/Element/Link 仍作为正式业务输出。
+
+Run 454 的两个 Round 目标也验证了旧过程的典型阶段性:第一轮建立整体段落结构并解释“毛驴效应/隐性亏损”,第二轮补行动指引、完成剩余段落并吸收候选分支优势。新系统允许 Planner 得出相似任务,也允许根据证据缺口选择不同顺序或省略无必要步骤。
+
+### 1.3 真实创作方向
+
+旧详情接口中,Run 454 的方向包含三类目标:
+
+1. 认知重构深度;
+2. 实用指引落地性;
+3. 人设风格一致性。
+
+这类方向不是后端确定性函数的输出。上一代 Prompt 要求主 Agent 先调用 `get_topic_detail`,按需调用 `get_account_script_persona_points`,再由模型生成 1–3 个方向目标和可选领域标准,最后调用 `save_script_direction`。新系统不能把旧 `script_direction` 当作初始 Root objective;它必须成为独立 Direction Task 的结构化、可验证、可版本化产物。
+
+### 1.4 安全事实
+
+上一代源码存在硬编码数据库或第三方访问凭据的风险。本文不记录任何凭据值。迁移前必须完成:
+
+- 轮换所有曾进入源码、日志或历史配置的凭据;
+- 使用环境注入或 Secret Manager;
+- 数据库读写账号分离;
+- 日志与 Trace 字段脱敏;
+- 外部 Endpoint allowlist、超时、响应大小限制与 SSRF 防护;
+- 禁止把 token、连接串或请求头写入 Artifact、Evidence、Event 和错误摘要。
+
+该项是阶段一上线前的 P0 门禁。
+
+## 2. 旧输入合同与冻结规则
+
+### 2.1 启动输入
+
+新业务入口保持旧调用合同,核心身份字段为:
+
+```text
+execution_id
+topic_build_id
+topic_id
+strategy selections
+data_source_url(若旧调用提供)
+model/role configuration
+```
+
+也继续支持旧的 Topic JSON 解析和导入入口。接口给出的 ID 只是定位信息,不能作为 Mission 运行期间的稳定输入;Host 必须在启动时组装并冻结完整内容。
+
+### 2.2 选题输入不是标题字符串
+
+`ScriptBuildInputSnapshotV1.topic` 至少包含:
+
+```text
+TopicBuildRecord 的 demand、demand_constraints、personal_config、origin 等核心字段
+Topic 的 id、topic_direction、inspiration_evaluation_config、result、status、failure_reason
+Points[]
+CompositionItems[]
+Relations[]
+Sources[]
+输入更新时间与来源标识
+```
+
+点、组成项、关系和来源必须保留原 ID 以及规范化内容,供 EvidenceRef 回指。Source 还要保存 `derivation_type` 和 `dataset_from`。对于缺失关系或空 sources,快照要显式保存空数组,不能在执行中再次读取“最新值”补齐。
+
+快照不能只调用旧 `get_topic_detail`,因为该工具的 Source projection 不返回 source row ID、`derivation_type`、`dataset_from`,也不包含 TopicBuildRecord 的 demand、constraints 和 personal_config。Host 启动时通过只读 Repository 联查:
+
+```text
+TopicBuildRecord
+  → TopicBuildTopic
+  → TopicBuildPoint
+  → TopicBuildPointItemRelation
+  → TopicBuildCompositionItem
+  → TopicBuildItemRelation
+  → TopicBuildItemSource
+```
+
+每层校验 execution/build/topic 归属和 active 语义。`get_topic_detail` 继续作为 Agent 兼容读取工具及交叉核验入口,不是 InputSnapshot 的唯一数据源。
+
+### 2.3 人设输入
+
+上一代运行时的人设并不等于单张 `account_persona` 表:
+
+- 初始消息从 `personal_config` 取得账号名称;
+- Persona Point 来自账号本地数据目录中的 `point_records.json`;
+- Section Pattern 来自账号对应的 section pattern 文件;
+- 主 Agent 可按需调用 `get_account_script_persona_points`。
+
+新快照统一为:
+
+```json
+{
+  "account": {"account_name": "每天心理学", "source": "personal_config"},
+  "persona_points": [],
+  "section_patterns": [],
+  "source_versions": {},
+  "content_sha256": "sha256:..."
+}
+```
+
+不得因未来有统一 Persona DB 就假定当前旧数据已在那里;Adapter 应允许文件源和数据库源并存,并在快照中记录实际来源。
+
+### 2.4 策略、Prompt 和模型配置
+
+冻结内容包括:
+
+- always-on 策略全文;
+- on-demand 策略摘要、ID、版本和内容 hash;
+- 主 Agent、Worker、Validator 的 preset 名称与 Prompt 版本;
+- 实际解析到的 Prompt 来源、`biz_type`、版本和 SHA-256;
+- 模型名、关键采样配置以及数据源配置版本。
+
+上一代 Prompt 存在 DB 优先、文件回退以及同步命名漂移。新系统每次 Mission 必须冻结“实际使用版本”,不能只记录期望的文件路径。
+
+### 2.5 输入快照对象
+
+```python
+@dataclass(frozen=True)
+class ScriptBuildInputSnapshotV1:
+    snapshot_id: str
+    script_build_id: int
+    execution_id: int
+    topic_build_id: int
+    topic_id: int
+    topic: dict
+    account: dict
+    persona_points: tuple[dict, ...]
+    section_patterns: tuple[dict, ...]
+    strategies: tuple[dict, ...]
+    prompt_manifest: tuple[dict, ...]
+    datasource_manifest: dict
+    model_manifest: dict
+    canonical_sha256: str
+    created_at: datetime
+```
+
+规范化规则:UTF-8、对象键排序、数组保持业务顺序、时间转 ISO 8601、空值和空数组不互换、浮点采用稳定字符串表示。`canonical_sha256` 对规范化 JSON 计算。业务对象和 Wire 引用使用 `sha256:<64位小写hex>`;MySQL `CHAR(64)` 列只保存裸 hex。Repository 是唯一转换边界,必须拒绝算法非 sha256、大小写异常和长度错误,避免同一 digest 出现两种比较语义。
+
+## 3. 输出合同
+
+### 3.1 正式业务输出
+
+对业务方仍只有一个正式脚本,即 `branch_id=0` 的活动记录:
+
+- `script_build_record`:构建概况、方向、计数、状态;
+- `script_build_paragraph`:段落层级、名称、内容/描述、排序和活动状态;
+- `script_build_element`:元素名称、类型、描述、内容及已有扩展字段;
+- `script_build_paragraph_element`:段落和元素关系;
+- 旧详情/日志接口需要的兼容投影。
+
+旧写工具未覆盖 Element 表所有扩展字段。产品输出合同已把 commonality、topic_support、weight 和 support elements 列为最低正式输出,因此阶段二必须补齐候选模型、Adapter 写入、freeze、正式发布和旧详情 readback;不得把这些字段降级为“不可写兼容字段”。数据库存在列不代表已有工具可写,阶段二验收以真实 create/update/readback 黑盒测试为准。
+
+旧 `GET /api/pattern/script_builds/{id}` 还必须兼容 `build_workflow`、`topic`、`decision_traces`、`post_datas`、`script_datas`,以及 Paragraph 内嵌的 `linked_element_ids`、`linked_elements`、`sub_paragraphs`。这些是旧响应合同,不代表新 Mission 模型重新引入 Workflow/Round/Page 对象。
+
+### 3.2 方向产物
+
+```python
+@dataclass(frozen=True)
+class ScriptDirectionArtifactV1:
+    goals: tuple[DirectionGoal, ...]       # 1..3
+    criteria: tuple[Criterion, ...]
+    domain_criteria: tuple[Criterion, ...]
+    topic_refs: tuple[str, ...]
+    persona_refs: tuple[str, ...]
+    strategy_refs: tuple[str, ...]
+    evidence_refs: tuple[str, ...]
+    legacy_markdown: str
+    canonical_sha256: str
+```
+
+`legacy_markdown` 是面向旧 `script_direction` 字段的投影,不是结构化真源。Direction Task 经 Validator passed 且 Planner ACCEPT 后,Host 才把已接受版本标记为 active direction;普通工具不得任意覆盖。
+
+### 3.3 证据产物
+
+```python
+@dataclass(frozen=True)
+class EvidenceRecordV1:
+    evidence_id: str
+    source_type: str
+    tool_name: str
+    query: dict
+    source_refs: tuple[str, ...]
+    raw_artifact_ref: str | None
+    summary: str
+    supports: tuple[str, ...]
+    confidence: str
+    limitations: tuple[str, ...]
+    content_sha256: str
+    created_at: datetime
+```
+
+证据摘要不能替代原始引用。涉及图片、外部网页或长文本时,原始内容进入受控对象存储/文件 Artifact;EvidenceRecord 保存引用、摘要和 digest。
+
+### 3.4 整稿产物
+
+```python
+@dataclass(frozen=True)
+class StructuredScriptArtifactV1:
+    direction_ref: str
+    paragraphs: tuple[dict, ...]
+    elements: tuple[dict, ...]
+    paragraph_element_links: tuple[dict, ...]
+    source_artifact_refs: tuple[str, ...]
+    evidence_refs: tuple[str, ...]
+    acceptance_notes: tuple[str, ...]
+    canonical_sha256: str
+```
+
+它是 Root Worker 提交的不可变候选,不等同于旧表中可变的“当前 branch”。Validator 和 Publisher 必须读取该版本固定的 canonical JSON。
+
+## 4. 总体架构
+
+```text
+Legacy-compatible HTTP API
+          │
+          ▼
+script_build_host/
+  ScriptMissionService ── InputSnapshotService
+          │                       │
+          │                       └── Legacy DB / files / prompt adapters
+          ▼
+  current agent framework
+  Planner → Task → Worker → Snapshot → Validator → Decision
+          │             │
+          │             └── LegacyScriptToolGateway
+          │                       │
+          │                       ├── four retrieval services
+          │                       └── candidate Paragraph/Element/Link writes
+          ▼
+  ScriptArtifactRepository (MySQL business versions + candidate branches)
+          │
+   Root ACCEPT
+          ▼
+  ScriptPublisher ── transaction publish to branch_id=0
+          │
+   legacy readback + digest verification
+          ▼
+  script_build_record.status = success
+```
+
+### 4.1 通用框架与业务 Host 分工
+
+框架提供:
+
+- Root objective、TaskSpec、Task 树和焦点;
+- Attempt、固定 child Decision 输入;
+- ArtifactSnapshot、Validation、Planner Decision;
+- capability 权限边界;
+- Operation、Event、Trace 和 V2 读取接口;
+- `FileSystemTaskStore` 的 revision/CAS、文件锁和原子替换,以及 `FileSystemArtifactStore` 的文件锁/first-write-wins;TraceStore 不具同等级跨进程写保证。
+
+业务 Host 提供:
+
+- 从旧 ID 组装稳定输入;
+- 创建 Root TaskSpec 和 Planner 初始消息;
+- 脚本角色 preset 和业务工具;
+- 候选正文持久化、冻结和 digest;
+- 验证规则和证据读取;
+- Root ACCEPT 后的正式发布;
+- 旧 API 和状态兼容。
+
+### 4.2 部署约束
+
+框架 Store 目录必须位于持久盘:
+
+```text
+{agent_data_root}/{root_trace_id}/orchestration/ledger.json
+{agent_data_root}/{root_trace_id}/orchestration/artifacts/*.json
+{agent_data_root}/{trace_id}/meta.json
+{agent_data_root}/{trace_id}/messages/
+```
+
+同一 `root_trace_id` 只能被一个活动 Host 进程驱动。文件锁保护 Ledger 提交,但 `OperationRuntime` 和正在执行的模型调用仍是进程内对象,因此不能把共享盘上的多个进程当作分布式调度器。进程重启后执行显式 recovery:对旧 RUNNING Attempt 不猜测、不接管模型上下文,按框架规则失败/重新规划。
+
+## 5. `script_build_host/` 模块设计
+
+```text
+script_build_host/
+  api/
+    routes.py
+    schemas.py
+    dependencies.py
+  application/
+    mission_service.py
+    mission_factory.py
+    input_snapshot_service.py
+    publisher.py
+    recovery_service.py
+    legacy_detail_projection_service.py
+  domain/
+    input_snapshot.py
+    direction.py
+    evidence.py
+    structured_script.py
+    publication.py
+    errors.py
+  agents/
+    presets.py
+    prompts/
+    validation_policy.py
+    deterministic_checks.py
+  tools/
+    registry.py
+    gateway.py
+    topic_adapter.py
+    persona_adapter.py
+    strategy_adapter.py
+    pattern_adapter.py
+    decode_adapter.py
+    external_adapter.py
+    knowledge_adapter.py
+    candidate_write_adapter.py
+  repositories/
+    mission_binding_repository.py
+    input_snapshot_repository.py
+    artifact_repository.py
+    publication_repository.py
+    legacy_script_repository.py
+  infrastructure/
+    db.py
+    config.py
+    canonical_json.py
+    secrets.py
+    migrations/
+  composition.py
+```
+
+依赖方向为 `api → application → domain/ports`,Infrastructure 实现 Port,Agent 工具只能调用 application/gateway,禁止直接持有 ORM Session。
+
+### 5.1 核心 Port 与函数合同
+
+| 组件/函数 | 输入 | 输出 | 事务与失败语义 |
+|---|---|---|---|
+| `ScriptInputSnapshotService.assemble` | execution/topic IDs、策略选择、data source、principal | `ScriptBuildInput` | 只读装配;任一归属不一致即 `INPUT_RELATION_MISMATCH` |
+| `ScriptInputSnapshotService.freeze` | assembled input | `ScriptBuildInputSnapshotV1` | 单事务按 build+hash 幂等写入;同版本异 hash 冲突 |
+| `ScriptInputSnapshotService.get` | snapshot ID + authorized build | frozen snapshot | 不存在/跨 build 统一 404 |
+| `ScriptMissionFactory.build_root_task_spec` | frozen snapshot | Root Task draft `dict` | 纯函数;字段仅 objective/acceptance_criteria/context_refs |
+| `ScriptMissionFactory.build_planner_message` | binding + snapshot refs | 初始消息 | 只放引用和合同摘要,不嵌固定任务序列 |
+| `ScriptMissionFactory.build_run_config` | binding | `RunConfig` | 强制 explicit validation、preset、root spec |
+| `ScriptArtifactRepository.create_workspace` | build/task/attempt/spec | draft version/workspace | 单事务按 attempt 唯一;先取 version ID 再作 branch ID |
+| `ScriptArtifactRepository.freeze` | workspace/attempt | frozen version + ArtifactRef | 同事务 canonical 化并拒绝后续写;重复 freeze 同 hash 幂等 |
+| `ScriptArtifactRepository.read_by_ref` | authorized ArtifactRef | immutable business artifact | 校验 build/task/attempt/type/digest;不解析任意文件 URI |
+| `ScriptArtifactRepository.verify_digest` | ArtifactRef | bool/detail | 只读;算法/格式/归属错误视为 protocol violation |
+| `LegacyScriptToolGateway.execute_read` | tool、args、protected context | normalized result | 从 snapshot/accepted refs 读取;禁止 live 漂移 |
+| `LegacyScriptToolGateway.execute_retrieval` | tool、query、context | EvidenceRecord | 受 capability、timeout、outbound policy 控制 |
+| `LegacyScriptToolGateway.execute_candidate_write` | tool、args、context | write result | 只写 Attempt workspace;每个工具调用短事务 |
+| `LegacyScriptToolGateway.snapshot_workspace` | attempt context | canonical candidate | 一致性读,供 freeze;缺引用即失败 |
+| `resolve_attempt_workspace` | protected context | CandidateWorkspace | 身份全部来自 Host context,拒绝模型传 branch |
+| `ensure_legacy_task_plan` | workspace/task | legacy PlanStep projection | 同 branch 首次结构写前幂等 upsert |
+| `normalize_tool_result` | legacy string/dict/error | typed result | 不把“错误:”文本误判为成功 |
+| `collect_evidence_refs` | child Decisions + tool results | ordered EvidenceRefs | 验证 Decision 闭合、去重并保持业务顺序 |
+| `ScriptPublisher.publish` | Root ACCEPT + ArtifactRef | PublicationResult | 单活锁 + 单事务 branch0 投影/readback/digest/状态 |
+| `LegacyDetailProjectionService.project` | authorized build + DB Session | 旧详情响应 | 可在 Publisher 同一事务中读取;稳定顺序、空值合同 |
+
+Repository 的领域错误统一映射为稳定代码,例如 `BUILD_NOT_FOUND`、`MISSION_ALREADY_OWNED`、`INPUT_HASH_CONFLICT`、`ATTEMPT_WORKSPACE_FROZEN`、`ARTIFACT_DIGEST_MISMATCH`、`CHILD_DECISION_INVALID`、`PUBLICATION_READBACK_MISMATCH`。错误摘要可观测,内部 SQL、文件路径和 secret 不出边界。
+
+### 5.2 Legacy Detail Projection
+
+```python
+class LegacyDetailProjectionService:
+    async def project(
+        self,
+        script_build_id: int,
+        *,
+        session: AsyncSession,
+        principal: Principal,
+    ) -> LegacyScriptBuildDetail: ...
+```
+
+字段映射:
+
+| 旧响应字段 | 新系统来源 | 规则 |
+|---|---|---|
+| build 概况/方向/计数/status | `script_build_record` + accepted/published pointers | success 时必须与 branch0 一致 |
+| `build_workflow` | 原记录兼容值 | 新 Agentic Mission 不生成新的固定 Workflow;无旧值返回旧合同规定的 null |
+| `topic` | frozen InputSnapshot 的 topic projection | 不读取 Mission 中途更新后的 live topic |
+| `decision_traces` | 已接受业务 Decision/Evidence 的兼容投影 | 稳定按发生时间+ID;不暴露 command fingerprint |
+| `post_datas` | accepted Evidence 中的参考帖子 | 以引用 ID 为 key 的 object/map;缺失为 `{}`,不改成数组 |
+| `script_datas` | accepted Evidence 中的参考脚本 | 以引用 ID 为 key 的 object/map;缺失为 `{}`,不混入未接受候选 |
+| Paragraph 列表 | branch0 active Paragraph | 按旧 sort/layer/ID 规则稳定排序 |
+| `linked_element_ids` | branch0 ParagraphElement | 与 `linked_elements` 顺序一致 |
+| `linked_elements` | branch0 ParagraphElement link projection | 每项严格为 `{element_id, link_id}`;完整 Element 只在顶层 `elements` |
+| `sub_paragraphs` | branch0 parent-child relation | 递归组装但防环;无子项为空数组 |
+
+所有 object/数组的空值、ID 类型、展示顺序和嵌套结构由旧 Run 详情 fixture 固化。Publisher 事务内调用同一 Projection Service 生成 canonical readback;普通 GET 也调用它,避免发布校验和线上响应使用两套拼装逻辑。
+
+## 6. Mission 生命周期与主要函数
+
+### 6.1 服务接口
+
+```python
+class ScriptMissionService:
+    async def start(
+        self, request: StartScriptBuildRequest
+    ) -> ScriptMissionStartResult: ...
+
+    async def run(self, script_build_id: int) -> None: ...
+
+    async def stop(self, script_build_id: int) -> StopResult: ...
+
+    async def resume(self, script_build_id: int) -> ResumeResult: ...
+
+    async def finalize_accepted_root(
+        self, script_build_id: int, root_trace_id: str
+    ) -> PublicationResult: ...
+```
+
+### 6.2 `start()`
+
+事务外读取和校验:
+
+1. 校验 execution、topic build、topic 的归属关系;
+2. 解析旧 API 的策略选择和 data source;
+3. 调 `ScriptInputSnapshotService.assemble()`;
+4. 规范化、计算 hash,并冻结输入;
+5. 调 `ScriptMissionFactory.build_root_task_spec()`;
+6. 预分配 Planner 主 Trace ID;该 ID 同时就是 `root_trace_id`,不建立第二个 Planner 身份;
+7. 事务内插入/复用 mission binding,保证一个 build 只有一个 active Mission;
+8. 返回旧接口可识别的 build ID/status,并在后台启动 `run()`。
+
+若同一 `script_build_id` 内的 Mission 创建命令以同一幂等键重复到达,必须返回已有 binding;若该 build 的输入 hash 不同,则拒绝隐式复用。旧 HTTP start/retry 本身仍按第 13.1 节每次创建新 build。
+
+### 6.3 Root TaskSpec
+
+```python
+def build_root_task_spec(snapshot: ScriptBuildInputSnapshotV1) -> dict:
+    topic_summary = normalize_topic_summary(
+        snapshot.topic.get("result")
+        or snapshot.topic.get("topic_direction")
+        or snapshot.topic["id"]
+    )
+    return {
+        "objective": (
+            f"为账号「{snapshot.account['account_name']}」基于选题"
+            f"「{topic_summary}」构建一份满足选题、人设、策略、"
+            "证据与旧结构化脚本输出合同的正式脚本候选"
+        ),
+        "acceptance_criteria": [
+            {"criterion_id": "input-consistency", "description": "全部创作可追溯到冻结输入", "hard": True},
+            {"criterion_id": "direction-accepted", "description": "使用已验证并接受的创作方向", "hard": True},
+            {"criterion_id": "structure-complete", "description": "段落、元素、关系完整且引用有效", "hard": True},
+            {"criterion_id": "evidence-closure", "description": "事实表达有证据且创作表达不伪装成事实", "hard": True},
+            {"criterion_id": "persona-consistency", "description": "语言和观点符合冻结人设", "hard": True},
+            {"criterion_id": "legacy-readable", "description": "能规范化投影为旧详情合同", "hard": True},
+        ],
+        "context_refs": [f"script-build://inputs/{snapshot.snapshot_id}"],
+    }
+```
+
+Root objective 描述总交付,不预先钉死“必须几轮、几个分支、按什么顺序”。创作方向由 Direction Task 生成。Root revise 只通过框架修订 TaskSpec,不更新第二套 mission 字段。
+
+真实 `TopicBuildTopic` 没有 `title` 字段。`normalize_topic_summary()` 只能从冻结的 `result` 或 `topic_direction` 生成有限长度摘要;账号从冻结的 `TopicBuildRecord.personal_config.account_name` 派生。若缺账号则使用明确的“未指定账号”策略并调整 persona criterion,不能虚构字段。
+
+### 6.4 `run()`
+
+```python
+async def run(self, script_build_id: int) -> None:
+    binding = await self.bindings.require(script_build_id)
+    config = self.factory.build_run_config(binding)
+    try:
+        await self.runner.run_result(
+            messages=[{
+                "role": "user",
+                "content": self.factory.build_planner_message(binding),
+            }],
+            config=config,
+        )
+        completion = await self.coordinator.root_completion(
+            binding.root_trace_id
+        )
+        if completion["status"] == "completed":
+            await self.finalize_accepted_root(
+                script_build_id, binding.root_trace_id
+            )
+        else:
+            await self.status_projector.project_non_success(completion)
+    except Exception as exc:
+        await self.status_projector.project_runtime_failure(exc)
+        raise
+```
+
+`AgentRunner.run()` 是 async iterator,不能直接 `await`;服务模式使用 `run_result()` 消费完整事件流,若需要流式投影则显式 `async for`。`RunConfig` 必须使用 `explicit_validation`、`script_planner` preset 和 Root Task draft dict。当前 `wire_orchestration()` 尚未暴露 validation policy、deterministic validator、evidence provider 等全部 Coordinator 参数;阶段一可在 composition root 直接构造 `TaskCoordinator`,设置 `LocalAgentExecutor` 与 `runner.task_coordinator`,或先做向后兼容 wiring 扩展,不能假定参数已经被装配。
+
+### 6.5 stop/resume/recovery
+
+`stop()`:
+
+1. 读取一次 Mission Snapshot;
+2. 先把旧业务状态投影为 `stopping`;
+3. 持久化 stop intent,停止 running/pending Operation,并通知当前 OwnerLease 对应的 Planner runtime;
+4. 轮询所有相关 Operation 和 Planner Trace;全部进入终态后才投影为 `stopped`;
+5. 超时则保持 `stopping`,记录脱敏、可恢复原因,不能假称已停止;
+6. 保留 Ledger、Trace、候选和发布记录,且 stop 路径不得执行发布。
+
+`resume()`:
+
+1. 获取 build/root 的单活所有权锁;锁冲突返回稳定 `409 MISSION_ALREADY_OWNED`;
+2. 若旧业务状态已是 `success`,验证 publication/digest 后幂等返回,绝不重开 Planner;
+3. 若 Root 已 completed 而 publication 为 pending/failed,只调用 `finalize_accepted_root()` 重试同一 accepted Artifact,绝不向 Planner 追加消息;
+4. 只有 Root 未完成且业务状态为 stopped/partial/failed 时,才调用框架 operation recovery 对齐 durable 状态;
+5. 对不可安全续跑的旧 RUNNING Attempt 按下述生产 Operation 规则恢复;
+6. 向同一未完成的 Planner Trace 追加恢复消息;
+7. 不 rewind,不猜测未提交结果,不扫描“当前最新子任务”作为输入。
+
+恢复分支要区分:
+
+- Ledger 有 Operation:`recover_operations()` 只对齐持久状态。未安全启动的 pending 可保留;已启动但进程丢失的 stage 归一为 stopped/expired/failed,不能自动调用模型;
+- 前台遗留 Attempt 无 Operation 且 `started_at is None`:尚未启动 Worker,才允许安全 `advance_cycle()`;
+- 前台遗留 Attempt 无 Operation 且 `started_at is not None`:禁止重跑同一 Attempt,避免重复外部副作用。当前框架没有公开的 foreground orphan 原子恢复命令,因此生产 Script preset 必须排除同步 `dispatch_tasks`,只开放 Host 包装的 `dispatch_script_tasks`;该工具内部使用 `start_operation()` 创建 durable BackgroundOperation 并等待/轮询。若未来保留前台路径,须先新增通用 Coordinator recovery command,原子将 Attempt 标为 stopped/failed、Task 转 needs_replan;
+- `accepted_child_decision_ids=None`:遗留协议未知,仍由现有 `advance_cycle()` 走 `PROTOCOL_VIOLATION`,不得猜测子输入;
+- `resume_operation()` 只允许框架判断为未安全启动的 stage;已启动后停止的模型 stage 返回 `resume_not_safe`,交给 Planner retry/replan;
+- Root completed:不调用上述模型恢复,只走同一 Artifact 的 Publisher 重试。
+
+### 6.6 单机单活所有权
+
+FileStore 自带的 Ledger 文件锁只保护单次 commit,不等于 Mission 运行所有权。Host 另设以 `script_build_id + root_trace_id` 为键的 `OwnerLease`(进程锁文件 + owner token):
+
+- 顶层 `run`/`resume` 在任何调度前获取一次;`start` 创建 binding 后把已获取 token 原子交接给后台 `run`,不存在两个 owner 的窗口;
+- `run()` 内调用 `finalize_accepted_root()` 时传递并复用同一个 owner token,禁止嵌套二次加锁;独立 publication recovery 才新获取 OwnerLease;
+- `stop()` 不争抢正在持有的独占 OwnerLease,只持久化 stop intent 并通过 runtime signal 通知当前 owner,且不能执行发布;
+- 锁文件写 owner PID、Host instance ID、root、获取时间,不写 secret;
+- 持有进程以文件描述符锁持续占有,不能仅靠可过期文本时间戳;
+- 正常结束或异常退出由 OS 释放,进程启动时核对 binding 和 Ledger 决定恢复;
+- 获取失败返回 `409 MISSION_ALREADY_OWNED`,不得启动第二个 Planner;
+- 发布过程与 Planner 运行使用同一所有权域和 token,避免 stop/resume/finalize 交叉执行。
+
+该机制只保证同一主机。跨机部署不在本方案范围。
+
+## 7. Agent 角色与权限
+
+### 7.1 Planner
+
+职责:检查 Ledger、识别缺口、创建/修订任务、调度 Worker、读取验证、做 Decision、推动 Root 收敛。
+
+只开放:
+
+```text
+read/task_control capability
+inspect_tasks
+create_tasks / revise_task
+dispatch_script_tasks(Host 的 BackgroundOperation 包装;排除同步 dispatch_tasks)
+decide_task
+必要的只读输入摘要
+```
+
+不得开放候选正文写工具和正式发布工具。Planner 可以决定是否需要 Pattern、Decode、External 或 Knowledge,不把四类取数写成固定顺序。
+
+### 7.2 专业取数 Worker
+
+注册四个 preset:
+
+| Preset | 老能力 | 工具白名单 |
+|---|---|---|
+| `script_pattern_retrieval_worker` | 结构/套路问答 | `get_script_snapshot`, `query_pattern_qa`, `submit_attempt` |
+| `script_decode_retrieval_worker` | 历史脚本/账号解码检索 | `get_script_snapshot`, `search_script_decode_case`, `submit_attempt` |
+| `script_external_retrieval_worker` | 外部资料与图片 | `get_script_snapshot`, `external_search_case`, `load_images`, `submit_attempt` |
+| `script_knowledge_retrieval_worker` | 内部知识库 | `search_knowledge`, `submit_attempt` |
+
+它们由 Planner 创建独立 Task 并调度,不由创作 Worker 在内部 spawn。结果统一为 EvidenceRecord Artifact;失败或低质量证据由 Validator 报告,Planner 决定重试、换源、修订查询或接受限制。
+
+### 7.3 创作 Worker
+
+建议 preset:
+
+```text
+script_direction_worker
+script_structure_worker
+script_paragraph_worker
+script_element_worker
+script_candidate_compare_worker
+script_root_worker
+```
+
+Worker 只能读取当前 TaskSpec、冻结输入、`accepted_child_decision_ids` 所引用的子结果,以及自己的 Attempt workspace。它不能读取另一个未接受候选作为“最新正确结果”。
+
+Planner 负责宏观连续性;整稿写作上下文由 `script_root_worker` 或专门的 whole-script Worker 承担,而不是要求 Planner 在长上下文中持续写正文。
+
+### 7.4 Validator
+
+```text
+script_retrieval_validator
+script_candidate_validator
+script_root_validator
+```
+
+Validator capability 上限为 read + validation_submit。它对固定 Snapshot 给出逐 criterion 的结构化报告,不修改 Artifact,不发布,不创建 Task。
+
+当前一次 ValidationPlan 只能选择 Agent 或 Deterministic 模式,不能宣称原生同时跑两者。实施选择:普通任务使用 Agent Validator,并向其开放受控的确定性检查工具;若必须保留两个独立报告,则拆成两个 Task。Root Validator 在综合判断前可调用 schema/digest/reference 等只读预检工具。
+
+本业务的 Retrieval、Candidate、Root 综合验证默认使用 **Agent mode**,因此会产生真实 Validator Sub-Trace;纯 schema/digest 预检若单独采用 deterministic mode,只产生独立 Validation record 和 validator execution identity,不产生模型 Trace。E2E 的“闭合到 Trace”只要求 Agent-mode 验证;deterministic 报告按 Validation record 闭合。
+
+### 7.5 完整 preset 隔离
+
+capability 只限制工具效果类别,不提供文件路径或数据库行级 ACL。所有 `script_*` preset 必须使用精确 `allowed_tools`,显式排除默认通用 `read_file`、`write_file`、`bash_command`、任意数据库客户端和通用网络工具。需要文件或数据库能力时,只注册下列 ownership-aware wrapper:
+
+```text
+read_input_snapshot
+read_accepted_artifact
+read_attempt_workspace
+write_attempt_workspace
+query_allowlisted_retrieval_service
+submit_attempt / submit_validation
+```
+
+wrapper 从 protected context 解析 build/root/task/attempt/snapshot 并执行行级归属检查;模型参数不能覆盖这些身份。Schema 过滤和执行前授权都必须通过,防止工具名白名单与实际执行策略漂移。
+
+## 8. 上一代工具能力盘点与 Adapter 合同
+
+### 8.1 实际能力数量
+
+上一代目录中有 57 个 JSON Schema 工具资产,但这不代表任一 Agent 同时拥有 57 个工具。口径固定为:57 个 Schema 中进入当前静态角色白名单的业务工具去重为 37 个;再加非 Schema 的 `load_images` 后,静态白名单并集为 38 个;存在 on-demand 策略时再加入 `load_strategy`,为 39 个。动态框架能力 `agent`/`implement_paths` 另算,不属于业务工具并集。新系统必须以 preset 白名单和 capability 为准,不以 Schema 文件总数授权。
+
+### 8.2 输入与当前结果读取
+
+至少适配:
+
+```text
+get_topic_detail
+get_account_script_persona_points
+load_strategy(若旧调用使用)
+get_script_snapshot / current script detail
+reference post/script detail readers
+```
+
+这些工具优先读取冻结 InputSnapshot 或明确 ArtifactRef。只有构建前组装阶段可以读取 live 输入表;Mission 运行期间不能悄悄切换成更新后的 Topic/Persona。
+
+### 8.3 Pattern Agent
+
+旧逻辑:
+
+```text
+query_pattern_qa
+POST /pattern-qa/tasks
+轮询 GET task result
+轮询间隔约 3 秒
+总超时约 600 秒
+```
+
+新 Adapter:
+
+```python
+class PatternRetrievalAdapter:
+    async def query(self, request: PatternQuery) -> EvidenceRecordV1: ...
+```
+
+要求:保存 query、任务 ID、响应 digest 和限制;Operation/Event 只表达 dispatch/Task 的高层状态变化。当前通用 Event 没有业务轮询 progress 事件,每次约 3 秒 poll 的明细进入脱敏业务日志,或由 Host 显式注入外部 Event Sink,不能复制进 Ledger。超时提交失败原因,不能返回伪空结果。
+
+### 8.4 Decode Agent
+
+旧工具签名:
+
+```python
+search_script_decode_case(
+    return_field,
+    keyword="",
+    account_name=None,
+    match_fields=None,
+    top_k=3,
+)
+```
+
+`return_field` 是必填首参,`top_k` 的真实约束为 1..5。真实本地索引审查值为 115 posts、45,972 chunks、323,632 tokens;有 keyword 时使用外部 embedding 服务生成向量。Adapter 必须固定索引版本、账号、字段、top_k、返回字段和 embedding 模型版本,并把命中文档 ID/score 作为 source refs,不能只保留模型摘要。
+
+### 8.5 External Agent 与图片
+
+旧工具:
+
+```python
+external_search_case(keyword, platform_channel, max_count)
+load_images(image_urls)
+```
+
+旧图片加载约束包括最多 12 张、单体/响应大小约束和 30 秒级超时。新 Adapter 必须:
+
+- 只允许配置中的协议和域名;
+- 解析 DNS/重定向后仍阻止内网地址;
+- 限制图片数、单文件大小、总大小、MIME 和超时;
+- 原图存 Artifact,只把安全摘要和引用传给模型;
+- 不在 Validator 阶段重新搜索网络。
+
+外部请求 capability 标为 `external_send`;已冻结资料读取标为 `read`。
+
+调用方提供的 `data_source_url`、Pattern 服务地址、External 搜索地址、图片 URL 及全部重定向统一经过 OutboundPolicy:仅允许配置好的 HTTPS endpoint;每次 DNS 解析和重定向后重验目标地址;拒绝 loopback、link-local、RFC1918、云 metadata 和非预期端口;限制连接/读取超时、响应字节和 content type。业务请求不得用任意 `data_source_url` 绕过 External Adapter 的 SSRF 防线。
+
+### 8.6 Knowledge Agent
+
+旧 `search_knowledge` 数据源审查到 16 条知识记录。新 Adapter 冻结知识库版本、查询、命中 ID、内容 hash 和排名。知识库规模变化时,已有 Mission 仍按旧证据版本验证。
+
+### 8.7 结构化写工具
+
+复用范围包括 Paragraph、Element、Paragraph-Element relation、方向、领域信息、数据决策、参考记录等能力。所有写调用先经过:
+
+```python
+async def execute_candidate_write(
+    tool_name: str,
+    args: dict,
+    context: ProtectedAgentContext,
+) -> ToolResult:
+    workspace = await resolve_attempt_workspace(context)
+    await ensure_legacy_task_plan(workspace, context.task_id)
+    normalized = normalize_tool_args(tool_name, args)
+    result = await legacy_adapter.execute(
+        tool_name,
+        normalized,
+        script_build_id=workspace.script_build_id,
+        branch_id=workspace.branch_id,
+    )
+    return normalize_tool_result(result)
+```
+
+上一代 Implementer 对 `branch_id>0` 的第一次 Paragraph/Element 写入要求先有 `record_task_plan`。Adapter 自动创建 Task→PlanStep 兼容投影,不能把该历史门禁交给模型记忆。它只是旧表兼容记录,不成为新系统控制面。
+
+### 8.8 创作角色的只读工具矩阵
+
+| Preset | 允许的旧只读能力 | 产物/用途 | 禁止项 |
+|---|---|---|---|
+| Direction Worker | topic detail、persona points、accepted evidence、strategy snapshot | `ScriptDirectionArtifactV1` | live 输入、正式写入、外部搜索 |
+| Structure Worker | accepted direction、topic composition、section patterns、script snapshot | 段落结构候选 | 跨 Attempt branch、发布 |
+| Paragraph Worker | accepted direction/structure/evidence、persona points、reference detail | Paragraph 候选 | 未接受检索结果、其他候选最新值 |
+| Element Worker | 当前 Attempt Paragraph、accepted evidence、关系读取 | Element/Link 候选,含 commonality/topic_support/weight/support elements | branch0、状态更新 |
+| Compare Worker | 明确列出的 frozen candidate refs | 比较报告 | 自动合并、候选改写 |
+| Root Worker | `accepted_child_decision_ids` 闭合后的 frozen refs、输入快照 | `StructuredScriptArtifactV1` | 任意 latest 查询、正式发布 |
+
+产品方案附录列出的 57 个 Schema 资产要在实现前建立机器可读清单,逐项记录 `legacy_name → adapter_name → preset allowlist → capability → input schema → output schema → timeout → evidence/ref policy → implemented/tested`。未进入某一 preset 白名单的 Schema 即使文件存在也不可调用;当前约 38 个业务工具运行并集不是授权默认值。
+
+## 9. Attempt 候选隔离和因果绑定
+
+### 9.1 工作区分配
+
+```python
+async def resolve_attempt_workspace(context) -> CandidateWorkspace:
+    assert context.role == "worker"
+    binding = await binding_repo.require_by_root(context.root_trace_id)
+    version = await artifact_repo.get_or_create_for_attempt(
+        script_build_id=binding.script_build_id,
+        task_id=context.task_id,
+        attempt_id=context.attempt_id,
+        spec_version=context.spec_version,
+    )
+    return CandidateWorkspace.from_version(version)
+```
+
+不使用 `SELECT MAX(branch_id)+1`。先插入 `script_build_artifact_version` 获得数据库全局自增 ID,再把该 ID 作为 `branch_id>0`,从而并发无冲突。
+
+### 9.2 固定子任务输入
+
+框架在 Attempt 创建时写入:
+
+- `None`:升级前 Attempt 未记录,因果未知;
+- `()`:新 Attempt 明确没有直接子任务输入;
+- 非空 tuple:按直接子任务 `display_path` 顺序冻结其 ACCEPT Decision ID。
+
+业务 Worker 解析每个 Decision 时必须闭合:
+
+```text
+Decision 唯一且 action=ACCEPT
+→ Decision.task_id 是父 Task 的直接子 Task
+→ Decision.attempt_id 存在且归属正确
+→ Attempt.submission 存在
+→ Validation passed 且绑定同一 Attempt/Snapshot
+→ ArtifactSnapshot.sha256 与提交一致
+→ ArtifactRef 指向 frozen 业务版本且 digest 一致
+```
+
+字段为 `None` 的遗留 RUNNING Attempt 不允许扫描“当前子任务最新状态”猜输入,按 `PROTOCOL_VIOLATION` 进入正常 replan/retry。
+
+### 9.3 冻结
+
+Worker 调用 `submit_attempt` 前,Gateway 执行:
+
+1. 读 Attempt branch 的 Paragraph/Element/Link;
+2. 规范化为对应业务 Artifact;
+3. 校验内部 ID、排序和引用;
+4. 将 canonical JSON 写入 `script_build_artifact_version`;
+5. 计算 hash,状态改为 frozen;
+6. 构造 ArtifactRef 和 EvidenceRef;
+7. 再提交框架 Attempt manifest。
+
+freeze 后所有写 Adapter 必须拒绝该 attempt。repair/retry 创建新 Attempt 和新 version;即使框架允许 repair 续用 Worker Trace,也不能写旧 branch。
+
+框架提交顺序是先 freeze ArtifactSnapshot 再提交 Ledger,CAS/所有权失败时可能留下未绑定的 orphan Snapshot;业务侧同理可能先形成 frozen version、随后 `submit_attempt` 失败。Reconciler 以 `attempt_id + digest` 检查 Ledger:已绑定则保持 frozen;Attempt 仍可安全重试提交则只重放同一 manifest;Attempt 已终止或归属丢失则标记 business version 为 discarded。绝不把 orphan 自动挂到“最新 Attempt”,定期清理也必须保留审计期。
+
+## 10. MySQL 数据设计
+
+### 10.1 原则
+
+保留旧业务表,新增恰好四类业务表。使用编号、可回滚、带 checksum 的 SQL migration;不在运行时 `create_all()`。字符集、时区和引擎继承现有库规范。
+
+### 10.2 `script_build_mission_binding`
+
+```sql
+CREATE TABLE script_build_mission_binding (
+  id BIGINT PRIMARY KEY AUTO_INCREMENT,
+  script_build_id BIGINT NOT NULL,
+  root_trace_id VARCHAR(128) NOT NULL,
+  input_snapshot_id BIGINT NOT NULL,
+  active_direction_artifact_version_id BIGINT NULL,
+  accepted_root_artifact_version_id BIGINT NULL,
+  engine_version VARCHAR(64) NOT NULL,
+  schema_version VARCHAR(32) NOT NULL,
+  created_at DATETIME(6) NOT NULL,
+  updated_at DATETIME(6) NOT NULL,
+  UNIQUE KEY uq_binding_build (script_build_id),
+  UNIQUE KEY uq_binding_root (root_trace_id)
+);
+```
+
+该表只保存业务关联和当前正式指针,不保存 root_status、focused_task、pending count 或 Decision 副本。
+
+explicit 模式中的 Planner 主 Trace ID 就是 `root_trace_id`;不另存 `planner_trace_id`,避免制造双身份。Worker/Validator 子 Trace 通过框架 parent/root 关联查询。
+
+### 10.3 `script_build_input_snapshot`
+
+```sql
+CREATE TABLE script_build_input_snapshot (
+  id BIGINT PRIMARY KEY AUTO_INCREMENT,
+  script_build_id BIGINT NOT NULL,
+  version INT NOT NULL,
+  canonical_json JSON NOT NULL,
+  canonical_sha256 CHAR(64) NOT NULL,
+  prompt_manifest_json JSON NOT NULL,
+  schema_version VARCHAR(32) NOT NULL,
+  created_at DATETIME(6) NOT NULL,
+  UNIQUE KEY uq_input_version (script_build_id, version),
+  UNIQUE KEY uq_input_hash (script_build_id, canonical_sha256)
+);
+```
+
+### 10.4 `script_build_artifact_version`
+
+```sql
+CREATE TABLE script_build_artifact_version (
+  id BIGINT PRIMARY KEY AUTO_INCREMENT,
+  script_build_id BIGINT NOT NULL,
+  task_id VARCHAR(128) NOT NULL,
+  attempt_id VARCHAR(128) NOT NULL,
+  spec_version INT NOT NULL,
+  artifact_type VARCHAR(64) NOT NULL,
+  legacy_branch_id BIGINT NULL,
+  base_revision BIGINT NULL,
+  parent_artifact_version_id BIGINT NULL,
+  canonical_json JSON NULL,
+  canonical_sha256 CHAR(64) NULL,
+  state VARCHAR(16) NOT NULL,
+  created_at DATETIME(6) NOT NULL,
+  frozen_at DATETIME(6) NULL,
+  published_at DATETIME(6) NULL,
+  UNIQUE KEY uq_artifact_attempt (attempt_id),
+  UNIQUE KEY uq_artifact_branch (script_build_id, legacy_branch_id),
+  KEY ix_artifact_task (script_build_id, task_id)
+);
+```
+
+状态仅允许 `draft/frozen/published/discarded`。`canonical_json` 在 draft 可为空,freeze 后不可变。它保存候选正文版本,不复制框架 Task 状态。
+
+### 10.5 `script_build_publication`
+
+```sql
+CREATE TABLE script_build_publication (
+  id BIGINT PRIMARY KEY AUTO_INCREMENT,
+  script_build_id BIGINT NOT NULL,
+  publication_type VARCHAR(16) NOT NULL,
+  accept_decision_id VARCHAR(128) NOT NULL,
+  artifact_version_id BIGINT NOT NULL,
+  expected_sha256 CHAR(64) NOT NULL,
+  state VARCHAR(16) NOT NULL,
+  attempt_count INT NOT NULL DEFAULT 0,
+  publication_revision BIGINT NOT NULL DEFAULT 0,
+  last_error_code VARCHAR(64) NULL,
+  last_error_summary VARCHAR(1000) NULL,
+  created_at DATETIME(6) NOT NULL,
+  updated_at DATETIME(6) NOT NULL,
+  published_at DATETIME(6) NULL,
+  UNIQUE KEY uq_publication_decision (accept_decision_id),
+  KEY ix_publication_build (script_build_id, publication_type, state)
+);
+```
+
+`publication_type` 为 `direction` 或 `final`。`last_error_summary` 必须脱敏。唯一 Decision 约束使重试幂等地发布同一 Artifact。
+
+## 11. 验证设计
+
+### 11.1 Retrieval Validator
+
+检查:
+
+- 工具和数据源与 Task 匹配;
+- query、账号、索引/知识版本记录完整;
+- source refs 可读取、digest 匹配;
+- summary 没有超出来源的确定性断言;
+- limitations 和空结果被如实表达;
+- 每个硬性 criterion 有结论和证据。
+
+### 11.2 Candidate Validator
+
+方向任务检查目标数量、选题引用、人设和策略依据、目标是否可验收;结构/段落任务检查层级、顺序、内容职责、元素关系、事实证据、重复与冲突;比较任务检查候选独立性和比较维度,不自动合并。
+
+### 11.3 Root Validator
+
+顺序:
+
+1. schema、digest、ID 和关系确定性预检;
+2. 确认 Root Attempt 绑定的直接 child ACCEPT Decisions;
+3. 检查 accepted direction 版本;
+4. 检查选题覆盖、叙事完整性、段落分工、元素落位;
+5. 检查事实/表达分离和证据闭合;
+6. 检查人设、风格、实用性和合规性;
+7. 运行旧详情 canonical projection dry-run;
+8. 输出逐 criterion 报告。
+
+Validator passed 仍不自动完成 Task;Planner 必须调用 ACCEPT Decision。
+
+## 12. 正式发布
+
+### 12.1 Publisher 接口
+
+```python
+class ScriptPublisher:
+    async def publish(
+        self,
+        root_trace_id: str,
+        accept_decision_id: str,
+        artifact_ref: ArtifactRef,
+    ) -> PublicationResult: ...
+
+    async def readback_and_verify(
+        self,
+        script_build_id: int,
+        expected_digest: str,
+    ) -> bool: ...
+```
+
+### 12.2 发布算法
+
+1. 一次读取 Mission Snapshot,定位 Root 最终 ACCEPT;
+2. 验证 Decision→Attempt→Validation→ArtifactSnapshot 闭合;
+3. 要求 Snapshot 恰有一个正式整稿 ArtifactRef;
+4. 从业务表读取 frozen version 并验证 digest;
+5. 检查 durable stop intent/execution epoch;已请求停止则不进入 publication;
+6. 插入或锁定 `script_build_publication`;
+7. 若已 published 且 digest 相同,直接返回成功;
+8. 复用 Mission OwnerLease,在 MySQL 事务中 `SELECT ... FOR UPDATE` 锁定 build/publication;
+9. 锁定后再次读取 stop intent/execution epoch;命中则 rollback,publication 保持 pending/failed,build 保持 stopping;
+10. 只替换该 `script_build_id + branch_id=0` 的 Paragraph/Element/Link,并更新计数、正式指针和 publication revision;
+11. **在同一事务、同一 Session 中**复用旧详情 repository 做 canonical readback 并比对 hash;
+12. mismatch 或读取异常立即 rollback,未验证正文不得对旧 GET 可见;
+13. digest 一致后、commit 前最后重检 stop intent/execution epoch;若发生变化,rollback 全部 branch0、digest、pointer 和 revision 写入;
+14. 无 stop 时在同一事务将 publication 标为 published、Artifact 标为 published、build 标为 success,然后 commit;
+15. commit 后再通过正常旧 HTTP/独立只读 Session 做健康读回;若此处异常,告警并进入一致性修复,不能生成新正文或新 ACCEPT;
+16. 后续重试永远使用同一 accept Decision 和 frozen version。
+
+本方案锁定“事务内 branch0 投影 + 同 Session 回读 + digest 门禁”实现,不在首版引入 shadow/active 双表方案。替换前精确锁定单个 build;任何删除都必须带显式 `script_build_id` 和 `branch_id=0` 条件,禁止 unresolved 变量、宽范围条件和跨 build 操作。
+
+### 12.3 状态映射
+
+```text
+Mission active                     → build running
+Stop 已请求、Operation 尚未终止    → build stopping
+Stop 已完成                        → build stopped
+达到预算但存在经验证的可用结果     → build partial(必须写 error_message/剩余问题)
+Root 暂时 blocked、仍可恢复          → build running(阻塞原因放 summary/detail)
+Root 终局 blocked 或不可恢复失败     → build failed(blocked_reason 投影到 error_message)
+Root completed、publication 进行中 → build running;内部状态仅在 publication 表表达
+Publication failed                 → build failed,绝不是 success
+事务内 readback verified           → build success
+```
+
+`ScriptBuildRecord.status` 只使用旧枚举 `running/stopping/success/failed/partial/stopped`。`pending/publishing/published/failed` 等发布细分只属于 `script_build_publication.state` 或 API detail,不扩展旧 status 字段。
+
+## 13. HTTP 与 SDK
+
+### 13.1 保持旧路由
+
+精确保留当前代码中的路由:
+
+| Method | Path | 兼容职责 |
+|---|---|---|
+| POST | `/api/pattern/script_builds` | 标准启动 |
+| POST | `/api/pattern/script_builds/parse_topic_json` | 解析上传 Topic JSON |
+| POST | `/api/pattern/script_builds/from_topic_json` | 从解析结果启动 |
+| POST | `/api/pattern/script_builds/{id}/retry` | 旧失败构建重试入口 |
+| GET | `/api/pattern/script_builds/{id}/prefill` | 取得重试/复制预填内容 |
+| DELETE | `/api/pattern/script_builds/{id}` | 软删除 |
+| PATCH | `/api/pattern/script_builds/{id}/favorite` | 收藏状态 |
+| GET | `/api/pattern/script_builds` | 列表 |
+| GET | `/api/pattern/script_builds/{id}` | 旧正式详情 |
+| GET | `/api/pattern/script_builds/{id}/log` | 旧日志投影 |
+| POST | `/api/pattern/script_builds/{id}/stop` | 请求停止,先进入 stopping |
+| POST | `/api/pattern/script_builds/{id}/resume` | 按 Root/publication 状态分支恢复 |
+| GET | `/api/pattern/script_builds/{id}/trace_messages` | 旧 Trace 消息投影 |
+| GET | `/api/pattern/script_builds/overview/{topic_build_id}` | Topic Build 下的概览 |
+| GET | `/script_build_detail/{id}` | 旧详情 HTML 入口,仅兼容、不在本期改 UI |
+
+请求和响应字段以旧黑盒 contract test 固化,新字段只能兼容追加。旧 start 和 retry 默认每次调用都创建新的 `script_build_record`,即使 body 相同也不能按 fingerprint 合并;同一个 `script_build_id` 内的 Mission 创建/命令仍按框架 request fingerprint 幂等。只有调用方显式提供可选 `Idempotency-Key` 时,HTTP 层才在该 endpoint/principal/有效期内复用响应。stop/resume 对同状态重复调用幂等;retry 重复调用默认仍是两次新构建。稳定错误为:请求/状态不合法 `400/409`,未认证 `401`,无权访问 `403`,不存在或为防枚举隐藏 `404`,内部执行错误 `500`;响应错误体包含稳定 `error_code`,不暴露路径、连接串和上游 token。
+
+### 13.2 新业务观测路由
+
+```http
+GET /api/pattern/script_builds/{id}/mission
+GET /api/pattern/script_builds/{id}/mission/events?after=&limit=
+GET /api/pattern/script_builds/{id}/tasks/{task_id}
+GET /api/pattern/script_builds/{id}/artifacts/{artifact_version_id}
+GET /api/pattern/script_builds/{id}/publication
+```
+
+这些接口通过 binding 查 `root_trace_id`,再用 `OrchestrationClient`/Coordinator 读取,不直接解析 ledger.json。Artifact 接口返回业务 canonical 内容时必须校验 build 归属,禁止任意 URI 文件读取。
+
+### 13.3 统一认证授权
+
+旧业务路由、新 Mission 路由、通用 V2、Trace detail 和 WebSocket 必须经过同一个 AuthN/AuthZ middleware:
+
+1. 验证调用主体并得到 principal/tenant;
+2. 先用 principal 校验 `script_build_id` 的读取或控制权限;
+3. 再从受权 binding 解析 `root_trace_id`;禁止调用方仅凭 root/task/trace ID 越权;
+4. task、attempt、validation、snapshot、artifact 和 trace 必须反向闭合到同一 build/root;
+5. WebSocket 在 upgrade 前鉴权,并在长连接期间执行权限撤销/过期策略;
+6. 审计允许/拒绝结果但不记录 credential。
+
+`root_trace_id`、`task_id` 和 Artifact URI 都不是权限凭证。黑盒测试必须覆盖未登录 401、跨租户 403/隐藏 404、跨 build Artifact/Trace、伪造 WebSocket 订阅和合法同 build 读取。
+
+### 13.4 通用 V2 路由
+
+Host composition 必须显式挂载当前框架的:
+
+```text
+/api/v2/capabilities
+/api/v2/roots/{root_trace_id}/snapshot
+/api/v2/roots/{root_trace_id}/completion
+/api/v2/roots/{root_trace_id}/snapshots/{snapshot_id}
+/api/v2/roots/{root_trace_id}/events
+Task / Attempt / Validation / Operation detail
+Trace query/detail/websocket
+```
+
+当前根 `agent/api_server.py` 不会自动挂载 orchestration router;必须在业务 Host 的 composition root `include_router(create_orchestration_router(coordinator))`。V2 Operation API 只能控制已存在 Task,不负责启动 Planner Mission,所以旧 start 路由仍由 `ScriptMissionService` 实现。
+
+### 13.5 客户端读取原则
+
+观测消费者优先调用:
+
+```python
+await client.get_mission_snapshot(root_trace_id)
+await client.get_root_completion(root_trace_id)
+await client.get_artifact_snapshot(root_trace_id, snapshot_id)
+await client.list_events(root_trace_id, cursor=cursor)
+await client.list_correlated_traces(
+    root_trace_id=root_trace_id,
+    task_id=task_id,
+    attempt_id=attempt_id,
+)
+```
+
+不得分别读取多个列表后自行假设属于同一 Ledger revision;完整状态用原子 Mission Snapshot。
+
+## 14. 一个真实输入驱动的 Agentic 示例
+
+### 14.1 启动
+
+以真实样本链路为参照,旧 `POST /api/pattern/script_builds` 的请求合同是:
+
+```json
+{
+  "execution_id": 401,
+  "topic_build_id": 1189,
+  "topic_id": 1352,
+  "agent_type": "AigcAgent",
+  "agent_config": {"model_name": "<configured-model>"},
+  "data_source_url": "<configured-allowlisted-endpoint>",
+  "strategies_always_on": [],
+  "strategies_on_demand": []
+}
+```
+
+`account_name` 不在旧 POST body 中;Host 从 `TopicBuildRecord.personal_config` 派生出真实账号“每天心理学”。Host 通过只读 Repository 读取完整 Topic/Points/Composition/Relations/Sources、人设点、Section Pattern、策略和 Prompt 实际版本,冻结 `input-454-v1`。Root objective 使用 `topic.result/topic_direction` 的规范摘要,只描述“为该账号和选题构建兼容正式结构化脚本”。
+
+### 14.2 动态规划
+
+Planner 首次检查输入后可能创建:
+
+```text
+T1 Decode:检索该账号关于损失厌恶/拖延决策的历史表达
+T2 Knowledge:核验“毛驴效应”定义和适用边界
+T3 Direction:综合选题、人设、策略与已接受证据形成方向
+```
+
+这只是一次可能决策,不是编码在后端的固定 Workflow。若 Topic 本身已有充分证据,Planner 可以不创建 External Task;若定义存在争议,可以再创建 Pattern 或 External Task。
+
+### 14.3 失败、修订、接受
+
+T1 v1 的 Decode 查询过窄,Validator 判定只找到弱相关片段。Planner 选择 revise:扩大 match_fields 并调整 keyword。T1 v2 返回可追溯命中,Validator passed,Planner ACCEPT。T3 Attempt 创建时冻结 T1 v2 和 T2 的 ACCEPT Decision ID;随后即使 T1 又产生实验版本,T3 也不会漂移。
+
+Direction Worker 输出三类结构化目标,可与旧样本的认知深度、实用落地、人设一致性对应。Validator 核对选题、人设、证据和可验收性,Planner ACCEPT 后 Host 发布 legacy markdown 投影到方向字段。
+
+### 14.4 创作与 Root
+
+Planner 根据方向建立结构/段落/元素任务;每个 Worker 写独立 Attempt branch。某段 Validator 发现“心理学概念被写成无条件事实”,Planner repair 该段而不是重跑整条流水线。已接受局部结果由综合任务吸收;Root Worker 只消费冻结的直接 child Decisions,产出 5 Paragraph、11 Element、13 Link 的候选只是示例,不是硬编码数量。
+
+Root Validator 通过后,Planner ACCEPT。Publisher 将同一 frozen Artifact 投影到 `branch_id=0`,旧详情回读得到相同 canonical digest 后才把状态设为 success。最终业务输出与旧版相同,过程不再依赖 Round 1/2 和 4 个固定 Branch。
+
+## 15. 三阶段实施计划
+
+### 阶段一:输入、Root、方向和取数闭环
+
+实现:
+
+- `script_build_host/` composition 和配置;
+- 四张业务表的 migration 基础版本;
+- InputSnapshotService 和真实旧数据 Adapter;
+- MissionFactory、MissionService.start/run/stop;
+- `script_planner`、Direction Worker、四个 Retrieval Worker;
+- Retrieval/Direction Validator;
+- Pattern、Decode、External、Knowledge、图片工具 Adapter;
+- 显式挂载 V2 Mission/Trace API;
+- 凭据轮换、读写账号分离、日志脱敏。
+
+暂不实现:Paragraph/Element 正式写入、正式发布、页面。
+
+验收:真实旧输入启动;输入 hash 稳定;Decode v1 fail→revise→v2 pass→ACCEPT;Direction Attempt 只消费固定 Decision;Root objective 可 revise;无 Round/multipath 固定计划;Worker 无法创建 Agent 或调用未授权写工具。
+
+### 阶段二:候选结构化创作
+
+实现:
+
+- ArtifactRepository 和 Attempt→version→branch 绑定;
+- Paragraph/Element/Link 等旧写工具 Adapter;
+- `record_task_plan` 自动兼容;
+- Structure/Paragraph/Element/Compare Worker;
+- Candidate Validator;
+- freeze、canonical JSON、digest 和 ArtifactRef;
+- repair/retry/revise 的新版本策略;
+- 业务 Artifact/Task 只读 API。
+
+验收:并行 Attempt 分配不同 branch;freeze 后不可写;repair 新建 version;Validator 只能读绑定 version;重复/乱序/跨任务/非 ACCEPT child Decision 被拒绝;旧 Element 扩展字段能力边界有测试。
+
+### 阶段三:Root、发布与恢复
+
+实现:
+
+- Root Worker 和 Root Validator;
+- ScriptPublisher 与 publication 幂等状态;
+- `branch_id=0` 事务投影、旧详情读回和 digest 门禁;
+- success 状态投影;
+- stop/resume/recover;
+- 单机单活锁与启动自检;
+- 全链路 Event/Trace 关联;
+- 旧 API 黑盒兼容 E2E。
+
+验收:Root ACCEPT 后模拟首次发布失败,状态不为 success;重试只发布同一 Artifact;读回一致后 success;Decision→Attempt→Validation→Snapshot→ArtifactVersion→Publication→Trace 全闭合;进程重启不猜测遗留 RUNNING Attempt。
+
+## 16. 测试方案
+
+### 16.1 单元测试
+
+- canonical JSON 和 digest 稳定;
+- Topic/Persona/Strategy/Prompt 快照完整;
+- Root TaskSpec 不包含固定 Workflow;
+- Gateway 身份从 protected context 获取;
+- branch ID 并发唯一;
+- frozen version 拒写;
+- ArtifactRef/build/task/attempt 归属检查;
+- Direction 和 StructuredScript schema;
+- Validator criterion 覆盖;
+- Publisher 幂等和 readback mismatch;
+- OwnerLease token 交接、嵌套 finalize 不重入加锁、重复 start/resume/finalize 的稳定 409;
+- 旧六状态映射,尤其 stopping、partial、Root completed+publication failed;
+- Legacy Detail Projection 的 topic/decision_traces/post_datas/script_datas/build_workflow 和 Paragraph 内嵌关系;
+- principal→build/root/task/artifact/trace 权限闭合与 401/403/404;
+- 日志脱敏、URL allowlist 和图片限制。
+
+### 16.2 集成测试
+
+- MySQL migration 前进/回滚/checksum;
+- 旧 Topic/Persona 文件和 DB 混合读取;
+- Pattern polling timeout;
+- Decode 索引版本和空命中;
+- External redirect/SSRF/超限;
+- Knowledge 版本冻结;
+- Attempt branch 写、freeze、repair;
+- FileStore reload 和 operation recovery;
+- `stop` 与 active run/finalize 竞态:先 stopping、只发 intent,不争夺 lease;Publisher 三次检查 intent/epoch,命中后 branch0、digest、publication revision 均不变化;
+- 生产 Planner 只能通过 `dispatch_script_tasks` 创建 durable Operation,同步 dispatch 工具不可见;
+- 旧详情 canonical readback,包括 Element 扩展字段和全部辅助字段;
+- 同事务 branch0 projection+readback mismatch rollback;
+- frozen business version/ArtifactSnapshot orphan reconcile;
+- V2/Trace/WebSocket 共用授权策略。
+
+### 16.3 黑盒 E2E
+
+1. 只通过旧公开入口提交真实格式输入;
+2. Host 冻结选题、人设、策略和 Prompt;
+3. Planner 动态创建取数任务;
+4. Child v1 验证失败并 revise;
+5. Child v2 通过并 ACCEPT;
+6. Root objective revise;
+7. 创作 Worker 写 Attempt 专属候选;
+8. Root Worker 消费冻结的 Child ACCEPT Decisions;
+9. Root Validator 通过,Planner ACCEPT;
+10. 第一次发布故障不产生 success;
+11. 同一 Artifact 重试发布;
+12. 旧详情返回 Paragraph/Element/Link、Element 扩展字段、topic、decision_traces、post_datas、script_datas、build_workflow 和 Paragraph 内嵌关系;
+13. 消费端只使用公开 API/SDK 读取 Mission、Completion、Artifact、Event 和 Trace;
+14. 未认证、跨 tenant、跨 build Artifact/Trace/WS 被拒绝;
+15. 响应中无业务之外的 Script Workflow/Round/Page 通用节点。
+
+### 16.4 真实基线回归
+
+以 Run 454 的 5 Paragraph、11 Element、13 Link 作为“旧详情可表达性”样本,而不是新模型必须逐字复现的金标。回归比较:
+
+- 输入字段覆盖率;
+- 输出 schema 和关系可读性;
+- 方向三类目标的可表示性;
+- 事实引用闭合率;
+- 人设/选题/策略 criterion 覆盖;
+- 旧接口和状态兼容;
+- Agentic 失败修订和因果冻结。
+
+## 17. 运行、观测与告警
+
+默认 Event 真源是 TaskStore durable journal,不默认写重复 `orchestration/events.jsonl`。业务告警基于 Event cursor 和 publication 状态:
+
+```text
+Attempt/Validation 超时
+连续 repair/retry 超预算
+Root 长时间无进展
+Artifact digest mismatch
+publication failed
+legacy readback mismatch
+FileStore revision conflict
+外部取数超时/被安全策略拒绝
+```
+
+Trace 查询必须带 `root_trace_id`,可进一步按 `agent_role`、`task_id`、`attempt_id`、`validation_id`、`operation_id` 过滤。详情和 WebSocket 使用过滤后 limit,不能先截 1000 条再查目标。
+
+## 18. 明确不做
+
+- 不实现 SQL TaskStore、SQL ArtifactSnapshot Store 或分布式 Operation Queue;
+- 不让多个 Host 并发驱动同一 Root;
+- 不修改通用模型加入脚本字段;
+- 不复制 Ledger 到业务状态表;
+- 不让 Agent 直接写 `branch_id=0` 或 status=success;
+- 不保留固定 Round/Branch/multipath 作为控制流程;
+- 不把 57 个 Schema 全量授权给任一角色;
+- 不把 Planner 当长文本共同写作者;
+- 不在本期修改 HTML、visualization 或新增页面;
+- 不把真实凭据写入配置、文档、Trace 或测试 fixture。
+
+## 19. 交付定义
+
+阶段三结束时,业务团队获得的是一个可装配的 Agentic Script Host:输入、取数和正式输出仍兼容上一代;主 Agent 持续围绕 Root 宏观目标规划;专业 Worker 只在受控工作区执行;Validator 独立核验固定版本;Planner 对报告做正式决策;Root 接受后由确定性 Publisher 发布并回读。任何一层失败都有可恢复状态和可追踪证据,而不会通过扫描“最新数据”或复用未验证结果掩盖因果缺口。
+
+这套方案以当前已完成的通用 Agent 框架为控制内核,以旧业务数据和工具 Adapter 为能力面,以四张 MySQL 业务表为冻结与发布边界,以 FileStore 单机单活为明确运行前提。它替换的是 Workflow,不替换业务输入输出,也不建立第二套 Mission 真相。