> **发布元数据**：WorkBuddy · Kimi K3（评测者确认）· R01
> **来源**：`workbuddy/输出/workbuddy/T02/R01/BENCHMARK_RESULT.md`
> **原始文件 SHA-256**：`639fbf65e9631f7edf94b144884e74b4333ac1d4c4106cc5dd4032f2b75af893`
> 本公开副本已将评测机绝对路径归一化；下文 PASS/FAIL/BLOCKED 是执行报告自述状态，不等同于独立验收结论。

---

BENCH-RUN T02 R01

# T02 配置优先级与错误路径（只读）

## 结果
PASS

## 任务：`python3 -m ledger list` 选择交易 CSV 的配置流程

### 数据来源优先级（实际验证）
配置解析统一由 `ledger/settings.py` 的 `resolve_data_file(cli_path=None, environ=None, config_path=None)`（第 8–34 行）完成。CLI 在 `ledger/cli.py:42` 以 `resolve_data_file(cli_path=args.data_file, environ=os.environ, config_path=args.config)` 调用它。

实际优先级（高 → 低，前四步为短路 `if/elif/elif/else`）：
1. **`--data-file` 全局参数**（`cli.py:29` 注册 → `resolve_data_file` 的 `cli_path`，第 13–14 行）。
2. **环境变量 `LEDGER_DATA_FILE`**（第 15–16 行）。
3. **`--config` 指定的 JSON 配置文件**中的 `data_file` 键（第 17–28 行）。
4. **默认文件** `DEFAULT_DATA_FILE`（第 5 行）= `ledger/data/transactions.csv`（`Path(__file__).resolve().parent / "data" / "transactions.csv"`），仅当前三者都缺省时返回（第 29–30 行）。

> 实证：用例 6 `LEDGER_DATA_FILE=/tmp/rel.csv ... --config <缺 data_file 的 config>` 仍成功输出 env 指向的数据（exit 0），证明 env 优先于 config；用例 1–3 证明 config 仅在前两者缺省时才被读取。

### 相对路径如何解析
- 通过 `Path(selected).expanduser()`（第 31 行）展开 `~`。
- **仅当来源是 config 文件时**（`selected_base = config_file.parent`，第 28 行），相对 `data_file` 才**相对于配置文件所在目录**拼接：`path = selected_base / path`（第 32–33 行）。
- 最终一律 `path.resolve()`（第 34 行）归一化为绝对路径。
- 来自 `--data-file` 或环境变量的相对路径，`selected_base is None`，不做目录拼接，直接 `resolve()`（即相对于**当前工作目录**）。
- 实证：用例 4 中 config `/tmp/t02rel.json` 含相对 `"data_file":"rel.csv"`，成功读到 `/tmp/rel.csv`（exit 0）。

### 配置格式错误的处理
- config 文件不存在 → `FileNotFoundError("config file not found: ...")`（第 21–22 行）。实证用例 1：`ledger: error: config file not found: /nonexistent/x.json`，exit=2。
- JSON 解析失败 / 编码错误 → `ValueError("invalid config file ...: ...")`（第 23–24 行）。实证用例 2：`ledger: error: invalid config file /tmp/t02bad.json: Expecting property name...`，exit=2。
- config 非 dict 或 `data_file` 缺失/非字符串/空 → `ValueError("config must contain a non-empty string 'data_file'")`（第 26–27 行）。实证用例 3：同前，exit=2。

### 目标数据文件不存在的处理
- `resolve_data_file` 不检查数据文件存在性，只在 `ledger/storage.py` 的 `load_transactions(path)` 打开时抛出 `FileNotFoundError`，被 `cli.py:54` 捕获，打印 `ledger: error: [Errno 2] No such file or directory: ...`，exit=2。实证用例 5 验证。

### 最终由哪个模块读取 CSV
`ledger/storage.py` 的 `load_transactions(path)`（第 11–29 行）：用 `csv.DictReader` 读取，校验必需列 `{id,date,category,amount}`（缺列抛 `ValueError("missing CSV columns: ...")`），逐行构造 `ledger.model.Transaction`，金额经 `ledger.money.parse_money()` 解析，行级错误包装为 `ValueError("invalid transaction at line N: ...")`。

### 错误处理出口
所有上述 `FileNotFoundError`/`ValueError`/`OSError` 均由 `ledger/cli.py:54-56` 统一捕获：`print(f"ledger: error: {exc}", file=sys.stderr)` 并 `return 2`（非零退出）。

## 实际改动
无（只读题，未修改任何文件；仅在工作区外 /tmp 临时创建并随后删除测试用 config/CSV）。

## 实际运行的命令
- 源码逐行审读 `ledger/settings.py`、`ledger/cli.py`、`ledger/storage.py`。
- 6 组 `python3 -m ledger ... list` 用例（见上），覆盖：config 缺失、JSON 非法、缺 data_file、config 相对路径解析、数据文件缺失、env 优先于 config。

## 耗时
约 4 分钟。

## 未完成项
无。所有结论均有源码行号或实证命令输出支撑，无"不确定"项。

## 会话隔离状态
在独立 workspace `输出/workbuddy/T02/R01/workspace/` 中操作，未读取父目录或其他工具目录，未修改基准仓库或题面。
