> ## Documentation Index
> Fetch the complete documentation index at: https://powercontext.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Handoff 闭环

> 做一次真正的交接。准备就会写 Source。提交才留下里程碑。

这一页做一次真正的交接。发送方记下委托边界和当前状态，接收方核对精确 Handoff (把当前目标、已验证进度、阻塞项和下一步交给接手者。不提交就还是临时包。)，最后把结果链回这次交接。

Handoff 这里不需要生成模型。示例用的是调用方已经核对过的类型化数据，最小 Server 就能跑。

![一次真正的交接：工作契约（Work Contract）、准备 Handoff、提交、精确确认，再记下 Task Outcome。](https://mintcdn.com/powercontext/1t_ajifOEOG5YQjP/assets/basic-handoff-loop-zh.svg?fit=max\&auto=format\&n=1t_ajifOEOG5YQjP\&q=85\&s=4eb6419992db13769f1a936dfcf7d1ec)

> [!NOTE]
>
> 工作契约（Work Contract）字段和 Handoff 接口以 [官方文档](https://oceanbase.github.io/powercontext/) 的最新发布版本为准。这页帮你把一次交接跑完，并看清哪一步会写 Source。

## 闭环长什么样

工作契约、Handoff 边界、确认回执和 Task Outcome 都会留下 Source 回执。已提交 Handoff 另有不可变 Artifact Revision。准备就会写边界 Source，所以它不是只读预览。

## 完整 Python 示例

新建 `handoff_loop.py`：

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import asyncio
import os
from uuid import uuid4

from powercontext.client import PowerContextClient
from powercontext.http import (
    AcknowledgeHandoffRequest,
    CommitHandoffRequest,
    ContinueHandoffRequest,
    CreateWorkContractRequest,
    HandoffCurrentWorkRequest,
    RecordTaskOutcomeRequest,
)


SERVER_URL = os.getenv(
    "POWERCONTEXT_CLIENT_SERVER_URL",
    "http://127.0.0.1:8000",
)
API_TOKEN = os.getenv("POWERCONTEXT_CLIENT_API_TOKEN")


async def main() -> None:
    run_id = uuid4().hex
    scope_id = f"project:powercontext-101:{run_id}"

    async with PowerContextClient(
        SERVER_URL,
        token=API_TOKEN,
        timeout=10.0,
    ) as client:
        contract = await client.create_work_contract(
            CreateWorkContractRequest.model_validate({
                "scope_id": scope_id,
                "source_id": f"contract-{run_id}",
                "contract": {
                    "schema": "powercontext.work-contract.v1",
                    "trust": "untrusted_input",
                    "objective": "Complete the OpenAPI client update.",
                    "facts": [{
                        "text": "The repository has contract tests.",
                        "basis": "declared",
                        "evidence": [],
                    }],
                    "in_scope": ["Regenerate the checked-in client."],
                    "exclusions": ["Do not deploy to production."],
                    "completion_criteria": ["Contract tests pass."],
                    "authorization_notes": [
                        "This contract does not grant deployment authority."
                    ],
                    "open_questions": [],
                },
            })
        )
        print("contract receipt:", contract.source, contract.position)

        prepared_work = await client.handoff_current_work(
            HandoffCurrentWorkRequest.model_validate({
                "scope_id": scope_id,
                "source_id": f"boundary-{run_id}",
                "handoff": {
                    "schema": "powercontext.current-work-handoff.v1",
                    "trust": "untrusted_input",
                    "objective": "Complete the OpenAPI client update.",
                    "state": [{
                        "text": "The checked-in client was regenerated.",
                        "basis": "declared",
                        "evidence": [],
                    }],
                    "disposition": "continuable",
                    "next_action": {
                        "text": "Run contract tests.",
                        "basis": "declared",
                        "evidence": [],
                    },
                    "omissions": ["Contract tests have not run yet."],
                },
            })
        )
        print("boundary receipt:", prepared_work.boundary.source)
        print("prepared base:", prepared_work.handoff.base)

        temporary = await client.continue_handoff(
            ContinueHandoffRequest(
                scope_id=scope_id,
                selection="prepared",
                prepared=prepared_work.handoff,
            )
        )
        assert temporary.status == "resolved"
        assert temporary.selection == "prepared"
        assert temporary.selected_revision is None

        committed = await client.commit_handoff(
            CommitHandoffRequest(
                scope_id=scope_id,
                handoff=prepared_work.handoff,
            )
        )
        print("committed revision:", committed.reference)

        latest = await client.continue_handoff(
            ContinueHandoffRequest(
                scope_id=scope_id,
                selection="latest",
            )
        )
        assert latest.selected_revision == committed.reference
        assert latest.current_revision == committed.reference

        exact = await client.continue_handoff(
            ContinueHandoffRequest(
                scope_id=scope_id,
                selection="exact",
                revision=latest.selected_revision,
            )
        )
        assert exact.selection == "exact"
        assert all(
            check.status == "available"
            for check in exact.evidence_checks
        )

        acknowledgement = await client.acknowledge_handoff(
            AcknowledgeHandoffRequest.model_validate({
                "scope_id": scope_id,
                "source_id": f"receipt-{run_id}",
                "receiver": "contract-test-agent",
                "status": "accepted",
                "selection": "exact",
                "receiver_checks": {
                    "live_state": "confirmed",
                    "capability": "confirmed",
                    "authorization": "confirmed",
                },
                "revision": committed.reference.model_dump(),
            })
        )
        assert acknowledgement.receipt.kind == "handoff-receipt"
        print("ack receipt:", acknowledgement.receipt.source)

        outcome = await client.record_task_outcome(
            RecordTaskOutcomeRequest.model_validate({
                "scope_id": scope_id,
                "source_id": f"outcome-{run_id}",
                "outcome": {
                    "schema": "powercontext.task-outcome.v1",
                    "trust": "untrusted_observation",
                    "objective": "Complete the OpenAPI client update.",
                    "status": "succeeded",
                    "summary": "Contract tests passed.",
                    "handoff_receipt_ref": (
                        acknowledgement.receipt.source.model_dump()
                    ),
                    "observations": [{
                        "text": "The contract tests completed successfully.",
                        "basis": "declared",
                        "evidence": [],
                    }],
                    "checks": [{
                        "name": "Contract tests",
                        "status": "passed",
                        "details": "The command exited successfully.",
                        "basis": "declared",
                        "evidence": [],
                    }],
                    "produced_artifacts": [],
                    "remaining_work": [],
                },
            })
        )
        assert outcome.kind == "task-outcome"
        print("outcome receipt:", outcome.source)


if __name__ == "__main__":
    asyncio.run(main())
```

运行：

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
uv run python handoff_loop.py
```

这里用 `model_validate` 构造嵌套请求，因为 JSON 契约中的字段名是 `schema`，而生成的 Python 模型内部属性使用别名。

## 每一步的边界

### 工作契约是委托基线

它记录目标、范围内的工作、排除项、完成标准、授权备注和关键问题。

契约是 `untrusted_input` Source，不会扩大当前授权。`basis="verified"` 的声明必须带精确证据；`basis="declared"` 则不能带证据。

### `handoff_current_work` 会写边界 Source

它返回：

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
PreparedWorkHandoff
├── boundary: WorkSourceReceipt
└── handoff: PreparedHandoff
```

后续提交/继续传 `prepared_work.handoff`，不是整个响应。

这一步没有创建 Handoff Revision，但已经持久化边界 Source。因此，它不是只读预览。

### 已准备交接不需要提交

示例先 `continue_handoff(selection="prepared")`，证明临时交接可用。此时 `selected_revision` 是 `None`。

只有需要持久里程碑时才调用 `commit_handoff`。

### `latest` 用于发现，`exact` 用于确认

`latest` 返回当前头版本，并把它放在 `selected_revision` 和 `current_revision`。接收方检查后，应把确认绑定到精确 Revision。

`acknowledge_handoff` 不接受 `selection="latest"`。

### 接受有硬性条件

只有下面三项全部已确认，且证据都可用，才能写 `accepted`：

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
live_state
capability
authorization
```

无法确认时使用 `needs_clarification` 或 `declined`，并提供消息。

确认只是接收记录，不授予权限。

### 结果要链接接受精确回执

`handoff_receipt_ref` 使用：

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
acknowledgement.receipt.source
```

只有状态接受、选择方式精确、已选择 Revision 指向已提交 Handoff 的回执，才能声明该持久 Handoff 的结果被覆盖。

已准备确认回执不能用于这个 linkage。

## 结果与 Check 状态

整体 `Task Outcome.status` 可选：

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
succeeded
partial
blocked
failed
cancelled
unknown
```

单项 `TaskCheck.status` 可选：

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
passed
failed
skipped
timed_out
unavailable
cancelled
unknown
```

不要因为一个检查 passed 就把整体结果写成 succeeded。没运行的检查也不能写 passed。

## 负向实验

### 不提交，读取最新版本

在提交前调用 `continue_handoff(selection="latest")`，应得到 `status="empty"`。已准备 Handoff 仍可通过 `selection="prepared"` 使用。

### 接受但授权信息未确认

把授权信息改成 `not_checked`，操作应拒绝。改用 `needs_clarification` 并说明缺少什么。

### 用已准备回执记录已提交 outcome

先确认已准备，再把它作为 `handoff_receipt_ref`。Server 应拒绝，因为回执没有绑定已提交精确 Revision。

### 两个已准备 Handoff 共享基础

先准备两个 Handoff，提交第一个，再提交第二个。第二个应发生 Revision 冲突。读取新头版本后重新准备，不要猜 Revision。

### 边界成功，提交失败

此时边界 Source 已存在，但没有新的 Handoff Revision。向用户报告这个实际状态，不能说“什么都没写”，也不能说“里程碑已保存”。

## 怎么知道跑通了

* 工作契约回执已经保存。
* 已准备 Handoff 可以先临时继续。
* 提交返回精确 Handoff Revision。
* 用最新版本找到之后，确认绑到精确 Revision。
* 接受前核对证据、实时状态、能力和授权。
* Task Outcome 链到确认回执的 SourceRef。
* 整体结果和单项检查状态没有混用。

对不上时，先对[排错](https://powercontext.mintlify.site/zh/start/troubleshooting.md)。

> [!TIP]
>
> 自动帮你找回上下文失败时，当前任务还应继续。你明确要求写入 Memory、提交 Handoff、批准或导出时，失败必须能看见。完整说明见 [fail-open](https://powercontext.mintlify.site/zh/mental-model/fail-open.md)。

## 下一步

> [card] [Experience、Skill 与 Review](https://powercontext.mintlify.site/zh/basic-usage/experience-skill-review.md)
>
> Candidate 先进入 Review。Skill 批准后还要显式导出。

> [card] [卡住时先对排错](https://powercontext.mintlify.site/zh/start/troubleshooting.md)
>
> 只要预览却调用了 Handoff，先打开排错。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.