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

# 04-完成第一个任务

> 用一个可运行的待办清单小项目，完整练习需求、启动、只读探索、提示词、审批、修改、测试、失败修复、验收与回滚。

## 本页目标

本页不把“完成任务”理解成让 Codex 生成一段代码。

真正的完成，是从需求开始，到可运行结果结束的一个闭环。

这个闭环包含以下动作：

1. 明确一个足够小、可以运行的项目。
2. 在正确目录启动 Codex。
3. 先让 Codex 只读探索，不急着修改。
4. 用提示词四件套说清目标、范围、约束和验证。
5. 查看即将执行的命令，并处理审批请求。
6. 审查工作区中的 diff。
7. 运行测试和手工验收。
8. 故意制造一次失败，练习定位和修复。
9. 解释结果、记录证据，并保留回滚路径。

本页使用一个纯 Python 的命令行待办清单项目。

项目只有一个运行时依赖：Python 标准库。

这样做的好处是：不需要联网安装包，改动范围小，命令可以复制运行，结果也容易核对。

> 本页示例中的界面文字、审批按钮和 Codex 默认策略可能随版本变化。命令行为以本机 `codex --help`、子命令帮助和当前官方文档为准。

## 贯穿案例

我们要做一个名为 `todo-codex-demo` 的小项目。

它的第一版需求如下：

* 用户可以添加一条待办事项。
* 用户可以列出当前待办事项。
* 用户可以把待办事项标记为已完成。
* 数据暂时保存在本地 JSON 文件中。
* 不引入第三方依赖。
* 命令行错误要给出清晰提示。
* 修改后必须有自动化测试。

这是一份有意保持克制的需求。

它没有要求数据库、用户登录、网络 API、彩色终端或复杂的目录结构。

第一次任务的重点是跑通协作流程，不是把练习项目做成生产系统。

## 开始前的安全边界

先给这次练习定出明确的非目标。

以下内容不在本次任务范围内：

* 不访问生产环境。
* 不读取真实客户数据。
* 不上传 `.env`、令牌或私钥。
* 不修改工作区之外的文件。
* 不安装依赖。
* 不提交或推送到远端。
* 不删除已有用户文件。
* 不进行大规模重构。

如果你的当前目录不是一个练习目录，请先停止。

不要把第一次任务直接放在包含业务代码的正式仓库里练习。

如果必须使用已有仓库，请先创建独立分支，或者复制到临时目录。

## 第一步：创建可运行项目

打开终端，先确认 Python 和 Git 可用。

```bash theme={null}
python --version
git --version
```

预期输出类似下面的内容，版本号可以不同：

```text theme={null}
Python 3.12.4
git version 2.45.2
```

如果你的系统使用 `python3`，将后文的 `python` 替换为 `python3`。

在练习目录中创建项目：

```bash theme={null}
mkdir todo-codex-demo
cd todo-codex-demo
```

预期结果是终端当前路径已经进入 `todo-codex-demo`。

用 PowerShell 创建初始文件时，可以使用下面的命令：

```powershell theme={null}
@'
import json
from pathlib import Path

DATA_FILE = Path("todos.json")


def load_todos():
    if not DATA_FILE.exists():
        return []
    return json.loads(DATA_FILE.read_text(encoding="utf-8"))


def save_todos(todos):
    DATA_FILE.write_text(
        json.dumps(todos, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )


def add_todo(title):
    todos = load_todos()
    todo = {"id": len(todos) + 1, "title": title, "done": False}
    todos.append(todo)
    save_todos(todos)
    return todo


def list_todos():
    return load_todos()


if __name__ == "__main__":
    print("待办清单项目已启动")
'@ | Set-Content -Encoding utf8 todo.py
```

如果你使用 macOS 或 Linux，可以用编辑器新建 `todo.py`，填入同样的内容。

先直接运行它：

```bash theme={null}
python todo.py
```

预期输出：

```text theme={null}
待办清单项目已启动
```

这一步很重要。

在让 Codex 修改以前，先证明基线能够运行。

现在初始化 Git，并保存一个回滚点：

```bash theme={null}
git init
git add todo.py
git commit -m "建立待办清单练习项目"
```

预期输出中应包含一次新的提交，例如：

```text theme={null}
[main (root-commit) 1a2b3c4] 建立待办清单练习项目
 1 file changed, 38 insertions(+)
```

检查工作区：

```bash theme={null}
git status --short --branch
```

预期至少能看到分支名称，且没有未提交文件：

```text theme={null}
## master
```

有些 Git 版本显示 `## main`，这同样正常。

## 第二步：先做只读探索

不要一上来就说“帮我把它做完”。

