diff --git a/dokimi/changelog.d/20261114_130000_partial-status-guard.md b/dokimi/changelog.d/20261114_130000_partial-status-guard.md new file mode 100644 index 000000000..a5222f3c5 --- /dev/null +++ b/dokimi/changelog.d/20261114_130000_partial-status-guard.md @@ -0,0 +1,3 @@ +### Fixed + +- **A routing result marked `"status": "partial"` without a `partial` block is a partial result to the overwrite guard** — a capped `novelty routing` run may replace it, and a finished result is kept as before. `novelty items` already refused such a probe file. diff --git a/dokimi/dokimi/commands/novelty_routing_report.py b/dokimi/dokimi/commands/novelty_routing_report.py index 7dc1dfe8c..63cceb000 100644 --- a/dokimi/dokimi/commands/novelty_routing_report.py +++ b/dokimi/dokimi/commands/novelty_routing_report.py @@ -531,7 +531,7 @@ def _is_partial_file(path: Path) -> bool: earlier = json.loads(path.read_text(encoding="utf-8")) except (OSError, ValueError): return False - return isinstance(earlier, dict) and "partial" in earlier + return isinstance(earlier, dict) and ("partial" in earlier or earlier.get("status") == "partial") def _takes_partial(candidate: Path) -> bool: diff --git a/dokimi/tests/test_novelty_routing_spend_cap_partial.py b/dokimi/tests/test_novelty_routing_spend_cap_partial.py index 2ec4d6f0f..f27f0409f 100644 --- a/dokimi/tests/test_novelty_routing_spend_cap_partial.py +++ b/dokimi/tests/test_novelty_routing_spend_cap_partial.py @@ -815,3 +815,13 @@ def test_a_partial_single_run_rates_its_selections_over_the_calls_it_made(tmp_pa hits = sum(1 for row in model["per_query"] if row.get("correct")) assert model["n_calls"] == 2 assert model["selection_rate"] == round(hits / 2, 4) + + +def test_a_file_marked_partial_by_status_alone_is_a_partial_result_to_write_over(tmp_path): + from dokimi.commands.novelty_routing_report import _is_partial_file, save_result + + marked = tmp_path / "routing.json" + marked.write_text('{"status": "partial"}', encoding="utf-8") + assert _is_partial_file(marked) + assert save_result({}, marked, {"partial": PARTIAL}) == marked + assert json.loads(marked.read_text(encoding="utf-8"))["partial"] == PARTIAL