Просмотр исходного кода

收敛制品绑定协议并扁平化项目目录

SamLee 3 дней назад
Родитель
Сommit
f331965bd6
100 измененных файлов с 336 добавлено и 308 удалено
  1. 0 0
      .env.example
  2. 2 2
      .gitignore
  3. 0 0
      .python-version
  4. 6 6
      README.md
  5. 11 9
      docs/adr/0001-source-asset-expectation-binding.md
  6. 0 0
      examples/studio_input.json
  7. 1 1
      langgraph.json
  8. 0 0
      production_build_agents/__init__.py
  9. 0 0
      production_build_agents/agents/__init__.py
  10. 0 0
      production_build_agents/agents/executor/__init__.py
  11. 14 11
      production_build_agents/agents/executor/agent.py
  12. 1 1
      production_build_agents/agents/executor/context.py
  13. 5 4
      production_build_agents/agents/executor/prompt.md
  14. 0 0
      production_build_agents/agents/executor/provenance.py
  15. 0 0
      production_build_agents/agents/executor/skills/external-research/SKILL.md
  16. 0 0
      production_build_agents/agents/executor/skills/image-production/SKILL.md
  17. 0 0
      production_build_agents/agents/executor/skills/reference-inspection/SKILL.md
  18. 0 0
      production_build_agents/agents/executor/skills/registry.py
  19. 0 0
      production_build_agents/agents/executor/skills/structured-analysis/SKILL.md
  20. 0 0
      production_build_agents/agents/executor/skills/video-production/SKILL.md
  21. 0 24
      production_build_agents/agents/invocation.py
  22. 0 0
      production_build_agents/agents/planner/__init__.py
  23. 8 8
      production_build_agents/agents/planner/agent.py
  24. 0 0
      production_build_agents/agents/planner/prompt.md
  25. 0 0
      production_build_agents/agents/planner/skills/__init__.py
  26. 0 0
      production_build_agents/agents/planner/skills/global-data-planning/SKILL.md
  27. 0 0
      production_build_agents/agents/planner/skills/registry.py
  28. 0 0
      production_build_agents/agents/validator/__init__.py
  29. 5 3
      production_build_agents/agents/validator/prompt.md
  30. 4 4
      production_build_agents/agents/validator/skills/global-data-stage-validation/SKILL.md
  31. 0 0
      production_build_agents/agents/validator/skills/image-validation/SKILL.md
  32. 0 0
      production_build_agents/agents/validator/skills/reference-validation/SKILL.md
  33. 0 0
      production_build_agents/agents/validator/skills/registry.py
  34. 0 0
      production_build_agents/agents/validator/skills/research-validation/SKILL.md
  35. 0 0
      production_build_agents/agents/validator/skills/structured-validation/SKILL.md
  36. 0 0
      production_build_agents/agents/validator/skills/video-validation/SKILL.md
  37. 10 10
      production_build_agents/agents/validator/stage_agent.py
  38. 13 13
      production_build_agents/agents/validator/stage_context.py
  39. 2 2
      production_build_agents/agents/validator/task_agent.py
  40. 1 1
      production_build_agents/agents/validator/task_context.py
  41. 0 0
      production_build_agents/app.py
  42. 0 0
      production_build_agents/contracts/__init__.py
  43. 82 82
      production_build_agents/contracts/evaluation.py
  44. 24 18
      production_build_agents/contracts/models.py
  45. 13 13
      production_build_agents/global_data_stage.py
  46. 0 0
      production_build_agents/graph.py
  47. 0 0
      production_build_agents/graph_nodes/__init__.py
  48. 0 0
      production_build_agents/graph_nodes/common.py
  49. 0 0
      production_build_agents/graph_nodes/create_plan.py
  50. 8 8
      production_build_agents/graph_nodes/create_replan.py
  51. 0 0
      production_build_agents/graph_nodes/execute_task.py
  52. 1 1
      production_build_agents/graph_nodes/finish_run.py
  53. 0 0
      production_build_agents/graph_nodes/prepare_brief.py
  54. 0 0
      production_build_agents/graph_nodes/prepare_task.py
  55. 2 2
      production_build_agents/graph_nodes/validate_stage.py
  56. 2 2
      production_build_agents/graph_nodes/validate_task.py
  57. 0 0
      production_build_agents/preprocess/__init__.py
  58. 0 0
      production_build_agents/preprocess/production_brief.py
  59. 0 0
      production_build_agents/preprocess/source_assets.py
  60. 0 0
      production_build_agents/run/__init__.py
  61. 0 0
      production_build_agents/run/langgraph_checkpointer.py
  62. 0 0
      production_build_agents/run/lock.py
  63. 0 0
      production_build_agents/run/operation_journal.py
  64. 0 0
      production_build_agents/run/records.py
  65. 0 0
      production_build_agents/run/registry.py
  66. 0 0
      production_build_agents/state.py
  67. 0 0
      production_build_agents/tools/README.md
  68. 0 0
      production_build_agents/tools/__init__.py
  69. 28 0
      production_build_agents/tools/brief_reader.py
  70. 0 0
      production_build_agents/tools/discovery.py
  71. 0 0
      production_build_agents/tools/media.py
  72. 0 0
      production_build_agents/tools/publishing.py
  73. 0 0
      production_build_agents/tools/registry.py
  74. 1 1
      pyproject.toml
  75. 0 0
      run_demo.py
  76. 0 0
      tests/__init__.py
  77. 1 0
      tests/agents/__init__.py
  78. 15 13
      tests/agents/test_executor.py
  79. 6 5
      tests/agents/test_planner.py
  80. 9 7
      tests/agents/test_task_validator.py
  81. 1 0
      tests/contracts/__init__.py
  82. 28 26
      tests/contracts/test_evaluation.py
  83. 0 0
      tests/fixtures/minimal_brief.json
  84. 0 0
      tests/fixtures/minimal_input.json
  85. 1 1
      tests/fixtures/v18_replay/deliveries/delivery.Task1.v1.json
  86. 1 1
      tests/fixtures/v18_replay/deliveries/delivery.Task2.v2.json
  87. 0 0
      tests/fixtures/v18_replay/plans/plan.v1.json
  88. 0 0
      tests/fixtures/v18_replay/plans/plan.v2.json
  89. 0 0
      tests/fixtures/v18_replay/production_brief.json
  90. 6 6
      tests/fixtures/v18_replay/stage_reports/expected_stage.v1.json
  91. 6 6
      tests/fixtures/v18_replay/stage_reports/expected_stage.v2.json
  92. 1 1
      tests/fixtures/v18_replay/tasks/task.Task1.v1.json
  93. 1 1
      tests/fixtures/v18_replay/tasks/task.Task2.v2.json
  94. 1 1
      tests/fixtures/v18_replay/validation_reports/report.Task1.v1.json
  95. 1 1
      tests/fixtures/v18_replay/validation_reports/report.Task2.v2.json
  96. 0 0
      tests/golden_case/preprocess_report.md
  97. 0 0
      tests/golden_case/production_brief.json
  98. 1 0
      tests/graph/__init__.py
  99. 6 6
      tests/graph/test_graph.py
  100. 7 7
      tests/graph/test_recovery.py

+ 0 - 0
production build agents/.env.example → .env.example


+ 2 - 2
.gitignore

@@ -5,5 +5,5 @@
 __pycache__/
 *.py[cod]
 *.egg-info/
-production build agents/demo_output/
-production build agents/.run_registry/
+demo_output/
+.run_registry/

+ 0 - 0
production build agents/.python-version → .python-version


+ 6 - 6
production build agents/README.md → README.md

@@ -21,14 +21,14 @@ Planner 自由决定 Global Data DAG 的任务数量和内容;运行时按
 `priority → task_id` 串行选择 ready Task。通用 Executor 按 `skill_id`
 加载唯一 Skill 和工具白名单,独立 Validator 按执行能力和交付类型选择验证
 Skill。只有 Validator PASS 的 Task 才会解锁依赖。全部 Task PASS 后,独立的
-Stage Validator 还会检查 Requirement 覆盖、Planner 漏项、Artifact 可用性和
+Stage Validator 还会检查 Requirement 满足情况、Planner 漏项、Artifact 可用性和
 跨 Task 冲突;任一层 FAIL 时 Planner 输出完整下一版 DAG,最多 Replan 5 次。
 
-当前生产协议为 `0.2`,不读取旧 Run。覆盖身份链固定为
-`SourceAsset → ArtifactExpectation → ArtifactFulfillment → RequirementCoverage`:
-只有 PASS Delivery 中显式落盘的 Fulfillment 才能满足 Expectation,未绑定的
+当前生产协议为 `0.2`,不读取旧 Run。身份链固定为
+`SourceAsset → ArtifactExpectation → ArtifactExpectationBinding → RequirementEvaluation`:
+只有 PASS Delivery 中显式落盘的 Artifact Binding 才能满足 Expectation,未绑定的
 Artifact 只作为辅助产物。协议决策见
-[`docs/adr/0001-source-asset-expectation-fulfillment.md`](docs/adr/0001-source-asset-expectation-fulfillment.md)。
+[`docs/adr/0001-source-asset-expectation-binding.md`](docs/adr/0001-source-asset-expectation-binding.md)。
 
 完整 Plan 历史、TaskPackage、ExecutorDelivery、ValidationReport、工具调用
 账本和阶段 Delivery 均按 Run 落盘。LangGraph State 只保存状态、路径和摘要,
@@ -50,7 +50,7 @@ uv sync
 把 `.env.example` 复制为 `.env`,配置 Planner、Executor、Validator 和需要
 使用的远程工具。
 工具目录、使用边界和 Skill 授权关系见
-[`src/tools/README.md`](src/tools/README.md)。
+[`production_build_agents/tools/README.md`](production_build_agents/tools/README.md)。
 
 ## 运行
 

+ 11 - 9
production build agents/docs/adr/0001-source-asset-expectation-fulfillment.md → docs/adr/0001-source-asset-expectation-binding.md

@@ -13,7 +13,7 @@
 
 Global Data 使用唯一身份链:
 
-`SourceAsset → ArtifactExpectation → ArtifactFulfillment → RequirementCoverage`
+`SourceAsset → ArtifactExpectation → ArtifactExpectationBinding → RequirementEvaluation`
 
 - `SourceAsset` 是预处理从完整保留版 Production Brief 中发现的显式图片、视频和音频。
   相同类型和精确 URI 合并,保留全部 JSONPath;根字段 `原始视频路径` 不参与扫描。
@@ -21,10 +21,11 @@ Global Data 使用唯一身份链:
   `RequirementN-ExpectationM` 连续编号。需要采纳已有媒体时显式列出
   `source_asset_ids`。
 - `PlannedTask` 通过 `expectation_ids` 声明工作,不声明自己覆盖整个 Requirement。