先让 Codex 只读理解当前项目。

在项目目录启动：

```bash theme={null}
codex
```

预期是进入交互式 Codex 会话。

如果提示登录，先完成登录；不要把 API key 直接写进提示词。

进入会话后，先查看当前权限状态：

```text theme={null}
/permissions
```

如果当前界面支持只读模式，将权限切换到 `read-only` 或 `Read Only`。

不同版本的菜单名称可能不同，请以屏幕提示为准。

只读模式的目的，是让探索阶段不能写入文件。

然后输入第一条 Codex 消息：

```text theme={null}
请只读探索当前项目，不要修改、创建或删除任何文件。
请读取 todo.py，并回答：
1. 当前已经实现了哪些能力？
2. 数据保存在哪里，数据结构是什么？
3. 要支持 add、list、done 三个命令，最小改动可能涉及哪些位置？
4. 现有实现有哪些需要先验证的假设？
请同时列出你实际读取的文件和准备运行的命令。
```

预期回答应提到：

* 当前已有 `load_todos`、`save_todos`、`add_todo` 和 `list_todos`。
* 数据保存在当前目录的 `todos.json`。
* 每条数据至少有 `id`、`title` 和 `done` 字段。
* 当前 `__main__` 只打印启动信息，没有真正解析命令行参数。
* 可能需要修改 `todo.py`，并新增测试文件。

预期回答不应声称已经实现了 `done` 命令。

也不应声称已经运行了一个并不存在的测试套件。

如果它的回答与文件内容不一致，先不要进入修改阶段。

继续用只读提示词核对入口：

```text theme={null}
继续只读，不要编辑文件。
请说明从执行 `python todo.py` 到程序输出的调用路径。
请指出当前代码中没有被测试覆盖的行为。
```

预期结果是它能指出模块入口位于 `if __name__ == "__main__":` 块。

它还应指出当前没有测试目录或测试文件。

这一步就是“只读探索”的验收。

只读探索通过的标准不是回答写得长，而是回答能被文件内容核对。

如果 Codex 提议运行命令，可以先观察命令是否只读。

例如 `pwd`、`ls`、`dir`、`python --version` 通常属于低风险检查。

遇到 `rm`、网络请求、安装依赖或访问工作区之外路径的命令，不要直接批准。

## 第三步：写好提示词四件套

接下来把需求写成四件套。

四件套分别是：目标、范围、约束、验证。

### 目标

目标回答“最后要得到什么”。

本案例的目标是：

> 把当前脚本变成一个可运行的待办命令行工具，支持 `add`、`list` 和 `done`。

### 范围

范围回答“允许改哪些文件和函数”。

本案例的范围是：

* 允许修改 `todo.py`。
* 允许新增 `test_todo.py`。
* 不修改 Git 配置和用户目录。
* 不修改其他项目文件。

### 约束

约束回答“不能用什么方式完成”。

本案例的约束是：

* 只使用 Python 标准库。
* 保留 JSON 文件存储方式。
* 保留现有 `add_todo`、`list_todos` 的兼容行为。
* 不删除已有数据。
* 不提交、不推送。
* 先展示计划，再请求修改权限。

### 验证

验证回答“什么证据可以证明完成”。

本案例的验证是：

* `python -m unittest -v` 通过。
* `python todo.py add "阅读文档"` 能添加任务。
* `python todo.py list` 能列出任务。
* `python todo.py done 1` 能标记任务。
* 对不存在的编号给出非零退出码和清晰错误。
* `git diff --check` 通过。

## 第四步：请求方案，但先不修改

在同一个 Codex 会话中输入下面的完整提示词。

```text theme={null}
现在只做分析和计划，先不要修改任何文件，也不要运行会写入文件的命令。

目标：把当前的 todo.py 变成可运行的命令行待办工具，支持：
- python todo.py add "阅读文档"
- python todo.py list
- python todo.py done 1

范围：
- 允许修改 todo.py。
- 允许新增 test_todo.py。
- 不修改其他文件、Git 配置或工作区之外的路径。

约束：
- 只使用 Python 标准库，不安装第三方依赖。
- 保留 JSON 文件存储。
- 保留现有 add_todo 和 list_todos 的基本行为。
- 不删除已有 todos.json 数据。
- 不提交、不推送。

验证：
- 运行 python -m unittest -v。
- 手工运行 add、list、done 三个命令。
- 验证不存在的任务编号会给出清晰错误和非零退出码。
- 最后运行 git diff --check。

请先读取 todo.py，检查是否存在已有测试，然后输出：
1. 你理解的需求；
2. 准备修改或新增的文件；
3. 分步实现计划；
4. 每一步的验证命令；
5. 可能的风险和回滚办法。
计划输出完后停下，等待我批准。
```

