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

合同:升级 Production 0.7 并定义 Progress Review 与 ADAPT

将全部 Production 正式记录 schema 升级到 0.7,并统一 Plan revision 上限与授权字段语义。

新增 ProductionProgressPackage、ProductionProgressDecision 及 PASS/FAIL 到 CONTINUE、ADAPT、REPLAN、STOP 的确定性合法矩阵。

补充 ADAPT PlanningPackage、sealed input、Segment 映射、时间线、授权范围和 no-op transition 校验;合同层继续保持对文件系统、LLM 和 LangGraph 的零依赖。

同步覆盖协议版本、Progress 引用、Accepted 快照、revision budget、ADAPT/REPLAN 越权修改和原有 Production 合同回归测试。
SamLee 3 дней назад
Родитель
Сommit
babdccdb0e

+ 1 - 1
production_build_agents/contracts/__init__.py

@@ -1 +1 @@
-"""Global Data 0.3 与当前 Production 0.6 正式合同。"""
+"""Global Data 0.3 与当前 Production 0.7 正式合同。"""

+ 2 - 2
production_build_agents/contracts/production_assembly_models.py

@@ -43,7 +43,7 @@ class ProductionAssemblyPackage(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1)
@@ -249,7 +249,7 @@ class ProductionAssemblyDelivery(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1)

+ 2 - 2
production_build_agents/contracts/production_execution_models.py

@@ -74,7 +74,7 @@ class SegmentPackage(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1, le=MAX_PRODUCTION_PLAN_VERSION)
@@ -220,7 +220,7 @@ class ProductionPackage(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1, le=MAX_PRODUCTION_PLAN_VERSION)

+ 7 - 7
production_build_agents/contracts/production_models.py

@@ -13,9 +13,9 @@ from pydantic import BaseModel, ConfigDict, Field, JsonValue, model_validator
 from .models import ArtifactId, CandidateArtifact, ToolCallRecord
 
 