-- Executor 输出 `FulfillmentClaim`;代码把候选 URI 映射为稳定 Artifact ID,落盘为
-  `ArtifactFulfillment`。
-- `RequirementCoverage` 只由 PASS Delivery 中仍然有效的显式 Fulfillment 推导。
-  没有绑定的 Artifact 是辅助产物,不产生覆盖。
+- Executor 输出 `ArtifactBindingClaim`;代码把候选 URI 映射为稳定 Artifact ID,落盘为
+  `ArtifactExpectationBinding`。
+- `RequirementEvaluation` 只由 PASS Delivery 中仍然有效的正式
+  `ArtifactExpectationBinding` 推导。没有绑定的 Artifact 是辅助产物,
+  不参与满足情况计算。
 
 ## 不变量
 
@@ -34,17 +35,18 @@ Global Data 使用唯一身份链:
 - 只有 `reference-inspection` 可以满足 `source_identity`;生成媒体的 `source_uri`
   只是参考来源。
 - 一个 Artifact 可以满足多个 Expectation,但每条关系必须单独声明。
-- CoverageGap 使用结构化身份和原因码,不解析错误文案。
+- ExpectationGap 使用结构化身份和原因码,不解析错误文案。
 
 ## 数据流
 
 预处理确定性生成 SourceAsset 目录;Planner 建立 Requirement、Expectation 和 Task;
-Executor 交付 Artifact 与 Fulfillment;Task Validator 判断内容质量;纯策略层重新计算
-Stage Coverage;Stage Validator 只判断跨 Task 语义、冲突和未规划业务缺口。
+Executor 交付 Artifact 与正式 Binding;Task Validator 判断内容质量;纯策略层重新计算
+Expectation 和 Requirement 的满足情况;Stage Validator 只判断跨 Task 语义、冲突和
+未规划业务缺口。
 
 ## 拒绝的方案
 
-- 按 Task 成员关系批量覆盖 Requirement:会产生跨 Requirement 借用。
+- 按 Task 成员关系批量关联 Requirement:会产生跨 Requirement 借用。
 - 按类型和数量猜测绑定:无法区分同类型来源。
 - 看到相同 URI 就自动采纳:生成产物也可能携带参考 URI。
 - 保留 0.1 兼容分支:会让新旧身份规则长期并存。

+ 0 - 0
production build agents/examples/studio_input.json → examples/studio_input.json


+ 1 - 1
production build agents/langgraph.json → langgraph.json

@@ -4,7 +4,7 @@
     "."
   ],
   "graphs": {
-    "production_global_data": "./src/production_build_agents/app.py:graph"
+    "production_global_data": "./production_build_agents/app.py:graph"
   },
   "env": ".env"
 }

+ 0 - 0
production build agents/src/production_build_agents/__init__.py → production_build_agents/__init__.py


+ 0 - 0
production build agents/src/production_build_agents/agents/__init__.py → production_build_agents/agents/__init__.py


+ 0 - 0
production build agents/src/production_build_agents/agents/executor/__init__.py → production_build_agents/agents/executor/__init__.py


+ 14 - 11
production build agents/src/production_build_agents/agents/executor/agent.py → production_build_agents/agents/executor/agent.py