预期输出应包含一个分步方案，而不是立刻改文件。

方案可能类似下面这样：

```text theme={null}
1. 在 todo.py 中增加 argparse 入口和 add/list/done 子命令。
2. 为 done 增加按 id 查找并保存的逻辑。
3. 对非法编号和缺少标题返回清晰错误。
4. 新增 test_todo.py，覆盖空列表、添加、完成和不存在编号。
5. 运行 unittest，再运行三条 CLI 命令和 diff 检查。
```

方案中的文件名必须落在你规定的范围内。

如果它计划修改 `README.md`、安装依赖或访问网络，先提出限制：

```text theme={null}
计划超出范围。请只修改 todo.py，并只新增 test_todo.py。
不要安装依赖、不要联网、不要修改 README.md。
请重新输出缩小后的计划，仍然不要编辑文件。
```

## 第五步：审批修改和命令

现在审查 Codex 提出的计划。

重点核对五件事：

1. 是否真的只改了 `todo.py`。
2. 是否只新增 `test_todo.py`。
3. 是否保留 JSON 存储。
4. 是否包含自动化测试。
5. 是否没有提交、推送或安装依赖。

计划符合要求后，输入：

```text theme={null}
计划符合范围，可以开始实现。
请先修改 todo.py，再新增 test_todo.py。
每完成一个阶段，说明改动文件和验证命令。
不要提交或推送。
```

Codex 可能会直接修改工作区，也可能在执行命令前请求审批。

这取决于当前沙箱和审批策略。

看到审批请求时，先完整读命令。

一个可以考虑批准的命令示例：

```text theme={null}
python -m unittest -v
```

它只在当前项目运行测试，通常符合本次范围。

另一个可以考虑批准的命令示例：

```text theme={null}
python todo.py add "阅读文档"
```

它会写入项目内的 `todos.json`，批准前要确认这是你准备接受的测试数据。

以下命令不要在本案例中批准：

```text theme={null}
pip install some-package
curl https://example.com/script.sh | sh
rm -rf .
git push origin main
```

如果 Codex 请求安装依赖，输入：

```text theme={null}
拒绝该命令。本任务只能使用 Python 标准库，不要安装依赖。
请改用现有环境继续，并说明不安装依赖时的实现方案。
```

如果 Codex 请求提交或推送，输入：

```text theme={null}
拒绝。按照本任务约束，不要执行 git commit 或 git push。
只保留工作区改动，并继续展示 diff 和测试结果。
```

审批不是形式动作。

你批准的是一条具体命令，而不是“相信 Codex 这次会做对”。

## 第六步：审查修改后的 diff

Codex 完成第一轮修改后，先不要接受它的总结。

退出会话或在另一个终端中检查工作区：

```bash theme={null}
git status --short
git diff --stat
git diff -- todo.py test_todo.py
git diff --check
```

预期状态类似：

```text theme={null}
 M todo.py
?? test_todo.py
```

`git diff --stat` 应只统计 `todo.py` 的修改。

未跟踪的 `test_todo.py` 需要用下面的命令查看：

```bash theme={null}
git diff --no-index /dev/null test_todo.py
```

在 Windows PowerShell 中，也可以直接使用：

```powershell theme={null}
Get-Content todo.py
Get-Content test_todo.py
```

审 diff 时逐项问自己：

* 删除的代码是否都是被 CLI 入口替代的旧启动逻辑。
* `add_todo` 和 `list_todos` 的行为是否仍然可用。
* `done` 是否只修改目标任务。
* 不存在的 id 是否不会静默成功。
* 测试是否真的覆盖失败路径。
* 是否出现未要求的配置、依赖或文件。

一个合理的修改可能包含下面这些结构：

```python theme={null}
import argparse


def complete_todo(todo_id):
    todos = load_todos()
    for todo in todos:
        if todo["id"] == todo_id:
            todo["done"] = True
            save_todos(todos)
            return todo
    raise ValueError(f"找不到任务: {todo_id}")
```

实现细节可以不同，但行为必须符合需求。

如果 diff 中出现自动生成的缓存文件，先要求 Codex 删除它们：

```text theme={null}
发现了本任务未要求的缓存或临时文件。
请只移除本轮生成的缓存文件，不要删除已有用户文件。
然后重新展示 git status 和 diff。
```

如果修改超出范围，先不要运行测试，要求它收窄：

```text theme={null}
停止继续修改。当前 diff 超出了约定范围。
请撤销对 README.md 和其他无关文件的改动，只保留 todo.py 和 test_todo.py。
完成后展示完整 diff，不要提交。
```

