IB-WP-0019-T07: archive integration; close IB-WP-0019

The default archive include set already pulls output/ in wholesale, so
output/budget/ already lands inside the archive package with no code
change. Add a budget_summary block to ArchiveRecord.metadata so
catalog-level tools can see plans_count, runs_count, total_tokens,
total_cost_usd_known, total_cost_usd_estimated, and the
latest_snapshot_id without unpacking the archive. An infospace with no
budget data still archives cleanly with an empty metadata dict.

Closes IB-WP-0019 (Budget and Usage Registry): T01-T07 all done.
Three-layer design landed end-to-end — layer 1 (per-infospace
plans.yaml / usage.yaml / summary.yaml) and layer 3 (state-hub
record_token_event emission with failure isolation) live here; layer 2
(cross-application QualityLedger for adaptive routing) is parked in
llm-connect LLM-WP-0004 and infospace-bench IB-WP-0018 awaits it.

122 tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 21:53:28 +02:00
parent 816a95b3ef
commit bb70b2f4b9
3 changed files with 89 additions and 2 deletions

View File

@@ -722,6 +722,40 @@ def test_budget_cli_list_and_show(tmp_path: Path) -> None:
assert show_payload["usage"]["runs"]
def test_archive_includes_budget_dir_and_records_summary(tmp_path: Path) -> None:
from infospace_bench.archive import archive_infospace
from infospace_bench.budget import PLANS_FILE, USAGE_FILE
from infospace_bench.generator import run_generation
root = _build_infospace(tmp_path)
fixture = tmp_path / "responses.yaml"
_write_minimal_fixture(fixture)
plan_generation(root)
run_generation(root, fixture_responses=fixture)
assert (root / PLANS_FILE).is_file()
assert (root / USAGE_FILE).is_file()
record = archive_infospace(root, retention_class="release-evidence")
summary = record.metadata.get("budget_summary")
assert summary is not None
assert summary["plans_count"] >= 1
assert summary["runs_count"] == 1
assert "latest_snapshot_id" in summary
# The budget dir is implicitly included via output/, so the archive's
# file_count should reflect that.
assert record.file_count > 0
def test_archive_metadata_empty_when_no_budget_data(tmp_path: Path) -> None:
from infospace_bench.archive import archive_infospace
root = _build_infospace(tmp_path)
record = archive_infospace(root, retention_class="release-evidence")
assert record.metadata.get("budget_summary") is None or record.metadata == {}
def test_plan_cli_writes_snapshot(tmp_path: Path) -> None:
root = _build_infospace(tmp_path)
env = os.environ.copy()