From b929a5db6c3b02a2f833c5f91bede08cfbcda97a Mon Sep 17 00:00:00 2001 From: iomgaa Date: Tue, 7 Jul 2026 12:23:27 -0400 Subject: [PATCH] =?UTF-8?q?fix(harness):=20Codex=20functional=20review=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20=E2=80=94=20=E5=91=BD=E5=90=8D/=E9=9B=86?= =?UTF-8?q?=E6=88=90=E6=B5=8B=E8=AF=95/delta=5Fmin/promote=20=E6=B6=88?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _apply_env_overrides → _apply_env_var_overrides,docstring 明确从 os.environ 读取 - 新增 TestLoadConfigRealYaml:用真实 config/default.yaml 验证嵌套 harness 解析 - 新增 test_delta_min_negative_rejected:覆盖 gate_delta_min >= 0 校验 - 恢复 promote 模式独立错误消息(从合并分支分离回 TRM4 原始提示) - 77 个单元测试全部通过,radon 全部 Grade B 或更好 Co-Authored-By: Claude Opus 4.6 (1M context) --- app/harness/config.py | 11 ++++++---- tests/unit/test_harness_config.py | 36 ++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/app/harness/config.py b/app/harness/config.py index 188d6b1..8fdc065 100644 --- a/app/harness/config.py +++ b/app/harness/config.py @@ -179,10 +179,12 @@ def _validate_mode_deps(config: RunConfig) -> None: 异常: ValueError: 模式依赖字段缺失。 """ - if config.mode in ("diagnose", "evolve", "promote") and not config.run_id: + if config.mode in ("diagnose", "evolve") and not config.run_id: raise ValueError(f"mode 为 {config.mode!r} 时必须提供 run_id。") if config.mode in ("eval", "promote") and not config.version: raise ValueError(f"mode 为 {config.mode!r} 时必须提供 --version。") + if config.mode == "promote" and not config.run_id: + raise ValueError("promote 必须提供 --run-id(指定 canonical eval run)。") _validate_train_run_id(config) @@ -363,9 +365,10 @@ def _validate_gate_ladder(config: RunConfig) -> None: raise ValueError(f"gate_guard_err 须在 (0,1),实际: {config.gate_guard_err}") -def _apply_env_overrides(data: dict) -> None: - """将 .env 工程配置环境变量覆盖到配置字典中(原地修改)。 +def _apply_env_var_overrides(data: dict) -> None: + """从环境变量覆盖路径字段(原地修改)。 + .env 文件由入口脚本 load_dotenv 加载到环境变量,本函数仅从 os.environ 读取。 仅覆盖 _ENV_FIELD_MAP 中声明的工程配置字段(workspace_dir、store_dir)。 参数: @@ -404,7 +407,7 @@ def load_config( yaml_data: dict = raw.get("harness", raw) # Phase 2: .env 覆盖层(仅工程配置字段) - _apply_env_overrides(yaml_data) + _apply_env_var_overrides(yaml_data) # Phase 3: CLI 覆盖层(最高优先级) valid_fields = {f.name for f in dataclasses.fields(RunConfig)} diff --git a/tests/unit/test_harness_config.py b/tests/unit/test_harness_config.py index 2d78c76..2d01d10 100644 --- a/tests/unit/test_harness_config.py +++ b/tests/unit/test_harness_config.py @@ -169,7 +169,7 @@ class TestModeValidation: def test_promote_requires_run_id_and_version(self) -> None: """promote 模式需同时提供 run_id 和 version。""" cfg = _make_config(mode="promote", run_id="", version="v1") - with pytest.raises(ValueError, match="run.id"): + with pytest.raises(ValueError, match="promote.*run-id"): _validate(cfg) def test_train_mode_requires_run_id_without_resume_fresh(self) -> None: @@ -359,6 +359,12 @@ class TestGateValidation: with pytest.raises(ValueError, match="gate_w_net_min"): _validate(cfg) + def test_delta_min_negative_rejected(self) -> None: + """gate_delta_min < 0 应抛出 ValueError。""" + cfg = _make_config(gate_delta_min=-0.1) + with pytest.raises(ValueError, match="gate_delta_min"): + _validate(cfg) + def test_lambda_dir_positive_rejected(self) -> None: """gate_lambda_dir >= 0 应抛出 ValueError。""" cfg = _make_config(gate_lambda_dir=0.5) @@ -527,6 +533,34 @@ class TestLoadConfigEnvOverrides: assert cfg.workspace_dir == Path("/cli/workspace") +# ──────────────────── test_load_config_real_yaml ───────────────────────── + + +class TestLoadConfigRealYaml: + """用真实 config/default.yaml 验证 load_config 嵌套解析。""" + + def test_load_config_real_default_yaml(self) -> None: + """真实 config/default.yaml 的 harness 段应正确解析并通过校验。""" + cfg = load_config(Path("config/default.yaml"), {}) + assert cfg.mode == "infer" + assert cfg.gate_e_confirm == 20.0 + assert cfg.batch_size == 15 + assert cfg.concurrency == 12 + assert cfg.workspace_dir == Path("workspaces/default") + assert cfg.store_dir == Path("store") + assert cfg.skill_mode == "auto" + + def test_real_yaml_cli_override(self) -> None: + """真实 YAML + CLI 覆盖应正确合并。""" + cfg = load_config( + Path("config/default.yaml"), + {"concurrency": 4, "max_steps": 30}, + ) + assert cfg.concurrency == 4 + assert cfg.max_steps == 30 + assert cfg.mode == "infer" # 未覆盖字段保持 YAML 值 + + # ─────────────────────────── test_val_size_floor ─────────────────────────