## 第七步：运行自动化测试

先运行项目要求的测试命令：

```bash theme={null}
python -m unittest -v
```

预期输出类似：

```text theme={null}
test_add_todo (test_todo.TodoTests) ... ok
test_complete_todo (test_todo.TodoTests) ... ok
test_missing_todo (test_todo.TodoTests) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.00s

OK
```

如果输出为 `Ran 0 tests`，不能把它当作通过。

这表示测试发现机制或测试文件命名可能有问题。

输入下面的 Codex 消息：

```text theme={null}
测试命令返回成功但发现测试数量为 0。
请只读检查测试发现原因，不要扩大修改范围。
修复测试发现配置或文件命名后，再运行 python -m unittest -v。
```

如果测试失败，保留完整输出，不要马上要求“全部重写”。

例如失败输出可能是：

```text theme={null}
FAIL: test_complete_todo (test_todo.TodoTests)
AssertionError: False is not true
```

先让 Codex 定位具体原因：

```text theme={null}
测试失败了。请不要猜测，也不要重写整个项目。
先解释 test_complete_todo 失败的直接原因，指出涉及的函数和数据状态。
只提出最小修复方案，等待我确认后再修改。
```

## 第八步：做命令行验收

自动化测试通过后，使用临时数据文件验证真实入口。

先确认项目目录干净到只剩预期改动：

```bash theme={null}
git status --short
```

运行添加命令：

```bash theme={null}
python todo.py add "阅读文档"
```

预期输出类似：

```text theme={null}
已添加任务 1: 阅读文档
```

运行列表命令：

```bash theme={null}
python todo.py list
```

预期输出类似：

```text theme={null}
[ ] 1 阅读文档
```

运行完成命令：

```bash theme={null}
python todo.py done 1
```

预期输出类似：

```text theme={null}
已完成任务 1: 阅读文档
```

再次列出：

```bash theme={null}
python todo.py list
```

预期输出类似：

```text theme={null}
[x] 1 阅读文档
```

测试错误输入：

```bash theme={null}
python todo.py done 999
```

预期应包含清晰错误，例如：

```text theme={null}
错误：找不到任务: 999
```

并且退出码应为非零。

在 Bash 中可以这样检查：

```bash theme={null}
python todo.py done 999
status=$?
printf 'exit code: %s\n' "$status"
```

预期 `exit code` 不是 `0`。

不要把手工生成的 `todos.json` 当成代码提交内容。

如果项目希望保留干净状态，可以在验收后删除练习数据，再检查 diff。

先查看文件是否只包含本次手工数据：

```bash theme={null}
python -c "from pathlib import Path; print(Path('todos.json').read_text(encoding='utf-8'))"
```

确认没有真实数据后，再执行：

```bash theme={null}
rm todos.json
```

Windows PowerShell 使用：

```powershell theme={null}
Remove-Item todos.json
```

如果文件不是本次生成的，不能执行删除命令。

## 第九步：故意制造一次失败并修复

为了练习失败修复，暂时把测试中的预期值改错。

不要直接让 Codex 盲目修复失败。

先运行测试并保存错误输出：

```bash theme={null}
python -m unittest -v 2>&1 | tee test-failure.txt
```

Windows PowerShell 可以使用：

```powershell theme={null}
python -m unittest -v 2>&1 | Tee-Object test-failure.txt
```

预期会看到某个测试失败，以及断言的实际值和期望值。

将失败信息原样贴给 Codex，并说明只修测试或实现中真正的原因：

```text theme={null}
测试失败，请基于下面的完整输出定位原因，不要把失败测试删掉，也不要降低断言强度。

<粘贴 test-failure.txt 的完整内容>

请先说明：
1. 失败发生在哪个测试；
2. 实际值和期望值分别是什么；
3. 这是测试写错、实现写错，还是测试数据污染；
4. 最小修复方案是什么。
先不要修改，等待确认。
```

如果它发现是测试数据污染，可以清理临时 `todos.json`，但必须先说明理由。

如果它发现实现逻辑错误，批准最小代码修复即可。

确认方案后输入：

```text theme={null}
方案正确。只实施最小修复，不删除回归测试。
修复后重新运行 python -m unittest -v，并报告失败是否消失。
```

预期修复过程会再次执行测试。

如果修复后测试通过，仍然要重新查看 diff。

“测试变绿”不能证明没有顺手改了无关文件。

## 第十步：结果解释

完成后要求 Codex 用证据解释结果：