@@ -29,7 +29,7 @@ from ...run.langgraph_checkpointer import (
 )
 from ...contracts.models import (
     Artifact,
-    ArtifactFulfillment,
+    ArtifactExpectationBinding,
     ExecutorCandidate,
     ExecutorDelivery,
     TaskPackage,
@@ -478,7 +478,7 @@ def _run_model_until_valid(
                             )
                             + "。顶层必须包含 run_id、plan_id、task_id、"
                             "plan_version、deliverable_type、payload、artifacts、"
-                            "fulfillment_claims、findings、unresolved、summary。"
+                            "artifact_binding_claims、findings、unresolved、summary。"
                             "请逐字复制身份字段,"
                             "在同一 Task 内修正并只输出完整 JSON。"
                             "executor_run_id 不是 "
@@ -533,32 +533,32 @@ def _build_artifacts(
     ]
 
 
-def _build_fulfillments(
+def _build_artifact_expectation_bindings(
     candidate: ExecutorCandidate,
     artifacts: list[Artifact],
-) -> list[ArtifactFulfillment]:
+) -> list[ArtifactExpectationBinding]:
     artifact_ids_by_uri = {
         item.uri: item.artifact_id for item in artifacts
     }
     if not candidate.artifacts and artifacts:
         artifact_ids_by_uri["payload"] = artifacts[0].artifact_id
-    fulfillments: list[ArtifactFulfillment] = []
-    for claim in candidate.fulfillment_claims:
+    artifact_expectation_bindings: list[ArtifactExpectationBinding] = []
+    for claim in candidate.artifact_binding_claims:
         artifact_id = artifact_ids_by_uri.get(claim.artifact_uri)
         if artifact_id is None:
             raise ExecutorOutputError(
-                "FulfillmentClaim.artifact_uri 未对应候选 Artifact:"
+                "ArtifactBindingClaim.artifact_uri 未对应候选 Artifact:"
                 f"{claim.artifact_uri}"
             )
-        fulfillments.append(
-            ArtifactFulfillment(
+        artifact_expectation_bindings.append(
+            ArtifactExpectationBinding(
                 expectation_id=claim.expectation_id,
                 artifact_id=artifact_id,
                 source_asset_id=claim.source_asset_id,
                 evidence_tool_call_ids=claim.evidence_tool_call_ids,
             )
         )
-    return fulfillments
+    return artifact_expectation_bindings
 
 
 def _build_delivery(
@@ -585,7 +585,10 @@ def _build_delivery(
         deliverable_type=task.expected_output.output_type,
         manifest_uri=str(manifest_path.resolve()),
         artifacts=artifacts,
-        fulfillments=_build_fulfillments(candidate, artifacts),
+        artifact_expectation_bindings=_build_artifact_expectation_bindings(
+            candidate,
+            artifacts,
+        ),
         findings=candidate.findings,
         unresolved=candidate.unresolved,
         tool_calls=records,

+ 1 - 1
production build agents/src/production_build_agents/agents/executor/context.py → production_build_agents/agents/executor/context.py

@@ -139,7 +139,7 @@ def build_executor_user_payload(
                     "description": "中文说明",
                 }
             ],
-            "fulfillment_claims": [
+            "artifact_binding_claims": [
                 {
                     "expectation_id": "TaskPackage.expectation_ids 中的一项",
                     "artifact_uri": (

+ 5 - 4
production build agents/src/production_build_agents/agents/executor/prompt.md → production_build_agents/agents/executor/prompt.md

@@ -24,10 +24,11 @@
 2. 按当前 Skill 的方法执行,只在需要时调用允许的工具;
 3. 工具需要发现时遵循 search_tool → inspect_tool → run_tool;
 4. 对照 acceptance_criteria 自检;能够在本次 AgentRun 内修正时继续修正;
-5. 为每个真正满足的 Expectation 输出独立 fulfillment_claim;没有显式绑定的
-   Artifact 只是辅助产物,不能自动产生覆盖
+5. 为每个真正满足的 Expectation 输出独立 artifact_binding_claim;没有显式
+   绑定的 Artifact 只是辅助产物,不能自动满足任何 Expectation
 6. 无法完成或缺少证据的内容写入 unresolved,不编造结果;
-7. 输出前检查身份、Artifact、Fulfillment、来源、证据和 output_json_schema。
+7. 输出前检查身份、ArtifactExpectationBinding、来源、证据和
+   output_json_schema。
 
 # BOUNDARIES
 
@@ -48,7 +49,7 @@
 - 远程 URL 只能逐字使用工具实际返回的值;
 - 不得编造工具结果、来源或 Artifact;
 - 未解决内容写入 unresolved。
-- fulfillment_claims.expectation_id 只能来自 TaskPackage.expectation_ids;
+- artifact_binding_claims.expectation_id 只能来自 TaskPackage.expectation_ids;
 - artifact_uri 必须逐字对应候选 Artifact URI;结构化 payload 使用保留值 `payload`;
 - source_asset_id 仅用于 source-bound Expectation,生成媒体不得冒充已有来源;
 - payload、findings、unresolved 和 summary 等面向人的业务文本统一使用中文。

+ 0 - 0
production build agents/src/production_build_agents/agents/executor/provenance.py → production_build_agents/agents/executor/provenance.py


+ 0 - 0
production build agents/src/production_build_agents/agents/executor/skills/external-research/SKILL.md → production_build_agents/agents/executor/skills/external-research/SKILL.md


+ 0 - 0
production build agents/src/production_build_agents/agents/executor/skills/image-production/SKILL.md → production_build_agents/agents/executor/skills/image-production/SKILL.md


+ 0 - 0
production build agents/src/production_build_agents/agents/executor/skills/reference-inspection/SKILL.md → production_build_agents/agents/executor/skills/reference-inspection/SKILL.md


+ 0 - 0
production build agents/src/production_build_agents/agents/executor/skills/registry.py → production_build_agents/agents/executor/skills/registry.py


+ 0 - 0
production build agents/src/production_build_agents/agents/executor/skills/structured-analysis/SKILL.md → production_build_agents/agents/executor/skills/structured-analysis/SKILL.md


+ 0 - 0
production build agents/src/production_build_agents/agents/executor/skills/video-production/SKILL.md → production_build_agents/agents/executor/skills/video-production/SKILL.md


+ 0 - 24
production build agents/src/production_build_agents/agents/invocation.py → production_build_agents/agents/invocation.py

@@ -6,12 +6,6 @@ import json
 from typing import Any
 
 from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
-from langchain_core.tools import BaseTool, tool
-
-from ..contracts.models import ProductionBrief
-from ..preprocess.production_brief import BriefAccessError, read_brief_paths
-
-
 _CORRECTION_PREFIX = "上一版未通过"
 
 
@@ -41,24 +35,6 @@ def extract_final_json(
     raise error_type(f"{role} 最终消息中没有合法 JSON 对象")
 
 
-def create_brief_reader_tool(brief: ProductionBrief) -> BaseTool:
-    """创建只允许读取 ProductionBrief 已授权路径的 Agent Tool。"""
-
-    @tool("read_production_brief")
-    def read_production_brief(source_paths: list[str]) -> dict[str, Any]:
-        """按目录中的 JSONPath 读取 Production Brief 局部内容。"""
-
-        try:
-            return {
-                "success": True,
-                "items": read_brief_paths(brief, source_paths),
-            }
-        except BriefAccessError as exc:
-            return {"success": False, "error": str(exc)}
-
-    return read_production_brief
-
-
 def invoke_or_resume(agent, initial_message: dict[str, Any], config):
     """恢复未完成 turn,或复用已完成 turn;没有历史时才创建首个 turn。"""
 

+ 0 - 0
production build agents/src/production_build_agents/agents/planner/__init__.py → production_build_agents/agents/planner/__init__.py


+ 8 - 8
production build agents/src/production_build_agents/agents/planner/agent.py → production_build_agents/agents/planner/agent.py

@@ -18,11 +18,11 @@ from pydantic import ValidationError
 
 from ..invocation import (
     completed_attempt_count,
-    create_brief_reader_tool,
     extract_final_json,
     invoke_correction,
     invoke_or_resume,
 )
+from ...tools.brief_reader import create_brief_reader_tool
 from ...run.langgraph_checkpointer import (
     create_in_memory_checkpointer,
     create_sqlite_checkpointer,
@@ -39,7 +39,7 @@ from ..executor.skills.registry import (
 )
 from ...contracts.models import (
     AcceptanceCriterion,
-    CoverageGap,
+    ExpectationGap,
     ExpectedOutput,
     GlobalDataPlan,
     PlannedTask,
@@ -536,8 +536,8 @@ def run_replan_agent(
     passed_task_summaries: dict[str, Any],
     remaining_replans: int,
     rejected_artifact_ids: Iterable[str] = (),
-    requirement_coverages: list[dict[str, Any]] | None = None,
-    coverage_gaps: list[dict[str, Any]] | None = None,
+    requirement_evaluations: list[dict[str, Any]] | None = None,
+    expectation_gaps: list[dict[str, Any]] | None = None,
     model: BaseChatModel | None = None,
     max_tasks: int | None = None,
     run_dir: Path | None = None,
@@ -575,8 +575,8 @@ def run_replan_agent(
     ]
     rejected = list(rejected_artifact_ids)
     stage_gaps = [
-        CoverageGap.model_validate(item)
-        for item in (coverage_gaps or [])
+        ExpectationGap.model_validate(item)
+        for item in (expectation_gaps or [])
     ]
 
     def validate_replan_output(plan: GlobalDataPlan) -> None:
@@ -619,8 +619,8 @@ def run_replan_agent(
                 else None
             ),
             "validation_report": validation_report,
-            "requirement_coverages": requirement_coverages or [],
-            "coverage_gaps": coverage_gaps or [],
+            "requirement_evaluations": requirement_evaluations or [],
+            "expectation_gaps": expectation_gaps or [],
             "rejected_artifact_ids": list(rejected_artifact_ids),
             "passed_task_ids": sorted(
                 passed,

+ 0 - 0
production build agents/src/production_build_agents/agents/planner/prompt.md → production_build_agents/agents/planner/prompt.md


+ 0 - 0
production build agents/src/production_build_agents/agents/planner/skills/__init__.py → production_build_agents/agents/planner/skills/__init__.py


+ 0 - 0
production build agents/src/production_build_agents/agents/planner/skills/global-data-planning/SKILL.md → production_build_agents/agents/planner/skills/global-data-planning/SKILL.md


+ 0 - 0
production build agents/src/production_build_agents/agents/planner/skills/registry.py → production_build_agents/agents/planner/skills/registry.py


+ 0 - 0
production build agents/src/production_build_agents/agents/validator/__init__.py → production_build_agents/agents/validator/__init__.py


+ 5 - 3
production build agents/src/production_build_agents/agents/validator/prompt.md → production_build_agents/agents/validator/prompt.md

@@ -24,13 +24,15 @@
 1. 从运行输入和当前 Skill 确认验证范围、验证目标、检查方法和完成条件;
 2. 只读取当前范围内已授权的数据、交付和证据;
 3. 媒体检查必须以实际加载的图片、视频帧或当前 Skill 明确支持的真实媒体证据为依据;
-4. 对每个验证目标独立检查,不遗漏、不合并、不跨目标借用未声明的覆盖关系;
+4. 对每个验证目标独立检查,不遗漏、不合并、不跨目标借用未声明的
+   ArtifactExpectationBinding;
 5. 缺少必要证据时按失败处理,不凭常识、Task 成员关系或 Artifact 类型补齐;
-6. 输出前检查身份、目标覆盖、证据引用和 output_json_schema。
+6. 输出前检查身份、验证目标、ArtifactExpectationBinding、证据引用和
+   output_json_schema。
 
 # BOUNDARIES
 
-- 不修改或补写产物、Fulfillment 和证据;
+- 不修改或补写产物、ArtifactExpectationBinding 和证据;
 - 不规划后续 Task,不修改 Plan 或 DAG;
 - 不使用未授权 Skill、工具、来源或路径;
 - 不输出隐藏思维链。

+ 4 - 4
production build agents/src/production_build_agents/agents/validator/skills/global-data-stage-validation/SKILL.md → production_build_agents/agents/validator/skills/global-data-stage-validation/SKILL.md

@@ -6,8 +6,8 @@
 
 0. 先调用 read_production_brief,一次或多次读取全部
    required_audit_source_paths;没有真实读取就不得输出报告;
-1. 检查代码给出的 Expectation 级 RequirementCoverage 和 CoverageGap;存在 critical
-   CoverageGap 的 Requirement 不得判为 PASS;
+1. 检查代码给出的 Expectation 级 RequirementEvaluation 和 ExpectationGap;存在 critical
+   ExpectationGap 的 Requirement 不得判为 PASS;
 2. 对照 Production Brief,Planner 是否遗漏了进入正式生产前不可缺少的关键共享资料或素材;
 3. Active Artifact 是否能直接作为后续生产 Task 的输入,而不是 URL 清单、文字描述或无法访问的占位符;
 4. 不同 Task 产生的角色、场景、镜头、声音或风格约束是否互相冲突;
@@ -23,7 +23,7 @@
 - 内容不合格、无法用于生产并且后续必须替换的 Artifact ID 放入
   rejected_artifact_ids;“不合格”与“多个产物互相冲突”不能混用;
 - 技术 probe 不能证明音色、BGM 风格或主观一致性;
-- SourceAsset 是否满足只由显式 ArtifactFulfillment 决定;不要按 Task、类型或 URI
-  自行推断新的覆盖
+- SourceAsset 是否满足只由显式 ArtifactExpectationBinding 决定;不要按 Task、类型或
+  URI 自行推断新的绑定
 - URL 存在、字段已整理或单 Task PASS,都不等于整个阶段完成;
 - 不输出总体 verdict;代码会根据 critical Requirement、critical missing requirement和 Artifact 冲突确定总体结论。

+ 0 - 0
production build agents/src/production_build_agents/agents/validator/skills/image-validation/SKILL.md → production_build_agents/agents/validator/skills/image-validation/SKILL.md


+ 0 - 0
production build agents/src/production_build_agents/agents/validator/skills/reference-validation/SKILL.md → production_build_agents/agents/validator/skills/reference-validation/SKILL.md


+ 0 - 0
production build agents/src/production_build_agents/agents/validator/skills/registry.py → production_build_agents/agents/validator/skills/registry.py


+ 0 - 0
production build agents/src/production_build_agents/agents/validator/skills/research-validation/SKILL.md → production_build_agents/agents/validator/skills/research-validation/SKILL.md


+ 0 - 0
production build agents/src/production_build_agents/agents/validator/skills/structured-validation/SKILL.md → production_build_agents/agents/validator/skills/structured-validation/SKILL.md


+ 0 - 0
production build agents/src/production_build_agents/agents/validator/skills/video-validation/SKILL.md → production_build_agents/agents/validator/skills/video-validation/SKILL.md


+ 10 - 10
production build agents/src/production_build_agents/agents/validator/stage_agent.py → production_build_agents/agents/validator/stage_agent.py

@@ -19,23 +19,23 @@ from ...contracts.models import (
 )
 from ..invocation import (
     completed_attempt_count,
-    create_brief_reader_tool,
     extract_final_json,
     invoke_correction,
     invoke_or_resume,
 )
+from ...tools.brief_reader import create_brief_reader_tool
 from ...run.langgraph_checkpointer import create_sqlite_checkpointer
 from ...preprocess.production_brief import (
     build_allowed_source_paths,
     load_production_brief,
 )
 from ...tools.registry import create_default_tool_registry
-from .agent import (
+from .task_agent import (
     ValidatorOutputError,
     create_real_validator_model,
     create_validator_agent,
 )
-from .context import build_validator_system_prompt
+from .task_context import build_validator_system_prompt
 from .skills.registry import load_global_data_stage_validator_skill
 from .stage_context import (
     build_stage_validator_user_message,
@@ -92,26 +92,26 @@ def _validate_candidate(
     if set(output.rejected_artifact_ids).difference(artifact_ids):
         mismatches.append("rejected_artifact_ids")
 
-    coverage = {
+    artifact_ids_by_requirement = {
         item.requirement_id: set(item.actual_artifact_ids)
-        for item in candidate.requirement_coverages
+        for item in candidate.requirement_evaluations
     }
     if any(
         not set(result.evidence_artifact_ids).issubset(
-            coverage.get(result.requirement_id, set())
+            artifact_ids_by_requirement.get(result.requirement_id, set())
         )
         for result in output.requirement_results
     ):
         mismatches.append("requirement_results.cross_requirement_evidence")
     gap_requirement_ids = {
-        gap.requirement_id for gap in candidate.coverage_gaps
+        gap.requirement_id for gap in candidate.expectation_gaps
     }
     if any(
         result.verdict == "PASS"
         and result.requirement_id in gap_requirement_ids
         for result in output.requirement_results
     ):
-        mismatches.append("requirement_results.coverage_gaps")
+        mismatches.append("requirement_results.expectation_gaps")
 
     _, brief = load_production_brief(candidate.production_brief_uri)
     allowed_paths = set(build_allowed_source_paths(brief))
@@ -143,8 +143,8 @@ def _critical_failures(
         and result.verdict == "FAIL"
     }
     failures.update(
-        f"coverage_gap:{gap.expectation_id}"
-        for gap in candidate.coverage_gaps
+        f"expectation_gap:{gap.expectation_id}"
+        for gap in candidate.expectation_gaps
         if importance[gap.requirement_id] == "critical"
     )
     failures.update(

+ 13 - 13
production build agents/src/production_build_agents/agents/validator/stage_context.py → production_build_agents/agents/validator/stage_context.py

@@ -20,7 +20,7 @@ from ...contracts.models import (
     ValidationReport,
 )
 from ...tools.media import image_as_data_url
-from .context import (
+from .task_context import (
     ValidatorContextError,
     ValidatorEvidenceError,
     invoke_evidence_tool,
@@ -51,13 +51,13 @@ def validate_stage_candidate_identity(
     requirement_ids = [
         requirement.requirement_id for requirement in plan.stage_requirements
     ]
-    coverage_ids = [
-        coverage.requirement_id
-        for coverage in candidate.requirement_coverages
+    evaluation_ids = [
+        requirement_evaluation.requirement_id
+        for requirement_evaluation in candidate.requirement_evaluations
     ]
-    if coverage_ids != requirement_ids:
+    if evaluation_ids != requirement_ids:
         raise ValidatorContextError(
-            "Stage Candidate 没有逐项覆盖当前 Plan 的 Requirement"
+            "Stage Candidate 没有逐项评价当前 Plan 的 Requirement"
         )
 
 
@@ -203,9 +203,9 @@ def build_stage_validator_user_message(
                     artifact.model_dump(mode="json")
                     for artifact in delivery.artifacts
                 ],
-                "fulfillments": [
-                    fulfillment.model_dump(mode="json")
-                    for fulfillment in delivery.fulfillments
+                "artifact_expectation_bindings": [
+                    binding.model_dump(mode="json")
+                    for binding in delivery.artifact_expectation_bindings
                 ],
                 "task_validation_report": report.model_dump(mode="json"),
             }
@@ -232,12 +232,12 @@ def build_stage_validator_user_message(
         "stage_requirements": [
             item.model_dump(mode="json") for item in plan.stage_requirements
         ],
-        "requirement_coverages": [
+        "requirement_evaluations": [
             item.model_dump(mode="json")
-            for item in candidate.requirement_coverages
+            for item in candidate.requirement_evaluations
         ],
-        "coverage_gaps": [
-            item.model_dump(mode="json") for item in candidate.coverage_gaps
+        "expectation_gaps": [
+            item.model_dump(mode="json") for item in candidate.expectation_gaps
         ],
         "active_artifacts": [
             item.model_dump(mode="json") for item in candidate.artifacts

+ 2 - 2
production build agents/src/production_build_agents/agents/validator/agent.py → production_build_agents/agents/validator/task_agent.py

@@ -1,4 +1,4 @@
-"""独立、通用且按逐项证据验收的 Validator Agent。"""
+"""单个 Task Delivery 的独立 Validator Agent。"""
 
 from __future__ import annotations
 
@@ -37,7 +37,7 @@ from ...tools.registry import (
     ToolRegistry,
     create_default_tool_registry,
 )
-from .context import (
+from .task_context import (
     build_validator_system_prompt,
     build_validator_user_message,
     create_validator_run_id,

+ 1 - 1
production build agents/src/production_build_agents/agents/validator/context.py → production_build_agents/agents/validator/task_context.py

@@ -1,4 +1,4 @@
-"""为一次独立 ValidatorRun 组装最小、可核对的上下文。"""
+"""为一次 Task ValidatorRun 组装最小、可核对的上下文。"""
 
 from __future__ import annotations
 

+ 0 - 0
production build agents/src/production_build_agents/app.py → production_build_agents/app.py


+ 0 - 0
production build agents/src/production_build_agents/contracts/__init__.py → production_build_agents/contracts/__init__.py


+ 82 - 82
production build agents/src/production_build_agents/contracts/evaluation.py → production_build_agents/contracts/evaluation.py

@@ -8,12 +8,12 @@ from typing import Iterable, Mapping, Sequence
 from ..agents.executor.skills.registry import SkillCapability
 from .models import (
     ContractIssue,
-    CoverageGap,
+    ExpectationGap,
     ExecutorDelivery,
-    ExpectationCoverage,
+    ExpectationEvaluation,
     GlobalDataPlan,
     ProductionBrief,
-    RequirementCoverage,
+    RequirementEvaluation,
     StageEvaluation,
     TaskPackage,
     ValidationReport,
@@ -204,7 +204,7 @@ def evaluate_task_delivery(
     delivery: ExecutorDelivery,
     capability_catalog: Mapping[str, SkillCapability],
 ) -> list[ContractIssue]:
-    """在模型 Validator 前验证显式 Fulfillment 的完整性。"""
+    """在模型 Validator 前验证显式 Binding 的完整性。"""
 
     issues: list[ContractIssue] = []
     for field in ("run_id", "plan_id", "task_id", "plan_version"):
@@ -283,46 +283,46 @@ def evaluate_task_delivery(
         asset.source_asset_id: asset for asset in task.source_assets
     }
     tool_ids = {item.tool_call_id for item in delivery.tool_calls}
-    fulfillment_keys: set[tuple[str, str, str | None]] = set()
+    binding_keys: set[tuple[str, str, str | None]] = set()
     by_expectation: dict[str, list] = defaultdict(list)
-    for fulfillment in delivery.fulfillments:
+    for binding in delivery.artifact_expectation_bindings:
         key = (
-            fulfillment.expectation_id,
-            fulfillment.artifact_id,
-            fulfillment.source_asset_id,
+            binding.expectation_id,
+            binding.artifact_id,
+            binding.source_asset_id,
         )
-        if key in fulfillment_keys:
+        if key in binding_keys:
             issues.append(
                 _issue(
-                    "duplicate_fulfillment",
-                    "同一 ArtifactFulfillment 重复",
+                    "duplicate_artifact_binding",
+                    "同一 ArtifactExpectationBinding 重复",
                     task_id=task.task_id,
-                    expectation_id=fulfillment.expectation_id,
-                    artifact_id=fulfillment.artifact_id,
+                    expectation_id=binding.expectation_id,
+                    artifact_id=binding.artifact_id,
                 )
             )
             continue
-        fulfillment_keys.add(key)
-        expectation = expectations.get(fulfillment.expectation_id)
-        artifact = artifacts.get(fulfillment.artifact_id)
+        binding_keys.add(key)
+        expectation = expectations.get(binding.expectation_id)
+        artifact = artifacts.get(binding.artifact_id)
         if expectation is None:
             issues.append(
                 _issue(
-                    "unknown_fulfillment_expectation",
-                    "Fulfillment 引用了 Task 未授权的 Expectation",
+                    "unknown_binding_expectation",
+                    "Binding 引用了 Task 未授权的 Expectation",
                     task_id=task.task_id,
-                    expectation_id=fulfillment.expectation_id,
+                    expectation_id=binding.expectation_id,
                 )
             )
             continue
         if artifact is None:
             issues.append(
                 _issue(
-                    "unknown_fulfillment_artifact",
-                    "Fulfillment 引用了不存在的 Artifact",
+                    "unknown_binding_artifact",
+                    "Binding 引用了不存在的 Artifact",
                     task_id=task.task_id,
-                    expectation_id=fulfillment.expectation_id,
-                    artifact_id=fulfillment.artifact_id,
+                    expectation_id=binding.expectation_id,
+                    artifact_id=binding.artifact_id,
                 )
             )
             continue
@@ -334,43 +334,43 @@ def evaluate_task_delivery(
                     "unsupported_delivery_artifact",
                     "Skill 不支持 Delivery 中的 Artifact 类型",
                     task_id=task.task_id,
-                    expectation_id=fulfillment.expectation_id,
-                    artifact_id=fulfillment.artifact_id,
+                    expectation_id=binding.expectation_id,
+                    artifact_id=binding.artifact_id,
                 )
             )
         if artifact.artifact_type != expectation.artifact_type:
             issues.append(
                 _issue(
-                    "fulfillment_artifact_type_mismatch",
+                    "binding_artifact_type_mismatch",
                     "Artifact 类型与 Expectation 不一致",
                     task_id=task.task_id,
-                    expectation_id=fulfillment.expectation_id,
-                    artifact_id=fulfillment.artifact_id,
+                    expectation_id=binding.expectation_id,
+                    artifact_id=binding.artifact_id,
                 )
             )
         missing_tools = set(
-            fulfillment.evidence_tool_call_ids
+            binding.evidence_tool_call_ids
         ).difference(tool_ids)
         if missing_tools:
             issues.append(
                 _issue(
-                    "unknown_fulfillment_evidence",
-                    "Fulfillment 引用了不存在的 ToolCallRecord",
+                    "unknown_binding_evidence",
+                    "Binding 引用了不存在的 ToolCallRecord",
                     task_id=task.task_id,
-                    expectation_id=fulfillment.expectation_id,
-                    artifact_id=fulfillment.artifact_id,
+                    expectation_id=binding.expectation_id,
+                    artifact_id=binding.artifact_id,
                 )
             )
         if expectation.source_asset_ids:
-            if fulfillment.source_asset_id not in expectation.source_asset_ids:
+            if binding.source_asset_id not in expectation.source_asset_ids:
                 issues.append(
                     _issue(
                         "invalid_source_asset_binding",
-                        "Fulfillment 未绑定该 Expectation 授权的 SourceAsset",
+                        "Binding 未绑定该 Expectation 授权的 SourceAsset",
                         task_id=task.task_id,
-                        expectation_id=fulfillment.expectation_id,
-                        artifact_id=fulfillment.artifact_id,
-                        source_asset_id=fulfillment.source_asset_id,
+                        expectation_id=binding.expectation_id,
+                        artifact_id=binding.artifact_id,
+                        source_asset_id=binding.source_asset_id,
                     )
                 )
             if delivery.skill_id != "reference-inspection":
@@ -379,12 +379,12 @@ def evaluate_task_delivery(
                         "generated_artifact_cannot_adopt_source",
                         "只有 reference-inspection 可满足 source_identity",
                         task_id=task.task_id,
-                        expectation_id=fulfillment.expectation_id,
-                        artifact_id=fulfillment.artifact_id,
+                        expectation_id=binding.expectation_id,
+                        artifact_id=binding.artifact_id,
                     )
                 )
             source_asset = source_assets.get(
-                fulfillment.source_asset_id or ""
+                binding.source_asset_id or ""
             )
             if (
                 source_asset is not None
@@ -398,33 +398,33 @@ def evaluate_task_delivery(
                         "source_artifact_identity_mismatch",
                         "Artifact 的类型或 source_uri 与 SourceAsset 不一致",
                         task_id=task.task_id,
-                        expectation_id=fulfillment.expectation_id,
-                        artifact_id=fulfillment.artifact_id,
-                        source_asset_id=fulfillment.source_asset_id,
+                        expectation_id=binding.expectation_id,
+                        artifact_id=binding.artifact_id,
+                        source_asset_id=binding.source_asset_id,
                     )
                 )
-        elif fulfillment.source_asset_id is not None:
+        elif binding.source_asset_id is not None:
             issues.append(
                 _issue(
                     "unexpected_source_asset_binding",
                     "非 source-bound Expectation 不得绑定 SourceAsset",
                     task_id=task.task_id,
-                    expectation_id=fulfillment.expectation_id,
-                    artifact_id=fulfillment.artifact_id,
+                    expectation_id=binding.expectation_id,
+                    artifact_id=binding.artifact_id,
                 )
             )
-        by_expectation[fulfillment.expectation_id].append(fulfillment)
+        by_expectation[binding.expectation_id].append(binding)
 
     for expectation_id, expectation in expectations.items():
-        fulfillments = by_expectation[expectation_id]
+        artifact_expectation_bindings = by_expectation[expectation_id]
         artifact_count = len(
-            {item.artifact_id for item in fulfillments}
+            {item.artifact_id for item in artifact_expectation_bindings}
         )
         if artifact_count < expectation.minimum_count:
             issues.append(
                 _issue(
-                    "insufficient_fulfillments",
-                    "显式 ArtifactFulfillment 数量不足",
+                    "insufficient_artifact_bindings",
+                    "显式 ArtifactExpectationBinding 数量不足",
                     task_id=task.task_id,
                     expectation_id=expectation_id,
                 )
@@ -432,7 +432,7 @@ def evaluate_task_delivery(
         if expectation.source_asset_ids:
             delivered_sources = {
                 item.source_asset_id
-                for item in fulfillments
+                for item in artifact_expectation_bindings
                 if item.source_asset_id is not None
             }
             for source_asset_id in set(
@@ -440,8 +440,8 @@ def evaluate_task_delivery(
             ).difference(delivered_sources):
                 issues.append(
                     _issue(
-                        "missing_source_asset_fulfillment",
-                        "SourceAsset 没有独立的 ArtifactFulfillment",
+                        "missing_source_asset_binding",
+                        "SourceAsset 没有独立的 ArtifactExpectationBinding",
                         task_id=task.task_id,
                         expectation_id=expectation_id,
                         source_asset_id=source_asset_id,
@@ -455,7 +455,7 @@ def evaluate_stage(
     passed_deliveries: Sequence[ExecutorDelivery],
     task_reports: Sequence[ValidationReport],
 ) -> StageEvaluation:
-    """只依据 PASS Delivery 中的显式 Fulfillment 计算阶段覆盖。"""
+    """只依据 PASS Delivery 中的正式绑定计算阶段满足情况。"""
 
     reports = {report.task_id: report for report in task_reports}
     deliveries = {
@@ -485,33 +485,33 @@ def evaluate_stage(
         if artifact.artifact_id in replacements
     ]
     active_ids = {artifact.artifact_id for artifact in active}
-    fulfillment_rows = [
-        (task_id, fulfillment)
+    binding_rows = [
+        (task_id, binding)
         for task_id, delivery in deliveries.items()
-        for fulfillment in delivery.fulfillments
-        if fulfillment.artifact_id in active_ids
+        for binding in delivery.artifact_expectation_bindings
+        if binding.artifact_id in active_ids
     ]
 
-    coverages: list[RequirementCoverage] = []
-    gaps: list[CoverageGap] = []
+    requirement_evaluations: list[RequirementEvaluation] = []
+    gaps: list[ExpectationGap] = []
     for requirement in plan.stage_requirements:
-        expectation_coverages: list[ExpectationCoverage] = []
+        expectation_evaluations: list[ExpectationEvaluation] = []
         requirement_artifact_ids: set[str] = set()
         requirement_task_ids: set[str] = set()
         for expectation in requirement.artifact_expectations:
             rows = [
-                (task_id, fulfillment)
-                for task_id, fulfillment in fulfillment_rows
-                if fulfillment.expectation_id == expectation.expectation_id
+                (task_id, binding)
+                for task_id, binding in binding_rows
+                if binding.expectation_id == expectation.expectation_id
             ]
             artifact_ids = sorted(
-                {fulfillment.artifact_id for _, fulfillment in rows}
+                {binding.artifact_id for _, binding in rows}
             )
             source_asset_ids = sorted(
                 {
-                    fulfillment.source_asset_id
-                    for _, fulfillment in rows
-                    if fulfillment.source_asset_id is not None
+                    binding.source_asset_id
+                    for _, binding in rows
+                    if binding.source_asset_id is not None
                 }
             )
             missing_sources = sorted(
@@ -523,8 +523,8 @@ def evaluate_stage(
                 len(artifact_ids) >= expectation.minimum_count
                 and not missing_sources
             )
-            expectation_coverages.append(
-                ExpectationCoverage(
+            expectation_evaluations.append(
+                ExpectationEvaluation(
                     expectation_id=expectation.expectation_id,
                     expected_count=expectation.minimum_count,
                     actual_count=len(artifact_ids),
@@ -537,7 +537,7 @@ def evaluate_stage(
             requirement_task_ids.update(task_id for task_id, _ in rows)
             if not satisfied:
                 gaps.append(
-                    CoverageGap(
+                    ExpectationGap(
                         requirement_id=requirement.requirement_id,
                         expectation_id=expectation.expectation_id,
                         expected_count=expectation.minimum_count,
@@ -546,21 +546,21 @@ def evaluate_stage(
                         reason_code=(
                             "missing_source_assets"
                             if missing_sources
-                            else "insufficient_fulfillments"
+                            else "insufficient_artifact_bindings"
                         ),
                     )
                 )
-        coverages.append(
-            RequirementCoverage(
+        requirement_evaluations.append(
+            RequirementEvaluation(
                 requirement_id=requirement.requirement_id,
-                covering_task_ids=sorted(requirement_task_ids),
-                expectation_coverages=expectation_coverages,
+                supporting_task_ids=sorted(requirement_task_ids),
+                expectation_evaluations=expectation_evaluations,
                 actual_artifact_ids=sorted(requirement_artifact_ids),
             )
         )
     return StageEvaluation(
-        requirement_coverages=coverages,
-        coverage_gaps=gaps,
+        requirement_evaluations=requirement_evaluations,
+        expectation_gaps=gaps,
         active_artifacts=active,
         superseded_artifacts=superseded,
     )
@@ -571,7 +571,7 @@ def validate_replan(
     next_plan: GlobalDataPlan,
     passed_task_ids: Iterable[str],
     rejected_artifact_ids: Iterable[str],
-    stage_gaps: Sequence[CoverageGap],
+    stage_gaps: Sequence[ExpectationGap],
 ) -> list[ContractIssue]:
     """验证 Replan 版本和不可变历史,不依赖 Validator 文案。"""
 

+ 24 - 18
production build agents/src/production_build_agents/contracts/models.py → production_build_agents/contracts/models.py

@@ -416,7 +416,7 @@ class CandidateArtifact(BaseModel):
     description: str = Field(min_length=1)
 
 
-class FulfillmentClaim(BaseModel):
+class ArtifactBindingClaim(BaseModel):
     """Executor 对候选 Artifact 与 Expectation 的显式绑定声明。"""
 
     model_config = ConfigDict(extra="forbid", strict=True)
@@ -454,7 +454,9 @@ class ExecutorCandidate(BaseModel):
     deliverable_type: DeliverableType
     payload: Dict[str, Any] = Field(default_factory=dict)
     artifacts: List[CandidateArtifact] = Field(default_factory=list)
-    fulfillment_claims: List[FulfillmentClaim] = Field(default_factory=list)
+    artifact_binding_claims: List[ArtifactBindingClaim] = Field(
+        default_factory=list
+    )
     findings: List[Finding] = Field(default_factory=list)
     unresolved: List[str] = Field(default_factory=list)
     summary: str = Field(min_length=1)
@@ -519,8 +521,8 @@ class ToolCallRecord(BaseModel):
     output_excerpt: Optional[str] = None
 
 
-class ArtifactFulfillment(BaseModel):
-    """落盘 Artifact 对一个 Expectation 的正式覆盖事实。"""
+class ArtifactExpectationBinding(BaseModel):
+    """一个已落盘 Artifact 与一个 Expectation 的正式身份绑定。"""
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
@@ -561,7 +563,9 @@ class ExecutorDelivery(BaseModel):
     deliverable_type: DeliverableType
     manifest_uri: str
     artifacts: List[Artifact]
-    fulfillments: List[ArtifactFulfillment] = Field(default_factory=list)
+    artifact_expectation_bindings: List[ArtifactExpectationBinding] = Field(
+        default_factory=list
+    )
     findings: List[Finding] = Field(default_factory=list)
     unresolved: List[str] = Field(default_factory=list)
     tool_calls: List[ToolCallRecord] = Field(default_factory=list)
@@ -654,18 +658,20 @@ class GlobalDataDeliveryTask(BaseModel):
     summary: str
 
 
-class RequirementCoverage(BaseModel):
-    """一个 Requirement 由显式 Fulfillment 推导出的覆盖事实。"""
+class RequirementEvaluation(BaseModel):
+    """由正式 Artifact 绑定计算出的 Requirement 满足情况。"""
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
     requirement_id: str = Field(pattern=r"^Requirement[1-9][0-9]*$")
-    covering_task_ids: List[str] = Field(default_factory=list)
-    expectation_coverages: List["ExpectationCoverage"] = Field(min_length=1)
+    supporting_task_ids: List[str] = Field(default_factory=list)
+    expectation_evaluations: List["ExpectationEvaluation"] = Field(
+        min_length=1
+    )
     actual_artifact_ids: List[str] = Field(default_factory=list)
 
 
-class ExpectationCoverage(BaseModel):
+class ExpectationEvaluation(BaseModel):
     model_config = ConfigDict(extra="forbid", strict=True)
 
     expectation_id: str = Field(
@@ -678,8 +684,8 @@ class ExpectationCoverage(BaseModel):
     satisfied: bool
 
 
-class CoverageGap(BaseModel):
-    """纯策略层输出的结构化覆盖缺口。"""
+class ExpectationGap(BaseModel):
+    """一个 ArtifactExpectation 尚未满足的结构化缺口。"""
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
@@ -691,7 +697,7 @@ class CoverageGap(BaseModel):
     actual_count: int = Field(ge=0)
     missing_source_asset_ids: List[str] = Field(default_factory=list)
     reason_code: Literal[
-        "insufficient_fulfillments",
+        "insufficient_artifact_bindings",
         "missing_source_assets",
     ]
 
@@ -713,8 +719,8 @@ class ContractIssue(BaseModel):
 class StageEvaluation(BaseModel):
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    requirement_coverages: List[RequirementCoverage]
-    coverage_gaps: List[CoverageGap]
+    requirement_evaluations: List[RequirementEvaluation]
+    expectation_gaps: List[ExpectationGap]
     active_artifacts: List[Artifact]
     superseded_artifacts: List[Artifact]
 
@@ -731,10 +737,10 @@ class GlobalDataStageCandidate(BaseModel):
     production_brief_uri: str
     source_assets: List[SourceAsset] = Field(default_factory=list)
     requirements: List[GlobalDataRequirement] = Field(min_length=1)
-    requirement_coverages: List[RequirementCoverage] = Field(min_length=1)
+    requirement_evaluations: List[RequirementEvaluation] = Field(min_length=1)
     tasks: List[GlobalDataDeliveryTask] = Field(min_length=1)
     artifacts: List[Artifact] = Field(default_factory=list)
-    coverage_gaps: List[CoverageGap] = Field(default_factory=list)
+    expectation_gaps: List[ExpectationGap] = Field(default_factory=list)
     unresolved: List[str] = Field(default_factory=list)
 
 
@@ -813,7 +819,7 @@ class GlobalDataStageDelivery(BaseModel):
     production_brief_uri: str
     plan_history: List[PlanArtifactRef] = Field(min_length=1)
     stage_requirements: List[GlobalDataRequirement] = Field(min_length=1)
-    requirement_coverages: List[RequirementCoverage] = Field(min_length=1)
+    requirement_evaluations: List[RequirementEvaluation] = Field(min_length=1)
     stage_validation_report_uri: str
     tasks: List[GlobalDataDeliveryTask] = Field(min_length=1)
     active_artifacts: List[Artifact] = Field(min_length=1)

+ 13 - 13
production build agents/src/production_build_agents/global_data_stage.py → production_build_agents/global_data_stage.py

@@ -21,7 +21,7 @@ from .contracts.models import (
 from .preprocess.production_brief import load_production_brief
 from .contracts.evaluation import evaluate_stage, evaluate_task_delivery
 from .agents.executor.skills.registry import CAPABILITIES
-from .agents.validator.context import validate_validation_report
+from .agents.validator.task_context import validate_validation_report
 
 
 class GlobalDataStageError(ValueError):
@@ -155,10 +155,10 @@ def build_global_data_stage_candidate(
         production_brief_uri=production_brief_uri,
         source_assets=brief.source_assets,
         requirements=plan.stage_requirements,
-        requirement_coverages=evaluation.requirement_coverages,
+        requirement_evaluations=evaluation.requirement_evaluations,
         tasks=tasks,
         artifacts=evaluation.active_artifacts,
-        coverage_gaps=evaluation.coverage_gaps,
+        expectation_gaps=evaluation.expectation_gaps,
         unresolved=unresolved,
     )
 
@@ -242,23 +242,23 @@ def build_global_data_stage_delivery(
         for requirement in plan.stage_requirements
         if requirement.importance == "critical"
     }
-    critical_coverage_gaps = [
+    critical_expectation_gaps = [
         gap
-        for gap in evaluation.coverage_gaps
+        for gap in evaluation.expectation_gaps
         if gap.requirement_id in critical_requirement_ids
     ]
-    if critical_coverage_gaps:
+    if critical_expectation_gaps:
         raise GlobalDataStageError(
-            "critical RequirementCoverage 尚不完整:"
+            "critical RequirementEvaluation 尚不完整:"
             + ";".join(
                 f"{gap.expectation_id}/{gap.reason_code}"
-                for gap in critical_coverage_gaps
+                for gap in critical_expectation_gaps
             )
         )
-    if evaluation.requirement_coverages != candidate.requirement_coverages:
-        raise GlobalDataStageError("Stage Candidate 的 Coverage 已陈旧")
-    if evaluation.coverage_gaps != candidate.coverage_gaps:
-        raise GlobalDataStageError("Stage Candidate 的 CoverageGap 已陈旧")
+    if evaluation.requirement_evaluations != candidate.requirement_evaluations:
+        raise GlobalDataStageError("Stage Candidate 的 Evaluation 已陈旧")
+    if evaluation.expectation_gaps != candidate.expectation_gaps:
+        raise GlobalDataStageError("Stage Candidate 的 ExpectationGap 已陈旧")
     if evaluation.active_artifacts != candidate.artifacts:
         raise GlobalDataStageError("Stage Candidate 的 Active Artifact 已陈旧")
     expected_unresolved = sorted(
@@ -331,7 +331,7 @@ def build_global_data_stage_delivery(
         production_brief_uri=candidate.production_brief_uri,
         plan_history=_plan_history(plan, plan_history),
         stage_requirements=plan.stage_requirements,
-        requirement_coverages=evaluation.requirement_coverages,
+        requirement_evaluations=evaluation.requirement_evaluations,
         stage_validation_report_uri=str(
             (
                 run_dir

+ 0 - 0
production build agents/src/production_build_agents/graph.py → production_build_agents/graph.py


+ 0 - 0
production build agents/src/production_build_agents/graph_nodes/__init__.py → production_build_agents/graph_nodes/__init__.py


+ 0 - 0
production build agents/src/production_build_agents/graph_nodes/common.py → production_build_agents/graph_nodes/common.py


+ 0 - 0
production build agents/src/production_build_agents/graph_nodes/create_plan.py → production_build_agents/graph_nodes/create_plan.py


+ 8 - 8
production build agents/src/production_build_agents/graph_nodes/create_replan.py → production_build_agents/graph_nodes/create_replan.py

@@ -56,8 +56,8 @@ def replan_global_data_node(
             )
             report_payload = validation_report.model_dump(mode="json")
             rejected_artifact_ids = []
-            requirement_coverages: list[dict[str, Any]] = []
-            coverage_gaps: list[dict[str, Any]] = []
+            requirement_evaluations: list[dict[str, Any]] = []
+            expectation_gaps: list[dict[str, Any]] = []
         elif state["status"] == "STAGE_REPLAN_REQUIRED":
             failure_scope = "global_data_stage"
             failed_task = None
@@ -78,13 +78,13 @@ def replan_global_data_node(
                     encoding="utf-8"
                 )
             )
-            requirement_coverages = [
+            requirement_evaluations = [
                 item.model_dump(mode="json")
-                for item in stage_candidate.requirement_coverages
+                for item in stage_candidate.requirement_evaluations
             ]
-            coverage_gaps = [
+            expectation_gaps = [
                 item.model_dump(mode="json")
-                for item in stage_candidate.coverage_gaps
+                for item in stage_candidate.expectation_gaps
             ]
         else:
             raise ValueError("当前状态不允许 Replan")
@@ -105,8 +105,8 @@ def replan_global_data_node(
             passed_task_summaries=passed_summaries,
             remaining_replans=MAX_GLOBAL_DATA_REPLANS - replan_count - 1,
             rejected_artifact_ids=rejected_artifact_ids,
-            requirement_coverages=requirement_coverages,
-            coverage_gaps=coverage_gaps,
+            requirement_evaluations=requirement_evaluations,
+            expectation_gaps=expectation_gaps,
             model=planner_model,
             run_dir=Path(state["output_dir"]),
         )

+ 0 - 0
production build agents/src/production_build_agents/graph_nodes/execute_task.py → production_build_agents/graph_nodes/execute_task.py


+ 1 - 1
production build agents/src/production_build_agents/graph_nodes/finish_run.py → production_build_agents/graph_nodes/finish_run.py

@@ -5,7 +5,7 @@ from __future__ import annotations
 from pathlib import Path
 from typing import Any
 
-from ..agents.validator.agent import ValidatorOutputError
+from ..agents.validator.task_agent import ValidatorOutputError
 from ..agents.validator.stage_agent import validate_global_data_stage_report
 from ..contracts.models import (
     GlobalDataPlan,

+ 0 - 0
production build agents/src/production_build_agents/graph_nodes/prepare_brief.py → production_build_agents/graph_nodes/prepare_brief.py


+ 0 - 0
production build agents/src/production_build_agents/graph_nodes/prepare_task.py → production_build_agents/graph_nodes/prepare_task.py


+ 2 - 2
production build agents/src/production_build_agents/graph_nodes/validate_stage.py → production_build_agents/graph_nodes/validate_stage.py

@@ -8,8 +8,8 @@ from typing import Any, Literal
 from langchain_core.language_models import BaseChatModel
 from langgraph.graph import END
 
-from ..agents.validator.agent import ValidatorOutputError
-from ..agents.validator.context import ValidatorContextError
+from ..agents.validator.task_agent import ValidatorOutputError
+from ..agents.validator.task_context import ValidatorContextError
 from ..agents.validator.stage_agent import (
     run_global_data_stage_validator,
     validate_global_data_stage_report,

+ 2 - 2
production build agents/src/production_build_agents/graph_nodes/validate_task.py → production_build_agents/graph_nodes/validate_task.py

@@ -9,12 +9,12 @@ from langchain_core.language_models import BaseChatModel
 from langgraph.graph import END
 
 from ..agents.executor.skills.registry import CAPABILITIES
-from ..agents.validator.agent import (
+from ..agents.validator.task_agent import (
     ValidatorOutputError,
     run_validator_agent,
     validate_validation_report,
 )
-from ..agents.validator.context import ValidatorContextError
+from ..agents.validator.task_context import ValidatorContextError
 from ..contracts.evaluation import evaluate_task_delivery
 from ..contracts.models import (
     ExecutorDelivery,

+ 0 - 0
production build agents/src/production_build_agents/preprocess/__init__.py → production_build_agents/preprocess/__init__.py


+ 0 - 0
production build agents/src/production_build_agents/preprocess/production_brief.py → production_build_agents/preprocess/production_brief.py


+ 0 - 0
production build agents/src/production_build_agents/preprocess/source_assets.py → production_build_agents/preprocess/source_assets.py


+ 0 - 0
production build agents/src/production_build_agents/run/__init__.py → production_build_agents/run/__init__.py


+ 0 - 0
production build agents/src/production_build_agents/run/langgraph_checkpointer.py → production_build_agents/run/langgraph_checkpointer.py


+ 0 - 0
production build agents/src/production_build_agents/run/lock.py → production_build_agents/run/lock.py


+ 0 - 0
production build agents/src/production_build_agents/run/operation_journal.py → production_build_agents/run/operation_journal.py


+ 0 - 0
production build agents/src/production_build_agents/run/records.py → production_build_agents/run/records.py


+ 0 - 0
production build agents/src/production_build_agents/run/registry.py → production_build_agents/run/registry.py


+ 0 - 0
production build agents/src/production_build_agents/state.py → production_build_agents/state.py


+ 0 - 0
production build agents/src/production_build_agents/tools/README.md → production_build_agents/tools/README.md


+ 0 - 0
production build agents/src/production_build_agents/tools/__init__.py → production_build_agents/tools/__init__.py


+ 28 - 0
production_build_agents/tools/brief_reader.py

@@ -0,0 +1,28 @@
+"""按授权 JSONPath 读取 Production Brief 的 Agent Tool。"""
+
+from __future__ import annotations
+
+from typing import Any
+
+from langchain_core.tools import BaseTool, tool
+
+from ..contracts.models import ProductionBrief
+from ..preprocess.production_brief import BriefAccessError, read_brief_paths
+
+
+def create_brief_reader_tool(brief: ProductionBrief) -> BaseTool:
+    """创建只允许读取 ProductionBrief 已授权路径的工具。"""
+
+    @tool("read_production_brief")
+    def read_production_brief(source_paths: list[str]) -> dict[str, Any]:
+        """按目录中的 JSONPath 读取 Production Brief 局部内容。"""
+
+        try:
+            return {
+                "success": True,
+                "items": read_brief_paths(brief, source_paths),
+            }
+        except BriefAccessError as exc:
+            return {"success": False, "error": str(exc)}
+
+    return read_production_brief

+ 0 - 0
production build agents/src/production_build_agents/tools/discovery.py → production_build_agents/tools/discovery.py


+ 0 - 0
production build agents/src/production_build_agents/tools/media.py → production_build_agents/tools/media.py


+ 0 - 0
production build agents/src/production_build_agents/tools/publishing.py → production_build_agents/tools/publishing.py


+ 0 - 0
production build agents/src/production_build_agents/tools/registry.py → production_build_agents/tools/registry.py


+ 1 - 1
production build agents/pyproject.toml → pyproject.toml

@@ -20,7 +20,7 @@ requires = ["setuptools>=75"]
 build-backend = "setuptools.build_meta"
 
 [tool.setuptools.packages.find]
-where = ["src"]
+where = ["."]
 include = ["production_build_agents*"]
 
 [dependency-groups]

+ 0 - 0
production build agents/run_demo.py → run_demo.py


+ 0 - 0
production build agents/tests/__init__.py → tests/__init__.py


+ 1 - 0
tests/agents/__init__.py

@@ -0,0 +1 @@
+"""Agent 测试。"""

+ 15 - 13
production build agents/tests/test_executor.py → tests/agents/test_executor.py

@@ -16,7 +16,7 @@ from production_build_agents.preprocess.production_brief import (
 from production_build_agents.contracts.models import (
     CandidateArtifact,
     ExecutorCandidate,
-    FulfillmentClaim,
+    ArtifactBindingClaim,
 )
 from production_build_agents.agents.executor.agent import (
     ExecutorOutputError,
@@ -43,17 +43,19 @@ from production_build_agents.run.langgraph_checkpointer import (
     create_sqlite_checkpointer,
 )
 from production_build_agents.tools.registry import ToolRegistry, create_default_tool_registry
-from tests.executor_fixtures import (
+from tests.support.executor_fixtures import (
     build_executor_candidate,
     build_executor_model,
 )
-from tests.fake_models import ToolAwareFakeChatModel
-from tests.planner_fixtures import (
+from tests.support.fake_models import ToolAwareFakeChatModel
+from tests.support.planner_fixtures import (
     build_planned_task,
     build_task_package,
 )
 
-BRIEF_PATH = Path(__file__).parent / "fixtures" / "minimal_brief.json"
+BRIEF_PATH = (
+    Path(__file__).parents[1] / "fixtures" / "minimal_brief.json"
+)
 IMAGE_URL = "https://example.test/generated-reference.png"
 SOURCE_URL = "https://example.test/research-source"
 VIDEO_URL = "https://example.test/generated-video.mp4"
@@ -343,8 +345,8 @@ class GeneralExecutorTest(unittest.TestCase):
                     description="错误绑定的参考图",
                 )
             ],
-            fulfillment_claims=[
-                FulfillmentClaim(
+            artifact_binding_claims=[
+                ArtifactBindingClaim(
                     expectation_id="Requirement1-Expectation1",
                     artifact_uri=local_a,
                 )
@@ -446,8 +448,8 @@ class GeneralExecutorTest(unittest.TestCase):
                         description="已检查并缓存的人物参考图",
                     )
                 ],
-                fulfillment_claims=[
-                    FulfillmentClaim(
+                artifact_binding_claims=[
+                    ArtifactBindingClaim(
                         expectation_id="Requirement1-Expectation1",
                         artifact_uri=str(image_path.resolve()),
                     )
@@ -555,8 +557,8 @@ class GeneralExecutorTest(unittest.TestCase):
                     description="已检查的人物参考图",
                 )
             ],
-            fulfillment_claims=[
-                FulfillmentClaim(
+            artifact_binding_claims=[
+                ArtifactBindingClaim(
                     expectation_id="Requirement1-Expectation1",
                     artifact_uri=probe_path,
                 )
@@ -674,8 +676,8 @@ class GeneralExecutorTest(unittest.TestCase):
                     description="已检查的动作参考视频",
                 )
             ],
-            fulfillment_claims=[
-                FulfillmentClaim(
+            artifact_binding_claims=[
+                ArtifactBindingClaim(
                     expectation_id="Requirement1-Expectation1",
                     artifact_uri=probe_path,
                 )

+ 6 - 5
production build agents/tests/test_planner.py → tests/agents/test_planner.py

@@ -21,22 +21,23 @@ from production_build_agents.agents.planner.agent import (
     validate_plan_runtime,
     validate_replan,
 )
-from tests.planner_fixtures import (
+from tests.support.planner_fixtures import (
     build_global_data_plan,
     build_planned_task,
     build_planner_model,
 )
-from tests.fake_models import ToolAwareFakeChatModel
+from tests.support.fake_models import ToolAwareFakeChatModel
 
 
-BRIEF_PATH = Path(__file__).parent / "fixtures" / "minimal_brief.json"
+BRIEF_PATH = (
+    Path(__file__).parents[1] / "fixtures" / "minimal_brief.json"
+)
 
 
 class PlannerDagTest(unittest.TestCase):
     def test_planner_core_is_generic_and_global_rules_live_in_skill(self) -> None:
         root = (
-            Path(__file__).parents[1]
-            / "src"
+            Path(__file__).parents[2]
             / "production_build_agents"
             / "agents"
             / "planner"

+ 9 - 7
production build agents/tests/test_validator.py → tests/agents/test_task_validator.py

@@ -12,7 +12,7 @@ from production_build_agents.contracts.models import (
     AcceptanceCriterion,
     ArtifactExpectation,
     Artifact,
-    ArtifactFulfillment,
+    ArtifactExpectationBinding,
     CandidateArtifact,
     ExecutorCandidate,
     ExecutorDelivery,
@@ -27,8 +27,8 @@ from production_build_agents.contracts.models import (
     ValidatorCandidate,
 )
 from production_build_agents.tools.registry import ToolRegistry
-from production_build_agents.agents.validator.agent import run_validator_agent
-from production_build_agents.agents.validator.context import (
+from production_build_agents.agents.validator.task_agent import run_validator_agent
+from production_build_agents.agents.validator.task_context import (
     ValidatorContextError,
     build_validator_user_message,
     create_validator_run_id,
@@ -37,10 +37,12 @@ from production_build_agents.agents.validator.skills.registry import (
     ValidatorSkillError,
     load_validator_skill,
 )
-from tests.fake_models import ToolAwareFakeChatModel
+from tests.support.fake_models import ToolAwareFakeChatModel
 
 
-BRIEF_PATH = Path(__file__).parent / "fixtures" / "minimal_brief.json"
+BRIEF_PATH = (
+    Path(__file__).parents[1] / "fixtures" / "minimal_brief.json"
+)
 
 
 def _task(
@@ -187,8 +189,8 @@ def _delivery(
         deliverable_type=task.expected_output.output_type,
         manifest_uri=str(manifest),
         artifacts=artifacts,
-        fulfillments=[
-            ArtifactFulfillment(
+        artifact_expectation_bindings=[
+            ArtifactExpectationBinding(
                 expectation_id="Requirement1-Expectation1",
                 artifact_id="Task1-artifact-1",
             )

+ 1 - 0
tests/contracts/__init__.py

@@ -0,0 +1 @@
+"""合同与纯评估测试。"""

+ 28 - 26
production build agents/tests/test_global_data_stage.py → tests/contracts/test_evaluation.py

@@ -8,7 +8,7 @@ from production_build_agents.contracts.models import (
     AcceptanceCriterion,
     Artifact,
     ArtifactExpectation,
-    ArtifactFulfillment,
+    ArtifactExpectationBinding,
     ExecutorDelivery,
     ExpectedOutput,
     GlobalDataPlan,
@@ -233,8 +233,8 @@ def _delivery(
                 description="辅助产物",
             )
         )
-    fulfillments = [
-        ArtifactFulfillment(
+    artifact_expectation_bindings = [
+        ArtifactExpectationBinding(
             expectation_id=expectation_id,
             artifact_id=artifact_id,
             source_asset_id=source_asset_id,
@@ -255,7 +255,7 @@ def _delivery(
         deliverable_type=task.expected_output.output_type,
         manifest_uri="/fixture/executor_candidate.json",
         artifacts=artifacts,
-        fulfillments=fulfillments,
+        artifact_expectation_bindings=artifact_expectation_bindings,
         attempt_count=1,
         summary=TaskSummary(
             task_id=task.task_id,
@@ -290,7 +290,7 @@ def _report(task: TaskPackage) -> ValidationReport:
 
 
 class IdentityPolicyTest(unittest.TestCase):
-    def test_one_artifact_may_explicitly_fulfill_multiple_expectations(
+    def test_one_artifact_may_bind_multiple_expectations(
         self,
     ) -> None:
         brief = _brief()
@@ -316,13 +316,13 @@ class IdentityPolicyTest(unittest.TestCase):
             [],
         )
         evaluation = evaluate_stage(plan, [delivery], [_report(task)])
-        self.assertEqual(evaluation.coverage_gaps, [])
+        self.assertEqual(evaluation.expectation_gaps, [])
         self.assertEqual(
             [
-                coverage.artifact_ids
-                for coverage in evaluation.requirement_coverages[
+                expectation_evaluation.artifact_ids
+                for expectation_evaluation in evaluation.requirement_evaluations[
                     0
-                ].expectation_coverages
+                ].expectation_evaluations
             ],
             [["Task1-artifact-1"], ["Task1-artifact-1"]],
         )
@@ -354,7 +354,7 @@ class IdentityPolicyTest(unittest.TestCase):
             [
                 item.expectation_id
                 for item in issues
-                if item.code == "fulfillment_artifact_type_mismatch"
+                if item.code == "binding_artifact_type_mismatch"
             ],
             ["Requirement1-Expectation2"],
         )
@@ -382,12 +382,12 @@ class IdentityPolicyTest(unittest.TestCase):
 
         issues = evaluate_task_delivery(task, delivery, CAPABILITIES)
         self.assertIn(
-            "insufficient_fulfillments",
+            "insufficient_artifact_bindings",
             {item.code for item in issues},
         )
         evaluation = evaluate_stage(plan, [delivery], [_report(task)])
         self.assertEqual(
-            [gap.expectation_id for gap in evaluation.coverage_gaps],
+            [gap.expectation_id for gap in evaluation.expectation_gaps],
             ["Requirement2-Expectation1"],
         )
 
@@ -447,10 +447,12 @@ class IdentityPolicyTest(unittest.TestCase):
             [],
         )
         evaluation = evaluate_stage(plan, [delivery], [_report(task)])
-        self.assertEqual(evaluation.coverage_gaps, [])
-        coverages = evaluation.requirement_coverages[0].expectation_coverages
+        self.assertEqual(evaluation.expectation_gaps, [])
+        expectation_evaluations = (
+            evaluation.requirement_evaluations[0].expectation_evaluations
+        )
         self.assertEqual(
-            [item.artifact_ids for item in coverages],
+            [item.artifact_ids for item in expectation_evaluations],
             [["Task1-artifact-1"], ["Task1-artifact-2"]],
         )
 
@@ -465,10 +467,10 @@ class IdentityPolicyTest(unittest.TestCase):
 
         evaluation = evaluate_stage(plan, [delivery], [_report(task)])
         self.assertEqual(
-            evaluation.requirement_coverages[0].actual_artifact_ids,
+            evaluation.requirement_evaluations[0].actual_artifact_ids,
             [],
         )
-        self.assertEqual(len(evaluation.coverage_gaps), 1)
+        self.assertEqual(len(evaluation.expectation_gaps), 1)
 
     def test_generated_skill_cannot_adopt_source_identity(self) -> None:
         brief = _brief("https://example.test/a.png")
@@ -544,20 +546,20 @@ class IdentityPolicyTest(unittest.TestCase):
             "artifact_id": (
                 valid.model_copy(
                     update={
-                        "fulfillments": [
-                            valid.fulfillments[0].model_copy(
+                        "artifact_expectation_bindings": [
+                            valid.artifact_expectation_bindings[0].model_copy(
                                 update={"artifact_id": "Task1-artifact-404"}
                             )
                         ]
                     }
                 ),
-                "unknown_fulfillment_artifact",
+                "unknown_binding_artifact",
             ),
             "source": (
                 valid.model_copy(
                     update={
-                        "fulfillments": [
-                            valid.fulfillments[0].model_copy(
+                        "artifact_expectation_bindings": [
+                            valid.artifact_expectation_bindings[0].model_copy(
                                 update={"source_asset_id": bad_source_id}
                             )
                         ]
@@ -568,8 +570,8 @@ class IdentityPolicyTest(unittest.TestCase):
             "evidence": (
                 valid.model_copy(
                     update={
-                        "fulfillments": [
-                            valid.fulfillments[0].model_copy(
+                        "artifact_expectation_bindings": [
+                            valid.artifact_expectation_bindings[0].model_copy(
                                 update={
                                     "evidence_tool_call_ids": [
                                         "missing-tool-call"
@@ -579,7 +581,7 @@ class IdentityPolicyTest(unittest.TestCase):
                         ]
                     }
                 ),
-                "unknown_fulfillment_evidence",
+                "unknown_binding_evidence",
             ),
         }
 
@@ -655,7 +657,7 @@ class IdentityPolicyTest(unittest.TestCase):
                 next_plan,
                 {"Task1"},
                 [],
-                evaluation.coverage_gaps,
+                evaluation.expectation_gaps,
             ),
             [],
         )

+ 0 - 0
production build agents/tests/fixtures/minimal_brief.json → tests/fixtures/minimal_brief.json


+ 0 - 0
production build agents/tests/fixtures/minimal_input.json → tests/fixtures/minimal_input.json


+ 1 - 1
production build agents/tests/fixtures/v18_replay/delivery.Task1.v1.json → tests/fixtures/v18_replay/deliveries/delivery.Task1.v1.json

@@ -17,7 +17,7 @@
       "description": "v18 角色基准图的脱敏缓存引用"
     }
   ],
-  "fulfillments": [
+  "artifact_expectation_bindings": [
     {
       "expectation_id": "Requirement1-Expectation1",
       "artifact_id": "Task1-artifact-1",

+ 1 - 1
production build agents/tests/fixtures/v18_replay/delivery.Task2.v2.json → tests/fixtures/v18_replay/deliveries/delivery.Task2.v2.json

@@ -17,7 +17,7 @@
       "description": "v18 手势引导视频的脱敏缓存引用"
     }
   ],
-  "fulfillments": [
+  "artifact_expectation_bindings": [
     {
       "expectation_id": "Requirement2-Expectation1",
       "artifact_id": "Task2-artifact-1",

+ 0 - 0
production build agents/tests/fixtures/v18_replay/plan.v1.json → tests/fixtures/v18_replay/plans/plan.v1.json


+ 0 - 0
production build agents/tests/fixtures/v18_replay/plan.v2.json → tests/fixtures/v18_replay/plans/plan.v2.json


+ 0 - 0
production build agents/tests/fixtures/v18_replay/production_brief.json → tests/fixtures/v18_replay/production_brief.json


+ 6 - 6
production build agents/tests/fixtures/v18_replay/expected_stage.v1.json → tests/fixtures/v18_replay/stage_reports/expected_stage.v1.json

@@ -1,11 +1,11 @@
 {
-  "requirement_coverages": [
+  "requirement_evaluations": [
     {
       "requirement_id": "Requirement1",
-      "covering_task_ids": [
+      "supporting_task_ids": [
         "Task1"
       ],
-      "expectation_coverages": [
+      "expectation_evaluations": [
         {
           "expectation_id": "Requirement1-Expectation1",
           "expected_count": 1,
@@ -25,8 +25,8 @@
     },
     {
       "requirement_id": "Requirement2",
-      "covering_task_ids": [],
-      "expectation_coverages": [
+      "supporting_task_ids": [],
+      "expectation_evaluations": [
         {
           "expectation_id": "Requirement2-Expectation1",
           "expected_count": 1,
@@ -39,7 +39,7 @@
       "actual_artifact_ids": []
     }
   ],
-  "coverage_gaps": [
+  "expectation_gaps": [
     {
       "requirement_id": "Requirement2",
       "expectation_id": "Requirement2-Expectation1",

+ 6 - 6
production build agents/tests/fixtures/v18_replay/expected_stage.v2.json → tests/fixtures/v18_replay/stage_reports/expected_stage.v2.json

@@ -1,11 +1,11 @@
 {
-  "requirement_coverages": [
+  "requirement_evaluations": [
     {
       "requirement_id": "Requirement1",
-      "covering_task_ids": [
+      "supporting_task_ids": [
         "Task1"
       ],
-      "expectation_coverages": [
+      "expectation_evaluations": [
         {
           "expectation_id": "Requirement1-Expectation1",
           "expected_count": 1,
@@ -25,10 +25,10 @@
     },
     {
       "requirement_id": "Requirement2",
-      "covering_task_ids": [
+      "supporting_task_ids": [
         "Task2"
       ],
-      "expectation_coverages": [
+      "expectation_evaluations": [
         {
           "expectation_id": "Requirement2-Expectation1",
           "expected_count": 1,
@@ -47,7 +47,7 @@
       ]
     }
   ],
-  "coverage_gaps": [],
+  "expectation_gaps": [],
   "active_artifacts": [
     {
       "artifact_id": "Task1-artifact-1",

+ 1 - 1
production build agents/tests/fixtures/v18_replay/task.Task1.v1.json → tests/fixtures/v18_replay/tasks/task.Task1.v1.json

@@ -90,7 +90,7 @@
         "method": "validator_agent",
         "required_evidence": [
           "artifact",
-          "fulfillment"
+          "artifact_expectation_binding"
         ]
       },
       {

+ 1 - 1
production build agents/tests/fixtures/v18_replay/task.Task2.v2.json → tests/fixtures/v18_replay/tasks/task.Task2.v2.json

@@ -90,7 +90,7 @@
         "method": "validator_agent",
         "required_evidence": [
           "artifact",
-          "fulfillment"
+          "artifact_expectation_binding"
         ]
       },
       {

+ 1 - 1
production build agents/tests/fixtures/v18_replay/report.Task1.v1.json → tests/fixtures/v18_replay/validation_reports/report.Task1.v1.json

@@ -10,7 +10,7 @@
       "criterion_id": "Task1-criterion-1",
       "verdict": "PASS",
       "evidence": [
-        "ArtifactFulfillment 精确绑定角色基准图 SourceAsset。"
+        "ArtifactExpectationBinding 精确绑定角色基准图 SourceAsset。"
       ],
       "reason": "来源身份一致。"
     },

+ 1 - 1
production build agents/tests/fixtures/v18_replay/report.Task2.v2.json → tests/fixtures/v18_replay/validation_reports/report.Task2.v2.json

@@ -10,7 +10,7 @@
       "criterion_id": "Task2-criterion-1",
       "verdict": "PASS",
       "evidence": [
-        "ArtifactFulfillment 精确绑定手势引导视频 SourceAsset。"
+        "ArtifactExpectationBinding 精确绑定手势引导视频 SourceAsset。"
       ],
       "reason": "来源身份一致。"
     },

+ 0 - 0
production build agents/tests/golden_case/preprocess_report.md → tests/golden_case/preprocess_report.md


+ 0 - 0
production build agents/tests/golden_case/production_brief.json → tests/golden_case/production_brief.json


+ 1 - 0
tests/graph/__init__.py

@@ -0,0 +1 @@
+"""Graph 与恢复测试。"""

+ 6 - 6
production build agents/tests/test_graph.py → tests/graph/test_graph.py

@@ -22,18 +22,18 @@ from production_build_agents.graph_nodes.finish_run import (
 from production_build_agents.run.langgraph_checkpointer import create_in_memory_checkpointer
 from production_build_agents.run.operation_journal import OperationOutcomeUnknownError
 from production_build_agents.agents.planner.agent import create_planner_agent
-from production_build_agents.agents.validator.agent import create_validator_agent
-from tests.executor_fixtures import (
+from production_build_agents.agents.validator.task_agent import create_validator_agent
+from tests.support.executor_fixtures import (
     build_executor_candidate,
     build_executor_model,
 )
-from tests.fake_models import ToolAwareFakeChatModel
-from tests.planner_fixtures import (
+from tests.support.fake_models import ToolAwareFakeChatModel
+from tests.support.planner_fixtures import (
     build_global_data_plan,
     build_planned_task,
     build_planner_model,
 )
-from tests.validator_fixtures import (
+from tests.support.validator_fixtures import (
     build_stage_read_call,
     build_validator_candidate,
     build_validator_model,
@@ -42,7 +42,7 @@ from tests.validator_fixtures import (
 
 
 INPUT_PATH = (
-    Path(__file__).parent / "fixtures" / "minimal_input.json"
+    Path(__file__).parents[1] / "fixtures" / "minimal_input.json"
 )
 
 

+ 7 - 7
production build agents/tests/test_recovery.py → tests/graph/test_recovery.py

@@ -19,19 +19,19 @@ from production_build_agents.run.records import VersionConflictError
 from production_build_agents.tools.registry import (
     create_default_tool_registry as real_create_default_tool_registry,
 )
-from production_build_agents.agents.validator.context import create_validator_run_id
-from tests.executor_fixtures import (
+from production_build_agents.agents.validator.task_context import create_validator_run_id
+from tests.support.executor_fixtures import (
     build_executor_candidate,
     build_executor_model,
 )
-from tests.fake_models import ToolAwareFakeChatModel
-from tests.planner_fixtures import (
+from tests.support.fake_models import ToolAwareFakeChatModel
+from tests.support.planner_fixtures import (
     build_global_data_plan,
     build_planned_task,
     build_planner_model,
     build_task_package,
 )
-from tests.validator_fixtures import (
+from tests.support.validator_fixtures import (
     build_stage_read_call,
     build_validator_candidate,
     build_validator_model,
@@ -40,7 +40,7 @@ from tests.validator_fixtures import (
 
 
 INPUT_PATH = (
-    Path(__file__).parent / "fixtures" / "minimal_input.json"
+    Path(__file__).parents[1] / "fixtures" / "minimal_input.json"
 )
 
 
@@ -109,7 +109,7 @@ class DurableRunTest(unittest.TestCase):
         with tempfile.TemporaryDirectory() as temp_dir:
             root = Path(temp_dir)
             task = build_task_package(
-                Path(__file__).parent
+                Path(__file__).parents[1]
                 / "fixtures"
                 / "minimal_brief.json",
                 run_id=run_id,

Некоторые файлы не были показаны из-за большого количества измененных файлов