-ProductionSchemaVersion = Literal["0.6"]
-MAX_PRODUCTION_REPLANS = 5
-MAX_PRODUCTION_PLAN_VERSION = 1 + MAX_PRODUCTION_REPLANS
+ProductionSchemaVersion = Literal["0.7"]
+MAX_PRODUCTION_PLAN_REVISIONS = 5
+MAX_PRODUCTION_PLAN_VERSION = 1 + MAX_PRODUCTION_PLAN_REVISIONS
 ProductionInputId = Annotated[
     str,
     Field(pattern=r"^ProductionInput-[0-9a-f]{64}$"),
@@ -90,7 +90,7 @@ class ProductionInputCatalog(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     production_brief_uri: str = Field(min_length=1)
     production_brief_sha256: str = Field(pattern=r"^[0-9a-f]{64}$")
     source_segment_ids: List[str] = Field(min_length=1)
@@ -243,7 +243,7 @@ class SegmentExecutorCandidate(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1)
@@ -321,7 +321,7 @@ class SegmentDelivery(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1)
@@ -373,7 +373,7 @@ class SegmentValidatorCandidate(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1)

+ 563 - 36
production_build_agents/contracts/production_planning_evaluation.py

@@ -20,13 +20,18 @@ from .production_execution_models import (
     SegmentPackage,
 )
 from .production_models import (
+    MAX_PRODUCTION_PLAN_REVISIONS,
     ProductionContractIssue,
     ProductionInputCatalog,
+    SegmentDelivery,
+    SegmentValidationReport,
 )
 from .production_planning_models import (
     PlannedSegment,
     ProductionPlan,
     ProductionPlanningPackage,
+    ProductionProgressDecision,
+    ProductionProgressPackage,
 )
 from .production_validation_models import (
     ProductionValidationReport,
@@ -34,6 +39,22 @@ from .production_validation_models import (
 )
 
 
+def remaining_plan_revisions(
+    plan_version: int,
+    max_plan_revisions: int,
+) -> int:
+    """由正式 Plan version 和 Run 预算确定性计算剩余 revision 次数。"""
+
+    if plan_version < 1:
+        raise ValueError("plan_version 必须至少为 1")
+    if not 0 <= max_plan_revisions <= MAX_PRODUCTION_PLAN_REVISIONS:
+        raise ValueError(
+            "max_plan_revisions 必须在 0 到 "
+            f"{MAX_PRODUCTION_PLAN_REVISIONS} 之间"
+        )
+    return max(0, max_plan_revisions - (plan_version - 1))
+
+
 def evaluate_production_planning_package(
     brief: ProductionBrief,
     delivery: GlobalDataStageDelivery,
@@ -99,6 +120,324 @@ def evaluate_production_planning_package(
     return issues
 
 
+def evaluate_production_progress_package(
+    package: ProductionProgressPackage,
+    *,
+    planning_package: ProductionPlanningPackage,
+    planning_package_ref: ImmutableJsonRef,
+    production_plan: ProductionPlan,
+    production_plan_ref: ImmutableJsonRef,
+    segment_package: SegmentPackage,
+    segment_package_ref: ImmutableJsonRef,
+    segment_delivery: SegmentDelivery,
+    segment_delivery_ref: ImmutableJsonRef,
+    segment_validation_report: SegmentValidationReport,
+    segment_validation_report_ref: ImmutableJsonRef,
+    accepted_segments: Iterable[AcceptedSegmentRef],
+    remaining_plan_revisions: int,
+) -> list[ProductionContractIssue]:
+    """重验 ProgressPackage 精确绑定当前正式 Segment 证据链。"""
+
+    issues: list[ProductionContractIssue] = []
+    expected_identity = (
+        package.run_id,
+        package.plan_id,
+        package.plan_version,
+        package.segment_id,
+    )
+    for label, value in (
+        (
+            "planning_package",
+            (
+                planning_package.run_id,
+                planning_package.plan_id,
+                planning_package.target_plan_version,
+            ),
+        ),
+        (
+            "production_plan",
+            (
+                package.run_id,
+                production_plan.plan_id,
+                production_plan.plan_version,
+                package.segment_id,
+            ),
+        ),
+        (
+            "segment_package",
+            (
+                segment_package.run_id,
+                segment_package.plan_id,
+                segment_package.plan_version,
+                segment_package.segment_id,
+            ),
+        ),
+        (
+            "segment_delivery",
+            (
+                segment_delivery.run_id,
+                segment_delivery.plan_id,
+                segment_delivery.plan_version,
+                segment_delivery.segment_id,
+            ),
+        ),
+        (
+            "segment_validation_report",
+            (
+                segment_validation_report.run_id,
+                segment_validation_report.plan_id,
+                segment_validation_report.plan_version,
+                segment_validation_report.segment_id,
+            ),
+        ),
+    ):
+        expected = (
+            expected_identity[:3]
+            if label == "planning_package"
+            else expected_identity
+        )
+        if value != expected:
+            add_production_issue(
+                issues,
+                "production_progress_identity_mismatch",
+                f"{label} 与 ProductionProgressPackage 身份不一致",
+                segment_id=package.segment_id,
+            )
+    for field_name, expected in (
+        ("planning_package_ref", planning_package_ref),
+        ("production_plan_ref", production_plan_ref),
+        ("segment_package_ref", segment_package_ref),
+        ("segment_delivery_ref", segment_delivery_ref),
+        (
+            "segment_validation_report_ref",
+            segment_validation_report_ref,
+        ),
+    ):
+        if getattr(package, field_name) != expected:
+            add_production_issue(
+                issues,
+                "production_progress_document_ref_mismatch",
+                f"ProductionProgressPackage.{field_name} 未绑定当前精确字节",
+                segment_id=package.segment_id,
+            )
+    if production_plan.planning_package_ref != planning_package_ref:
+        add_production_issue(
+            issues,
+            "production_progress_plan_source_mismatch",
+            "ProductionPlan 没有绑定当前 PlanningPackage",
+            segment_id=package.segment_id,
+        )
+    if (
+        segment_delivery.segment_package_uri != segment_package_ref.uri
+        or segment_delivery.segment_package_sha256
+        != segment_package_ref.document_sha256
+    ):
+        add_production_issue(
+            issues,
+            "production_progress_delivery_source_mismatch",
+            "SegmentDelivery 没有绑定当前 SegmentPackage",
+            segment_id=package.segment_id,
+        )
+    if (
+        segment_validation_report.executor_run_id
+        != segment_delivery.executor_run_id
+    ):
+        add_production_issue(
+            issues,
+            "production_progress_validator_source_mismatch",
+            "SegmentValidationReport 与 SegmentDelivery executor_run_id 不一致",
+            segment_id=package.segment_id,
+        )
+
+    expected_accepted = list(accepted_segments)
+    if package.accepted_segments != expected_accepted:
+        add_production_issue(
+            issues,
+            "production_progress_accepted_snapshot_mismatch",
+            "ProductionProgressPackage 没有绑定当前 Accepted Segment 快照",
+            segment_id=package.segment_id,
+        )
+    known_ids = {item.segment_id for item in production_plan.segments}
+    for accepted in package.accepted_segments:
+        if accepted.segment_id not in known_ids:
+            add_production_issue(
+                issues,
+                "production_progress_unknown_accepted_segment",
+                "Accepted Segment 不属于当前 ProductionPlan",
+                segment_id=accepted.segment_id,
+            )
+    current_accepted = next(
+        (
+            accepted
+            for accepted in package.accepted_segments
+            if accepted.segment_id == package.segment_id
+        ),
+        None,
+    )
+    if segment_validation_report.verdict == "PASS":
+        if current_accepted is None:
+            add_production_issue(
+                issues,
+                "production_progress_pass_not_accepted",
+                "正式 PASS Segment 必须已进入 Accepted 快照",
+                segment_id=package.segment_id,
+            )
+        elif (
+            current_accepted.source_run_id != package.run_id
+            or current_accepted.source_plan_id != package.plan_id
+            or current_accepted.source_plan_version != package.plan_version
+            or current_accepted.segment_package_ref != segment_package_ref
+            or current_accepted.segment_delivery_ref != segment_delivery_ref
+            or current_accepted.validation_report_ref
+            != segment_validation_report_ref
+        ):
+            add_production_issue(
+                issues,
+                "production_progress_pass_acceptance_mismatch",
+                "当前 PASS AcceptedSegmentRef 没有绑定同一正式证据链",
+                segment_id=package.segment_id,
+            )
+    elif current_accepted is not None:
+        add_production_issue(
+            issues,
+            "production_progress_fail_cannot_be_accepted",
+            "正式 FAIL Segment 不得进入 Accepted 快照",
+            segment_id=package.segment_id,
+        )
+    if package.remaining_plan_revisions != remaining_plan_revisions:
+        add_production_issue(
+            issues,
+            "production_progress_revision_budget_mismatch",
+            "ProductionProgressPackage 的 Plan revision 余额不一致",
+            segment_id=package.segment_id,
+        )
+    return issues
+
+
+def evaluate_production_progress_decision(
+    package: ProductionProgressPackage,
+    decision: ProductionProgressDecision,
+    *,
+    progress_package_ref: ImmutableJsonRef,
+    production_plan: ProductionPlan,
+    segment_validation_report: SegmentValidationReport,
+) -> list[ProductionContractIssue]:
+    """根据正式 verdict、pending DAG 和预算重算 Planner 路由决定。"""
+
+    issues: list[ProductionContractIssue] = []
+    if (
+        decision.run_id,
+        decision.plan_id,
+        decision.plan_version,
+        decision.segment_id,
+    ) != (
+        package.run_id,
+        package.plan_id,
+        package.plan_version,
+        package.segment_id,
+    ):
+        add_production_issue(
+            issues,
+            "production_progress_decision_identity_mismatch",
+            "ProductionProgressDecision 与 ProgressPackage 身份不一致",
+            segment_id=package.segment_id,
+        )
+    if decision.progress_package_ref != progress_package_ref:
+        add_production_issue(
+            issues,
+            "production_progress_decision_ref_mismatch",
+            "ProductionProgressDecision 没有绑定当前 ProgressPackage",
+            segment_id=package.segment_id,
+        )
+    if (
+        segment_validation_report.run_id != package.run_id
+        or segment_validation_report.plan_id != package.plan_id
+        or segment_validation_report.plan_version != package.plan_version
+        or segment_validation_report.segment_id != package.segment_id
+    ):
+        add_production_issue(
+            issues,
+            "production_progress_report_identity_mismatch",
+            "SegmentValidationReport 与 ProgressPackage 身份不一致",
+            segment_id=package.segment_id,
+        )
+
+    allowed_actions = (
+        {"CONTINUE", "ADAPT", "STOP"}
+        if segment_validation_report.verdict == "PASS"
+        else {"REPLAN", "STOP"}
+    )
+    if decision.action not in allowed_actions:
+        add_production_issue(
+            issues,
+            "production_progress_action_verdict_mismatch",
+            "Planner action 不符合正式 Segment verdict",
+            segment_id=package.segment_id,
+        )
+
+    plan_ids = {item.segment_id for item in production_plan.segments}
+    if (
+        production_plan.plan_id != package.plan_id
+        or production_plan.plan_version != package.plan_version
+    ):
+        add_production_issue(
+            issues,
+            "production_progress_plan_identity_mismatch",
+            "ProductionPlan 与 ProgressPackage 身份不一致",
+            segment_id=package.segment_id,
+        )
+    accepted_ids = {
+        item.segment_id for item in package.accepted_segments
+    }
+    pending_ids = plan_ids - accepted_ids
+    if decision.action == "ADAPT":
+        for segment_id in set(decision.affected_segment_ids) - pending_ids:
+            add_production_issue(
+                issues,
+                "production_progress_adapt_segment_not_pending",
+                "ADAPT 只能修改尚未 Accepted 的 Segment",
+                segment_id=segment_id,
+            )
+    if (
+        decision.action in {"ADAPT", "REPLAN"}
+        and package.remaining_plan_revisions == 0
+    ):
+        add_production_issue(
+            issues,
+            "production_progress_revision_budget_exhausted",
+            "ADAPT 或 REPLAN 没有剩余 Plan revision 预算",
+            segment_id=package.segment_id,
+        )
+    return issues
+
+
+def validate_production_progress_package(
+    package: ProductionProgressPackage,
+    **context: object,
+) -> None:
+    """ProgressPackage 不满足正式证据链时抛出结构化异常。"""
+
+    issues = evaluate_production_progress_package(package, **context)
+    if issues:
+        raise ProductionContractError(issues)
+
+
+def validate_production_progress_decision(
+    package: ProductionProgressPackage,
+    decision: ProductionProgressDecision,
+    **context: object,
+) -> None:
+    """ProgressDecision 不满足确定性路由规则时抛出结构化异常。"""
+
+    issues = evaluate_production_progress_decision(
+        package,
+        decision,
+        **context,
+    )
+    if issues:
+        raise ProductionContractError(issues)
+
+
 def _dependency_issues(
     plan: ProductionPlan,
 ) -> list[ProductionContractIssue]:
@@ -208,9 +547,15 @@ def evaluate_production_plan(
             "initial_plan_cannot_regenerate",
             "INITIAL ProductionPlan 不得声明 regenerate_segment_ids",
         )
+    if planning_package.mode == "ADAPT" and plan.regenerate_segment_ids:
+        add_production_issue(
+            issues,
+            "adapt_plan_cannot_regenerate",
+            "ADAPT 只调整未来 Plan,不得声明 regenerate_segment_ids",
+        )
     if planning_package.mode == "REPLAN":
         authorized = set(
-            planning_package.authorized_regenerate_segment_ids
+            planning_package.authorized_revision_segment_ids
         )
         for segment_id in set(plan.regenerate_segment_ids) - authorized:
             add_production_issue(
@@ -347,37 +692,41 @@ def _protected_segment_payload(
     }
 
 
-def evaluate_replan_transition(
+def _evaluate_revision_transition(
     previous_plan: ProductionPlan,
     current_plan: ProductionPlan,
     planning_package: ProductionPlanningPackage,
+    *,
+    mode: str,
 ) -> list[ProductionContractIssue]:
-    """验证版本推进、固定 Segment 映射、授权修改和有效返工。"""
+    """验证共同的版本推进、固定映射、授权修改和实质变化。"""
 
+    label = "Replan" if mode == "REPLAN" else "Adapt"
+    prefix = mode.lower()
     issues: list[ProductionContractIssue] = []
-    if planning_package.mode != "REPLAN":
+    if planning_package.mode != mode:
         add_production_issue(
             issues,
-            "replan_package_mode_mismatch",
-            "Replan transition 必须使用 REPLAN PlanningPackage",
+            f"{prefix}_package_mode_mismatch",
+            f"{label} transition 必须使用 {mode} PlanningPackage",
         )
     if current_plan.plan_id != previous_plan.plan_id:
         add_production_issue(
             issues,
-            "replan_plan_id_changed",
-            "Replan 不得改变 plan_id",
+            f"{prefix}_plan_id_changed",
+            f"{label} 不得改变 plan_id",
         )
     if current_plan.plan_version != previous_plan.plan_version + 1:
         add_production_issue(
             issues,
-            "replan_version_not_next",
-            "Replan 的 plan_version 必须精确加一",
+            f"{prefix}_version_not_next",
+            f"{label} 的 plan_version 必须精确加一",
         )
     if current_plan.plan_version != planning_package.target_plan_version:
         add_production_issue(
             issues,
-            "replan_target_version_mismatch",
-            "Replan 结果没有使用 PlanningPackage 的目标版本",
+            f"{prefix}_target_version_mismatch",
+            f"{label} 结果没有使用 PlanningPackage 的目标版本",
         )
     previous_identity = [
         (item.segment_id, item.source_segment_id)
@@ -390,8 +739,8 @@ def evaluate_replan_transition(
     if current_identity != previous_identity:
         add_production_issue(
             issues,
-            "replan_segment_mapping_changed",
-            "Replan 不得拆分、合并、新增或重排制作表 Segment",
+            f"{prefix}_segment_mapping_changed",
+            f"{label} 不得拆分、合并、新增或重排制作表 Segment",
         )
         return issues
     if [item.segment_id for item in current_plan.timeline] != [
@@ -399,17 +748,17 @@ def evaluate_replan_transition(
     ]:
         add_production_issue(
             issues,
-            "replan_timeline_order_changed",
-            "Replan 不得改变 Segment 时间线顺序",
+            f"{prefix}_timeline_order_changed",
+            f"{label} 不得改变 Segment 时间线顺序",
         )
 
     known_ids = {item.segment_id for item in previous_plan.segments}
-    authorized = set(planning_package.authorized_regenerate_segment_ids)
+    authorized = set(planning_package.authorized_revision_segment_ids)
     for segment_id in authorized - known_ids:
         add_production_issue(
             issues,
-            "replan_authorized_unknown_segment",
-            "返工授权包含上一版 Plan 之外的 Segment",
+            f"{prefix}_authorized_unknown_segment",
+            "Plan revision 授权包含上一版 Plan 之外的 Segment",
             segment_id=segment_id,
         )
     regenerated = set(current_plan.regenerate_segment_ids)
@@ -420,6 +769,12 @@ def evaluate_replan_transition(
             "Planner 不得重做代码未授权的 Segment",
             segment_id=segment_id,
         )
+    if mode == "ADAPT" and regenerated:
+        add_production_issue(
+            issues,
+            "adapt_plan_cannot_regenerate",
+            "ADAPT 不得要求重新生成已执行 Segment",
+        )
 
     current_by_id = {
         item.segment_id: item for item in current_plan.segments
@@ -434,34 +789,69 @@ def evaluate_replan_transition(
         if changed and previous.segment_id not in authorized:
             add_production_issue(
                 issues,
-                "protected_pass_segment_changed",
-                "未授权 PASS Segment 的业务执行输入必须保持不变",
+                (
+                    "protected_pass_segment_changed"
+                    if mode == "REPLAN"
+                    else "adapt_protected_segment_changed"
+                ),
+                "未授权 Segment 的业务执行输入必须保持不变",
                 segment_id=previous.segment_id,
             )
         if changed and previous.segment_id in authorized:
             changed_authorized.add(previous.segment_id)
-    if not changed_authorized and not regenerated:
+    has_effect = bool(changed_authorized)
+    if mode == "REPLAN":
+        has_effect = has_effect or bool(regenerated)
+    if not has_effect:
         add_production_issue(
             issues,
-            "replan_has_no_effect",
-            "Replan 必须修改授权 Segment 或显式要求重新生成",
+            f"{prefix}_has_no_effect",
+            (
+                "Replan 必须修改授权 Segment 或显式要求重新生成"
+                if mode == "REPLAN"
+                else "ADAPT 必须实质修改至少一个授权的未来 Segment"
+            ),
         )
     return issues
 
 
-def evaluate_replan_planning_package(
+def evaluate_replan_transition(
+    previous_plan: ProductionPlan,
+    current_plan: ProductionPlan,
+    planning_package: ProductionPlanningPackage,
+) -> list[ProductionContractIssue]:
+    """验证正式失败后的 Plan revision。"""
+
+    return _evaluate_revision_transition(
+        previous_plan,
+        current_plan,
+        planning_package,
+        mode="REPLAN",
+    )
+
+
+def evaluate_adapt_transition(
+    previous_plan: ProductionPlan,
+    current_plan: ProductionPlan,
+    planning_package: ProductionPlanningPackage,
+) -> list[ProductionContractIssue]:
+    """验证 PASS 后只影响未来未接纳 Segment 的 Plan revision。"""
+
+    return _evaluate_revision_transition(
+        previous_plan,
+        current_plan,
+        planning_package,
+        mode="ADAPT",
+    )
+
+
+def evaluate_revision_planning_package(
     previous: ProductionPlanningPackage,
     current: ProductionPlanningPackage,
 ) -> list[ProductionContractIssue]:
-    """验证 Replan Package 没有替换本 Run 的封印输入。"""
+    """验证 Plan revision Package 没有替换本 Run 的封印输入。"""
 
     issues: list[ProductionContractIssue] = []
-    if current.mode != "REPLAN":
-        add_production_issue(
-            issues,
-            "replan_package_mode_mismatch",
-            "下一版 ProductionPlanningPackage 必须使用 REPLAN mode",
-        )
     for field_name in (
         "run_id",
         "plan_id",
@@ -474,14 +864,137 @@ def evaluate_replan_planning_package(
         if getattr(current, field_name) != getattr(previous, field_name):
             add_production_issue(
                 issues,
-                "replan_sealed_input_changed",
-                f"Replan 不得改变 ProductionPlanningPackage.{field_name}",
+                "revision_sealed_input_changed",
+                (
+                    "Plan revision 不得改变 "
+                    f"ProductionPlanningPackage.{field_name}"
+                ),
             )
     if current.target_plan_version != previous.target_plan_version + 1:
         add_production_issue(
             issues,
-            "replan_package_version_not_next",
-            "Replan PlanningPackage 的目标版本必须精确加一",
+            "revision_package_version_not_next",
+            "Plan revision PlanningPackage 的目标版本必须精确加一",
+        )
+    return issues
+
+
+def evaluate_replan_planning_package(
+    previous: ProductionPlanningPackage,
+    current: ProductionPlanningPackage,
+    *,
+    progress_decision_ref: ImmutableJsonRef | None = None,
+) -> list[ProductionContractIssue]:
+    """验证 Replan Package 的 mode 与共同封印输入。"""
+
+    issues = [
+        issue.model_copy(
+            update={
+                "code": {
+                    "revision_sealed_input_changed": (
+                        "replan_sealed_input_changed"
+                    ),
+                    "revision_package_version_not_next": (
+                        "replan_package_version_not_next"
+                    ),
+                }.get(issue.code, issue.code)
+            }
+        )
+        for issue in evaluate_revision_planning_package(previous, current)
+    ]
+    if current.mode != "REPLAN":
+        add_production_issue(
+            issues,
+            "replan_package_mode_mismatch",
+            "下一版 ProductionPlanningPackage 必须使用 REPLAN mode",
+        )
+    if (
+        progress_decision_ref is not None
+        and current.progress_decision_ref != progress_decision_ref
+    ):
+        add_production_issue(
+            issues,
+            "replan_progress_decision_ref_mismatch",
+            "Segment REPLAN PlanningPackage 没有绑定当前 ProgressDecision",
+        )
+    return issues
+
+
+def evaluate_adapt_planning_package(
+    previous: ProductionPlanningPackage,
+    current: ProductionPlanningPackage,
+    *,
+    progress_package: ProductionProgressPackage,
+    progress_package_ref: ImmutableJsonRef,
+    progress_decision: ProductionProgressDecision,
+    progress_decision_ref: ImmutableJsonRef,
+) -> list[ProductionContractIssue]:
+    """验证 ADAPT Package 精确继承正式 Review 的快照与授权。"""
+
+    issues = [
+        issue.model_copy(
+            update={
+                "code": {
+                    "revision_sealed_input_changed": (
+                        "adapt_sealed_input_changed"
+                    ),
+                    "revision_package_version_not_next": (
+                        "adapt_package_version_not_next"
+                    ),
+                }.get(issue.code, issue.code)
+            }
+        )
+        for issue in evaluate_revision_planning_package(previous, current)
+    ]
+    if current.mode != "ADAPT":
+        add_production_issue(
+            issues,
+            "adapt_package_mode_mismatch",
+            "下一版 ProductionPlanningPackage 必须使用 ADAPT mode",
+        )
+    if current.progress_decision_ref != progress_decision_ref:
+        add_production_issue(
+            issues,
+            "adapt_progress_decision_ref_mismatch",
+            "ADAPT PlanningPackage 没有绑定当前 ProgressDecision",
+        )
+    if progress_decision.action != "ADAPT":
+        add_production_issue(
+            issues,
+            "adapt_progress_decision_action_mismatch",
+            "ADAPT PlanningPackage 只能绑定 ADAPT Decision",
+        )
+    if progress_decision.progress_package_ref != progress_package_ref:
+        add_production_issue(
+            issues,
+            "adapt_progress_package_ref_mismatch",
+            "ADAPT Decision 没有绑定当前 ProgressPackage",
+        )
+    if (
+        progress_package.plan_id != previous.plan_id
+        or progress_package.plan_version != previous.target_plan_version
+        or progress_decision.plan_id != previous.plan_id
+        or progress_decision.plan_version != previous.target_plan_version
+    ):
+        add_production_issue(
+            issues,
+            "adapt_progress_identity_mismatch",
+            "ADAPT review 身份不属于上一版 ProductionPlan",
+        )
+    if current.accepted_segments != progress_package.accepted_segments:
+        add_production_issue(
+            issues,
+            "adapt_accepted_snapshot_mismatch",
+            "ADAPT PlanningPackage 必须精确继承 ProgressPackage Accepted 快照",
+        )
+    if (
+        current.authorized_revision_segment_ids
+        != progress_decision.affected_segment_ids
+    ):
+        add_production_issue(
+            issues,
+            "adapt_authorized_segments_mismatch",
+            "ADAPT PlanningPackage 授权必须精确等于 Decision affected Segment",
         )
     return issues
 
@@ -500,6 +1013,20 @@ def validate_replan_transition(
         raise ProductionContractError(issues)
 
 
+def validate_adapt_transition(
+    previous_plan: ProductionPlan,
+    current_plan: ProductionPlan,
+    planning_package: ProductionPlanningPackage,
+) -> None:
+    issues = evaluate_adapt_transition(
+        previous_plan,
+        current_plan,
+        planning_package,
+    )
+    if issues:
+        raise ProductionContractError(issues)
+
+
 def ready_segment_ids(
     plan: ProductionPlan,
     accepted_segment_ids: Iterable[str],

+ 99 - 7
production_build_agents/contracts/production_planning_models.py

@@ -13,6 +13,7 @@ from .production_execution_models import (
 )
 from .production_models import (
     ArtifactInputBinding,
+    MAX_PRODUCTION_PLAN_REVISIONS,
     MAX_PRODUCTION_PLAN_VERSION,
     PlannedShot,
     ProductionInputId,
@@ -23,7 +24,8 @@ from .production_models import (
 )
 
 
-ProductionPlanningMode = Literal["INITIAL", "REPLAN"]
+ProductionPlanningMode = Literal["INITIAL", "ADAPT", "REPLAN"]
+ProductionProgressAction = Literal["CONTINUE", "ADAPT", "REPLAN", "STOP"]
 
 
 class ProductionPlanningPackage(BaseModel):
@@ -31,7 +33,7 @@ class ProductionPlanningPackage(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     mode: ProductionPlanningMode
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
@@ -46,8 +48,9 @@ class ProductionPlanningPackage(BaseModel):
     shared_visual_anchor: SharedVisualAnchor
     previous_plan_ref: Optional[ImmutableJsonRef] = None
     failure_report_ref: Optional[ImmutableJsonRef] = None
+    progress_decision_ref: Optional[ImmutableJsonRef] = None
     accepted_segments: List[AcceptedSegmentRef] = Field(default_factory=list)
-    authorized_regenerate_segment_ids: List[SegmentId] = Field(
+    authorized_revision_segment_ids: List[SegmentId] = Field(
         default_factory=list
     )
 
@@ -58,10 +61,10 @@ class ProductionPlanningPackage(BaseModel):
             raise ValueError(
                 "ProductionPlanningPackage.accepted_segments 不能重复"
             )
-        authorized = self.authorized_regenerate_segment_ids
+        authorized = self.authorized_revision_segment_ids
         if len(authorized) != len(set(authorized)):
             raise ValueError(
-                "authorized_regenerate_segment_ids 不能重复"
+                "authorized_revision_segment_ids 不能重复"
             )
         if self.mode == "INITIAL":
             if self.target_plan_version != 1:
@@ -69,10 +72,27 @@ class ProductionPlanningPackage(BaseModel):
             if (
                 self.previous_plan_ref is not None
                 or self.failure_report_ref is not None
+                or self.progress_decision_ref is not None
                 or self.accepted_segments
                 or authorized
             ):
-                raise ValueError("INITIAL 不得携带任何 Replan 输入")
+                raise ValueError("INITIAL 不得携带任何 Plan revision 输入")
+        elif self.mode == "ADAPT":
+            if self.target_plan_version < 2:
+                raise ValueError("ADAPT 的 target_plan_version 必须至少为 2")
+            if self.previous_plan_ref is None:
+                raise ValueError("ADAPT 必须引用上一版 ProductionPlan")
+            if self.progress_decision_ref is None:
+                raise ValueError("ADAPT 必须引用正式 ProgressDecision")
+            if self.failure_report_ref is not None:
+                raise ValueError("ADAPT 不得引用失败 Report")
+            if not self.accepted_segments:
+                raise ValueError("ADAPT 必须携带当前 Accepted Segment 快照")
+            if not authorized:
+                raise ValueError("ADAPT 必须声明代码授权的调整 Segment")
+            accepted_set = set(accepted_ids)
+            if accepted_set.intersection(authorized):
+                raise ValueError("ADAPT 不得授权修改 Accepted Segment")
         else:
             if self.target_plan_version < 2:
                 raise ValueError("REPLAN 的 target_plan_version 必须至少为 2")
@@ -85,6 +105,78 @@ class ProductionPlanningPackage(BaseModel):
         return self
 
 
+class ProductionProgressPackage(BaseModel):
+    """一次正式 Segment 验收后交给 Planner 快速复核的封印输入。"""
+
+    model_config = ConfigDict(extra="forbid", strict=True)
+
+    schema_version: ProductionSchemaVersion = "0.7"
+    run_id: str = Field(min_length=1)
+    plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
+    plan_version: int = Field(ge=1, le=MAX_PRODUCTION_PLAN_VERSION)
+    segment_id: SegmentId
+    planning_package_ref: ImmutableJsonRef
+    production_plan_ref: ImmutableJsonRef
+    segment_package_ref: ImmutableJsonRef
+    segment_delivery_ref: ImmutableJsonRef
+    segment_validation_report_ref: ImmutableJsonRef
+    accepted_segments: List[AcceptedSegmentRef] = Field(default_factory=list)
+    remaining_plan_revisions: int = Field(
+        ge=0,
+        le=MAX_PRODUCTION_PLAN_REVISIONS,
+    )
+
+    @model_validator(mode="after")
+    def validate_accepted_snapshot(self) -> ProductionProgressPackage:
+        segment_ids = [item.segment_id for item in self.accepted_segments]
+        if len(segment_ids) != len(set(segment_ids)):
+            raise ValueError(
+                "ProductionProgressPackage.accepted_segments 不能重复"
+            )
+        for accepted in self.accepted_segments:
+            if accepted.source_plan_id != self.plan_id:
+                raise ValueError(
+                    "AcceptedSegmentRef 必须属于当前 plan_id"
+                )
+            if accepted.source_plan_version > self.plan_version:
+                raise ValueError(
+                    "AcceptedSegmentRef 不得来自未来 Plan version"
+                )
+        return self
+
+
+class ProductionProgressDecision(BaseModel):
+    """Production Planner 对一个正式 Segment Report 的最小路由决定。"""
+
+    model_config = ConfigDict(extra="forbid", strict=True)
+
+    schema_version: ProductionSchemaVersion = "0.7"
+    run_id: str = Field(min_length=1)
+    plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
+    plan_version: int = Field(ge=1, le=MAX_PRODUCTION_PLAN_VERSION)
+    segment_id: SegmentId
+    progress_package_ref: ImmutableJsonRef
+    action: ProductionProgressAction
+    affected_segment_ids: List[SegmentId] = Field(default_factory=list)
+    reason: str = Field(min_length=1)
+
+    @model_validator(mode="after")
+    def validate_action_shape(self) -> ProductionProgressDecision:
+        if len(self.affected_segment_ids) != len(
+            set(self.affected_segment_ids)
+        ):
+            raise ValueError(
+                "ProductionProgressDecision.affected_segment_ids 不能重复"
+            )
+        if self.action == "ADAPT" and not self.affected_segment_ids:
+            raise ValueError("ADAPT 必须声明 affected_segment_ids")
+        if self.action != "ADAPT" and self.affected_segment_ids:
+            raise ValueError(
+                f"{self.action} 不得声明 affected_segment_ids"
+            )
+        return self
+
+
 class PlannedSegment(BaseModel):
     """Planner 生成的一个 Segment 节点及其显式数据依赖。"""
 
@@ -122,7 +214,7 @@ class ProductionPlan(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1, le=MAX_PRODUCTION_PLAN_VERSION)
     planning_package_ref: ImmutableJsonRef

+ 5 - 5
production_build_agents/contracts/production_validation_models.py

@@ -90,7 +90,7 @@ class ProductionMediaInspection(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1)
@@ -138,7 +138,7 @@ class ProductionReadinessReport(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1)
@@ -190,7 +190,7 @@ class ProductionValidatorCandidate(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1)
@@ -223,7 +223,7 @@ class ProductionValidationReport(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     plan_version: int = Field(ge=1)
@@ -240,7 +240,7 @@ class ProductionDelivery(BaseModel):
 
     model_config = ConfigDict(extra="forbid", strict=True)
 
-    schema_version: ProductionSchemaVersion = "0.6"
+    schema_version: ProductionSchemaVersion = "0.7"
     run_id: str = Field(min_length=1)
     plan_id: str = Field(pattern=r"^[a-zA-Z][a-zA-Z0-9-]*$")
     final_plan_version: int = Field(ge=1)

+ 3 - 3
tests/contracts/test_production_assembly.py

@@ -89,14 +89,14 @@ class ProductionAssemblyContractTest(unittest.TestCase):
             ],
             timeline=[item.timeline for item in base_packages],
             regenerate_segment_ids=[],
-            summary="0.6 DAG",
+            summary="0.7 DAG",
         )
         self.production_package = materialize_production_package(
             self.planning,
             self.plan,
             planning_package_ref=self.planning_ref,
             production_plan_ref=self.plan_ref,
-            summary="0.6 公共执行输入",
+            summary="0.7 公共执行输入",
         )
         self.packages = [
             SegmentPackage(
@@ -164,7 +164,7 @@ class ProductionAssemblyContractTest(unittest.TestCase):
     def test_reuses_existing_assembly_contract_without_data_loss(
         self,
     ) -> None:
-        self.assertEqual(self.assembly.schema_version, "0.6")
+        self.assertEqual(self.assembly.schema_version, "0.7")
         self.assertEqual(
             evaluate_production_assembly_package(
                 self.planning,

+ 1 - 2
tests/contracts/test_production_evaluation.py

@@ -15,7 +15,6 @@ from production_build_agents.contracts.production_execution_models import (
     SegmentPackage,
 )
 from production_build_agents.contracts.production_models import (
-    ArtifactLineage,
     ProductionInput,
     SegmentDelivery,
     SegmentExecutorCandidate,
@@ -51,7 +50,7 @@ class ProductionSegmentOutputContractTest(unittest.TestCase):
             self.report,
         )
         self.assertTrue(
-            all(record.schema_version == "0.6" for record in records)
+            all(record.schema_version == "0.7" for record in records)
         )
         for model, record in (
             (SegmentPackage, self.package),

+ 3 - 3
tests/contracts/test_production_planning.py

@@ -304,7 +304,7 @@ class ProductionPlanningContractTest(unittest.TestCase):
             previous_plan_ref=_ref("/production/plans/v1.json"),
             failure_report_ref=_ref("/production/reports/v1.json"),
             accepted_segments=[accepted],
-            authorized_regenerate_segment_ids=authorized or ["Segment1"],
+            authorized_revision_segment_ids=authorized or ["Segment1"],
         )
 
     def _codes(self, issues) -> set[str]:
@@ -328,7 +328,7 @@ class ProductionPlanningContractTest(unittest.TestCase):
         )
 
     def test_current_plan_is_strict_and_rejects_old_schema(self) -> None:
-        self.assertEqual(self.plan.schema_version, "0.6")
+        self.assertEqual(self.plan.schema_version, "0.7")
         with self.assertRaises(ValidationError):
             ProductionPlan.model_validate(
                 {
@@ -1564,7 +1564,7 @@ class ProductionPlanningContractTest(unittest.TestCase):
             )
             self.assertEqual(
                 json.loads(planning_path.read_text())["schema_version"],
-                "0.6",
+                "0.7",
             )
             self.assertEqual(
                 json.loads(plan_path.read_text())["plan_version"],

+ 319 - 0
tests/contracts/test_production_progress.py

@@ -0,0 +1,319 @@
+from __future__ import annotations
+
+import unittest
+
+from pydantic import ValidationError
+
+from production_build_agents.contracts.production_planning_evaluation import (
+    evaluate_adapt_planning_package,
+    evaluate_adapt_transition,
+    evaluate_production_progress_decision,
+    evaluate_production_progress_package,
+)
+from production_build_agents.contracts.production_planning_models import (
+    ProductionPlanningPackage,
+    ProductionProgressDecision,
+    ProductionProgressPackage,
+)
+from tests.support.production_fixtures import (
+    immutable_ref,
+    output_profile,
+    production_plan,
+    segment_bundle,
+    shared_anchor,
+)
+
+
+class ProductionProgressContractTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.segment1 = segment_bundle(1)
+        self.segment2 = segment_bundle(2)
+        self.plan = production_plan(
+            [self.segment1[0], self.segment2[0]]
+        )
+        self.planning = ProductionPlanningPackage(
+            run_id="production-run",
+            mode="INITIAL",
+            plan_id=self.plan.plan_id,
+            target_plan_version=1,
+            production_brief_ref=immutable_ref("brief"),
+            global_data_delivery_ref=immutable_ref("global-delivery"),
+            production_input_catalog_ref=immutable_ref("catalog"),
+            output_profile=output_profile(),
+            shared_visual_anchor=shared_anchor(),
+        )
+        segment_package, _, delivery, _, report, accepted = self.segment1
+        self.segment_package_ref = accepted.segment_package_ref.model_copy(
+            update={
+                "uri": delivery.segment_package_uri,
+                "document_sha256": delivery.segment_package_sha256,
+            }
+        )
+        self.delivery_ref = accepted.segment_delivery_ref
+        self.report_ref = accepted.validation_report_ref
+        self.accepted = accepted.model_copy(
+            update={"segment_package_ref": self.segment_package_ref}
+        )
+        self.progress_ref = immutable_ref("progress-Segment1-v1")
+        self.progress = ProductionProgressPackage(
+            run_id="production-run",
+            plan_id=self.plan.plan_id,
+            plan_version=1,
+            segment_id="Segment1",
+            planning_package_ref=self.plan.planning_package_ref,
+            production_plan_ref=immutable_ref("production-plan"),
+            segment_package_ref=self.segment_package_ref,
+            segment_delivery_ref=self.delivery_ref,
+            segment_validation_report_ref=self.report_ref,
+            accepted_segments=[self.accepted],
+            remaining_plan_revisions=5,
+        )
+
+    @staticmethod
+    def _codes(issues) -> set[str]:
+        return {item.code for item in issues}
+
+    def _package_issues(self, package=None):
+        segment_package, _, delivery, _, report, _ = self.segment1
+        return evaluate_production_progress_package(
+            package or self.progress,
+            planning_package=self.planning,
+            planning_package_ref=self.plan.planning_package_ref,
+            production_plan=self.plan,
+            production_plan_ref=immutable_ref("production-plan"),
+            segment_package=segment_package,
+            segment_package_ref=self.segment_package_ref,
+            segment_delivery=delivery,
+            segment_delivery_ref=self.delivery_ref,
+            segment_validation_report=report,
+            segment_validation_report_ref=self.report_ref,
+            accepted_segments=[self.accepted],
+            remaining_plan_revisions=5,
+        )
+
+    def _decision(self, action: str, affected=None):
+        return ProductionProgressDecision(
+            run_id=self.progress.run_id,
+            plan_id=self.progress.plan_id,
+            plan_version=self.progress.plan_version,
+            segment_id=self.progress.segment_id,
+            progress_package_ref=self.progress_ref,
+            action=action,
+            affected_segment_ids=affected or [],
+            reason="正式进度审阅",
+        )
+
+    def _decision_codes(self, decision, *, report=None, package=None):
+        return self._codes(
+            evaluate_production_progress_decision(
+                package or self.progress,
+                decision,
+                progress_package_ref=self.progress_ref,
+                production_plan=self.plan,
+                segment_validation_report=report or self.segment1[4],
+            )
+        )
+
+    def test_progress_package_binds_formal_pass_chain(self) -> None:
+        self.assertEqual(self._package_issues(), [])
+        wrong = self.progress.model_copy(
+            update={"production_plan_ref": immutable_ref("wrong-plan")}
+        )
+        self.assertIn(
+            "production_progress_document_ref_mismatch",
+            self._codes(self._package_issues(wrong)),
+        )
+
+    def test_progress_models_reject_duplicate_and_invalid_action_shapes(
+        self,
+    ) -> None:
+        with self.assertRaises(ValidationError):
+            ProductionProgressPackage(
+                **self.progress.model_dump(exclude={"accepted_segments"}),
+                accepted_segments=[self.accepted, self.accepted],
+            )
+        with self.assertRaises(ValidationError):
+            self._decision("ADAPT")
+        with self.assertRaises(ValidationError):
+            self._decision("CONTINUE", ["Segment2"])
+
+    def test_pass_allows_continue_adapt_stop_but_not_replan(self) -> None:
+        for action in ("CONTINUE", "STOP"):
+            with self.subTest(action=action):
+                self.assertEqual(
+                    self._decision_codes(self._decision(action)),
+                    set(),
+                )
+        self.assertEqual(
+            self._decision_codes(
+                self._decision("ADAPT", ["Segment2"])
+            ),
+            set(),
+        )
+        self.assertIn(
+            "production_progress_action_verdict_mismatch",
+            self._decision_codes(self._decision("REPLAN")),
+        )
+
+    def test_fail_allows_replan_stop_and_rejects_continue(self) -> None:
+        report = self.segment1[4].model_copy(update={"verdict": "FAIL"})
+        failed_progress = self.progress.model_copy(
+            update={"accepted_segments": []}
+        )
+        self.assertEqual(
+            self._decision_codes(
+                self._decision("REPLAN"),
+                report=report,
+                package=failed_progress,
+            ),
+            set(),
+        )
+        self.assertIn(
+            "production_progress_action_verdict_mismatch",
+            self._decision_codes(
+                self._decision("CONTINUE"),
+                report=report,
+                package=failed_progress,
+            ),
+        )
+
+    def test_adapt_requires_pending_segment_and_revision_budget(self) -> None:
+        self.assertIn(
+            "production_progress_adapt_segment_not_pending",
+            self._decision_codes(
+                self._decision("ADAPT", ["Segment1"])
+            ),
+        )
+        exhausted = self.progress.model_copy(
+            update={"remaining_plan_revisions": 0}
+        )
+        self.assertIn(
+            "production_progress_revision_budget_exhausted",
+            self._decision_codes(
+                self._decision("ADAPT", ["Segment2"]),
+                package=exhausted,
+            ),
+        )
+
+    def test_adapt_package_and_transition_are_future_only(self) -> None:
+        decision_ref = immutable_ref("adapt-decision")
+        package = ProductionPlanningPackage(
+            **self.planning.model_dump(
+                exclude={
+                    "mode",
+                    "target_plan_version",
+                    "previous_plan_ref",
+                    "failure_report_ref",
+                    "progress_decision_ref",
+                    "accepted_segments",
+                    "authorized_revision_segment_ids",
+                }
+            ),
+            mode="ADAPT",
+            target_plan_version=2,
+            previous_plan_ref=immutable_ref("production-plan"),
+            progress_decision_ref=decision_ref,
+            accepted_segments=[self.accepted],
+            authorized_revision_segment_ids=["Segment2"],
+        )
+        changed_segment = self.plan.segments[1].model_copy(
+            update={"objective": "根据最新进度调整未来段落"}
+        )
+        current = self.plan.model_copy(
+            update={
+                "plan_version": 2,
+                "planning_package_ref": immutable_ref("planning-v2"),
+                "segments": [self.plan.segments[0], changed_segment],
+                "summary": "未来规划已调整",
+            }
+        )
+        decision = self._decision("ADAPT", ["Segment2"])
+        self.assertEqual(
+            evaluate_adapt_planning_package(
+                self.planning,
+                package,
+                progress_package=self.progress,
+                progress_package_ref=self.progress_ref,
+                progress_decision=decision,
+                progress_decision_ref=decision_ref,
+            ),
+            [],
+        )
+        widened = package.model_copy(
+            update={
+                "authorized_revision_segment_ids": [
+                    "Segment1",
+                    "Segment2",
+                ]
+            }
+        )
+        self.assertIn(
+            "adapt_authorized_segments_mismatch",
+            self._codes(
+                evaluate_adapt_planning_package(
+                    self.planning,
+                    widened,
+                    progress_package=self.progress,
+                    progress_package_ref=self.progress_ref,
+                    progress_decision=decision,
+                    progress_decision_ref=decision_ref,
+                )
+            ),
+        )
+        stale_snapshot = package.model_copy(
+            update={"accepted_segments": []}
+        )
+        self.assertIn(
+            "adapt_accepted_snapshot_mismatch",
+            self._codes(
+                evaluate_adapt_planning_package(
+                    self.planning,
+                    stale_snapshot,
+                    progress_package=self.progress,
+                    progress_package_ref=self.progress_ref,
+                    progress_decision=decision,
+                    progress_decision_ref=decision_ref,
+                )
+            ),
+        )
+        self.assertEqual(
+            evaluate_adapt_transition(self.plan, current, package),
+            [],
+        )
+        no_effect = current.model_copy(
+            update={"segments": self.plan.segments}
+        )
+        self.assertIn(
+            "adapt_has_no_effect",
+            self._codes(
+                evaluate_adapt_transition(
+                    self.plan,
+                    no_effect,
+                    package,
+                )
+            ),
+        )
+        accepted_changed = current.model_copy(
+            update={
+                "segments": [
+                    self.plan.segments[0].model_copy(
+                        update={"objective": "越权修改"}
+                    ),
+                    changed_segment,
+                ]
+            }
+        )
+        self.assertIn(
+            "adapt_protected_segment_changed",
+            self._codes(
+                evaluate_adapt_transition(
+                    self.plan,
+                    accepted_changed,
+                    package,
+                )
+            ),
+        )
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 2 - 2
tests/contracts/test_production_validation.py

@@ -151,10 +151,10 @@ class ProductionValidationContractTest(unittest.TestCase):
         readiness_payload = readiness.model_dump(mode="json")
         delivery_payload = delivery.model_dump(mode="json")
 
-        self.assertEqual(readiness_payload["schema_version"], "0.6")
+        self.assertEqual(readiness_payload["schema_version"], "0.7")
         self.assertIn("coverage_evaluation", readiness_payload)
         self.assertNotIn("production_coverage", readiness_payload)
-        self.assertEqual(delivery_payload["schema_version"], "0.6")
+        self.assertEqual(delivery_payload["schema_version"], "0.7")
         self.assertIn("preflight_report_ref", delivery_payload)
         self.assertNotIn("readiness_report_ref", delivery_payload)
 

+ 1 - 1
tests/production/test_validator.py

@@ -136,7 +136,7 @@ class ProductionValidatorRuntimeTest(unittest.TestCase):
             ],
             timeline=self.plan.timeline,
             regenerate_segment_ids=[],
-            summary="0.6 DAG",
+            summary="0.7 DAG",
         )
 
         @contextmanager

+ 1 - 2
tests/support/production_fixtures.py

@@ -25,7 +25,6 @@ from production_build_agents.contracts.production_assembly_models import (
     ProductionArtifactGraph,
     ProductionArtifactLineage,
     ProductionAssemblyDelivery,
-    ProductionAssemblyPackage,
 )
 from production_build_agents.contracts.production_execution_models import (
     AcceptedSegmentRef,
@@ -757,7 +756,7 @@ def final_delivery(
 
 
 class ProductionScenario:
-    """完整合法的当前 Production 0.6 离线测试场景。"""
+    """完整合法的当前 Production 0.7 离线测试场景。"""
 
     def __init__(self) -> None:
         bundles = [