```text theme={null}
请用简短的验收报告总结本次任务，必须包含：
- 修改和新增的文件；
- 每个文件解决了什么问题；
- 执行过的命令及其关键结果；
- 自动化测试数量和结果；
- 手工 CLI 验收结果；
- 当前仍未覆盖的风险；
- 是否执行了提交、推送、联网或安装依赖；
- 如果需要回滚，应使用什么命令。
不要只说“完成了”，每一项都给出可核对证据。
```

一份合格的结果解释类似下面这样：

```text theme={null}
修改文件：todo.py
新增文件：test_todo.py

功能：增加 add、list、done 命令；保留 JSON 存储；对不存在的 id 返回错误。
测试：python -m unittest -v，Ran 4 tests，OK。
手工验收：add、list、done 成功；done 999 返回非零退出码。
检查：git diff --check 通过；未安装依赖，未联网，未提交，未推送。
剩余风险：尚未覆盖并发写入和 JSON 文件损坏恢复。
回滚：确认没有其他未提交改动后，使用 git restore todo.py，并删除本轮新增的 test_todo.py。
```

如果报告与命令输出不一致，以命令输出和实际 diff 为准。

不要因为总结看起来完整，就跳过人工检查。

## 验收清单

逐条执行下面的验收：

* [ ] 当前终端路径是 `todo-codex-demo`。
* [ ] `python todo.py` 可以启动。
* [ ] 初始 Git 提交在 Codex 修改前已经存在。
* [ ] 探索阶段没有修改文件。
* [ ] 提示词明确写出了目标、范围、约束、验证。
* [ ] 计划没有越过文件范围。
* [ ] 所有需要批准的命令都经过人工阅读。
* [ ] diff 只包含 `todo.py` 和 `test_todo.py`。
* [ ] 没有新增第三方依赖。
* [ ] `python -m unittest -v` 通过且测试数量大于零。
* [ ] `add`、`list`、`done` 可以运行。
* [ ] 不存在的任务编号有错误信息和非零退出码。
* [ ] `git diff --check` 通过。
* [ ] 没有密钥、令牌、真实数据或临时错误日志进入提交范围。
* [ ] 没有执行提交和推送。
* [ ] 剩余风险已经记录。

在最终验收时再次执行：

```bash theme={null}
git diff --check
git status --short
git diff --stat
```

如果 `test-failure.txt` 只是本轮调试产物，确认没有价值后删除它。

删除后再次执行 `git status --short`。

## 回滚方式

如果只想撤销 `todo.py` 的未提交修改，先确认没有其他人的工作：

```bash theme={null}
git diff -- todo.py
git restore -- todo.py
```

如果要删除本轮新增且未跟踪的测试文件，先确认文件确实由本轮创建：

```bash theme={null}
git status --short
rm test_todo.py
```

Windows PowerShell 使用：

```powershell theme={null}
Remove-Item test_todo.py
```

如果同时需要恢复多个本轮文件，可以使用：

```bash theme={null}
git restore -- todo.py
git clean -n -- test_todo.py
```

`git clean -n` 只预览，不会删除。

确认预览只列出本轮新增文件后，再执行：

```bash theme={null}
git clean -f -- test_todo.py
```

不要随意使用 `git restore .` 或 `git clean -fd`。

前者会丢弃所有未提交修改，后者可能删除多个未跟踪目录。

如果发现工作区原本就有未提交改动，不要覆盖它们。

应使用 `git diff` 区分基线修改与 Codex 本轮修改，必要时从补丁恢复。

如果已经提交，不能用恢复工作区的命令撤销提交内容。

应创建一个新的反向修复提交，并先经过审查。

本页练习明确要求不提交，因此正常结果应当仍然显示未提交的代码改动。

## 小结

第一次任务的核心不是一句“帮我写代码”。

核心是一个可观察、可验证、可恢复的协作循环：

```text theme={null}
需求 -> 只读探索 -> 四件套提示词 -> 计划 -> 审批 -> 修改 -> diff -> 测试 -> 手工验收 -> 解释或回滚
```

只读探索让 Codex 先理解项目。

四件套让它知道要做什么、改哪里、不能做什么、如何证明完成。

审批让高影响命令停在人的判断边界上。

diff 让你看到实际发生了什么，而不是只听总结。

测试把修复后的行为固定下来。

失败修复训练你根据证据定位，而不是凭感觉重写。

验收报告把“我认为完成”变成可以复查的事实。

Git 检查点则提供最后的回滚路径。

熟悉这套流程后，再把同样的方法带到真实项目：先缩小范围，先读后改，小步验证，保留回滚。

参考资料：`参考/codex/06-first-task.md`、`参考/codex/13-prompting.md`、`参考/codex/14-workflows.md`。
