Claude Code 改完代码后,终端返回 1,究竟是程序坏了,还是程序正确地找到了坏数据?如果事先没有定义退出码,工具很容易沿着错误方向继续修改。
本文从这个具体问题展开:安装与登录后,让 Claude Code 实现一个小型 JSONL 检查器,再用独立测试判断交付。最后区分继续会话、回退代码和恢复数据,避免把它们混成同一种“撤销”。
一、Windows、macOS、Linux 的安装与更新
以下于 2026-09-20 对照 Anthropic 安装文档 核查。macOS、Linux、WSL 在对应终端执行:
curl -fsSL https://claude.ai/install.sh | bash
Windows 原生 PowerShell:
irm https://claude.ai/install.ps1 | iex
当前原生安装方式支持自动更新,也可执行 claude update 主动更新。用 Homebrew 或 WinGet 安装的,应按原包管理器更新;不要把几种渠道同时堆进 PATH。
Windows 原生环境推荐配合 Git for Windows;当前文档也说明未找到 Git Bash 时可使用 PowerShell。需要 Linux 工具链可使用 WSL 2,并在 WSL 内单独核对安装、目录和 Python。Claude Code 原生 Windows 的沙盒支持与 Codex 不相同,不能把另一工具的说明直接搬过来;当前内置沙盒不支持原生 Windows,WSL 2 是相应选择之一。
安装后新开终端:
claude --version
claude doctor
找不到命令时,Windows 用 Get-Command claude,macOS/Linux 用 command -v claude 看实际位置;claude doctor 用于检查安装及配置状态。
二、进入项目并登录
新建空目录 jsonl-lab,进入目录执行 claude,根据界面选择适用账号并完成登录。会话内可用 /login 重新认证;如果环境已经设置 ANTHROPIC_API_KEY,登录流程可能先询问是否使用该密钥。本文不需要把密钥写进代码。参见 官方快速开始。
先让工具说明当前目录、已有文件和运行 Python 3 的命令。本文示例只依赖 Python 标准库。Windows 常用 py -3,macOS/Linux 常用 python3,都要先检查版本是否可用。
项目根目录建立 CLAUDE.md:
只实现本练习的 JSONL 校验器,使用 Python 标准库。
输入只读;报告使用新文件名,拒绝覆盖已有报告。
修改前说明计划,发现规格冲突先指出具体冲突。
不要为了通过测试而删除断言或放宽数据校验规则。
检查失败时先读错误和契约,再决定改实现还是纠正规格。
最后报告实际测试命令、退出码、结果及未验证事项。
这些内容帮助模型理解项目,不能替代权限控制。确认操作范围后再允许具体命令,不需要为入门练习开启跳过权限确认的模式。
三、先定义任务与五项契约测试
给 Claude Code 下面这段任务,再把后面的测试保存为 test_contract.py:
先阅读 test_contract.py,然后说明计划并实现 audit_jsonl.py。
提供 audit(text) 函数,返回 valid、invalid、errors。
每个非空物理行是一个 JSON 对象;空行忽略但行号保留。
id 和 text 必须为去除两端空白后非空的字符串。
ID 去空白后判重,仅此前有效记录占用 ID。
错误顺序为 invalid_json、not_object、invalid_id、invalid_text、duplicate_id。
每条坏记录仅记一项错误,errors 中只记录 line 和 code。
JSON 重复键及 NaN、Infinity 等非标准常量都按 invalid_json 处理。
CLI 接收输入路径及 --output 参数;按 UTF-8 读取并兼容开头 BOM。
不修改输入,拒绝覆盖已有报告。
全部有效退出 0;有坏数据且报告写出退出 1;文件或参数错误退出 2。
先运行已有契约测试,再补空文件、非法编码、重复 JSON 键等边界检查。
不要把退出码 1 擅自改成 0。
以下五项测试可以完整运行;假定实现文件与测试在同一目录:
import json
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from audit_jsonl import audit
class ContractTests(unittest.TestCase):
def test_duplicate_id(self):
data = '{"id":"a","text":"甲"}\n{"id":" a ","text":"乙"}'
self.assertEqual(audit(data)['errors'],
[{'line': 2, 'code': 'duplicate_id'}])
def test_bad_record_does_not_reserve_id(self):
data = '{"id":"a","text":""}\n{"id":"a","text":"ok"}'
self.assertEqual(audit(data)['valid'], 1)
def test_physical_line(self):
self.assertEqual(audit('\nwrong')['errors'][0]['line'], 2)
def test_unicode_separator(self):
data = json.dumps({'id': 'a', 'text': '甲\u2028乙'}, ensure_ascii=False)
self.assertEqual(audit(data)['invalid'], 0)
def test_cli_and_no_overwrite(self):
with tempfile.TemporaryDirectory() as folder:
source = Path(folder) / '输入 sample.jsonl'
report = Path(folder) / 'report.json'
original = b'{"id":"a","text":"ok"}\nbroken\n'
source.write_bytes(original)
command = [sys.executable,
str(Path(__file__).with_name('audit_jsonl.py')),
str(source), '--output', str(report)]
first = subprocess.run(command, capture_output=True, text=True)
self.assertEqual(first.returncode, 1)
self.assertEqual(first.stdout.strip(), 'valid=1 invalid=1')
saved = report.read_bytes()
self.assertEqual(json.loads(saved)['errors'],
[{'line': 2, 'code': 'invalid_json'}])
second = subprocess.run(command, capture_output=True, text=True)
self.assertEqual(second.returncode, 2)
self.assertEqual(report.read_bytes(), saved)
self.assertEqual(source.read_bytes(), original)
if __name__ == '__main__':
unittest.main()
测试把输入路径放在临时目录中,包含中文和空格;通过参数列表调用 Python,不靠拼接 shell 字符串。它既检查函数规则,也检查真实 CLI、退出码、已有报告和输入字节。
Windows 运行:
py -3 -m unittest -v test_contract.py
macOS/Linux 运行:
python3 -m unittest -v test_contract.py
本次参考实现先通过了 17 个 unittest 方法;从验收需求另整理的上面五项测试也已单独运行通过,包含与原测试重叠的检查,不把它们算成 22 个独立场景。环境是 macOS arm64、Python 3.13.13。本轮未安装或运行 Claude Code 模型会话,三系统安装步骤来自当日官方文档。
四、失败时让工具围绕证据修复
如果 test_unicode_separator 失败,先让它解释:字符串里的 U+2028 为什么被切成了另一条 JSONL 记录。问题可能是用了 splitlines(),而我们的契约只按物理 LF 换行拆记录。
如果真实 CLI 测试返回 0,要核对“有坏记录也成功生成报告”应该返回哪个状态,不要因为终端没有 traceback 就认定正确。
可追加提示:
只处理当前失败的契约。先指出实际值和预期值的差异,
解释根因,再做最小修改。保留原断言,重新运行相关测试,
通过后运行全部测试。说明输入文件和已有报告是否仍保持原样。
测试也可能写错,所以“保留断言”不代表绝对禁止调整;真正冲突时,应先讲清需求为何变化,再同步实现、测试和文档。不能在不说明的情况下悄悄降低标准。
五、继续会话不等于回退磁盘
在项目目录执行 claude -c 可继续当前目录最近会话;claude -r 用于选择历史会话。回到对话后,让工具先读最新文件与测试结果,再继续计划。
需要检查点菜单时,在会话使用 /rewind,或在输入为空时连按两次 Esc。菜单可以选择恢复代码、恢复对话或两者。关键限制是:检查点主要跟踪 Claude 文件编辑工具的直接修改,Bash 命令造成的文件变化不在这套回退范围内。参见 检查点官方说明。
本例运行 Python 产生的报告属于命令执行的输出,不能假定回退会自动清除或还原它。恢复会话后再次运行相同报告名,程序拒绝覆盖是预期行为;指定新报告名即可保留两次结果。
Git 版本记录、文件备份、数据库事务各有自己的职责。不要因为菜单上有“恢复代码”,就推断数据库和远程服务也会回到原状。
六、AGENTS.md 是否需要再复制一份
今天核查的文档已经给出直接读取 AGENTS.md 的支持:要求 Claude Code v2.1.277 或更高,并受会话环境及项目指令配置影响。默认情况下,工作目录或上级已有 CLAUDE.md / CLAUDE.local.md 时,会优先读这些文件;不能仅因两个文件都存在就认为二者都已加载。
某些会话(例如特定第三方提供商或禁用遥测时)不支持直接读取;已有 CLAUDE.md 导入 @AGENTS.md 的兼容做法仍可保留。请对照 项目记忆文档 检查自己版本与会话。本例直接使用 CLAUDE.md,便于明确项目规则。
完整工作流应有实际输入、独立验收和恢复边界。先用小项目走通这些环节,再增加插件或复杂编排,调试时才知道问题来自哪一层。
本文使用 AI 辅助整理与编写;只把有记录的离线 Python 检查称为实测,不把官方功能说明写成个人模型使用经历。

387

被折叠的 条评论
为什么被折叠?



