IB-WP-0014: archive-list, restore, retention annotation, docs (T03-T05)

Round out IB-WP-0014 with the remaining archive operations and docs.

- restore_archive() and `infospace-bench restore <pkg> --target <dir>` round-trip
  a finalized package's bytes back to disk. Refuses to overwrite a non-empty
  target unless --force. --from <infospace-root> resolves the store location.
- archive-list CLI with --with-retention flag; annotate_retention() opens the
  per-infospace registry and joins each record with its current retention
  state (effective class, expires, holds, eligibility).
- docs/archive-integration.md covers when to archive, the include set,
  retention classes, storage layout, credentials policy, and the explicit
  non-goal that S3/git backends live in artifact-store.
- SCOPE.md cross-links the new doc.
- Workplan flipped to status: done. Full pytest suite: 72 passed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 11:46:23 +02:00
parent e343443d77
commit ddefd69f71
8 changed files with 636 additions and 2 deletions

View File

@@ -57,3 +57,53 @@ def test_cli_returns_structured_error(tmp_path: Path) -> None:
assert result.returncode == 2
payload = json.loads(result.stderr)
assert payload["error"]["code"] == "missing_infospace"
def test_cli_archive_list_and_restore(tmp_path: Path) -> None:
create = run_cli(
"create",
str(tmp_path),
"cli-archive",
"--name",
"CLI Archive",
)
assert create.returncode == 0, create.stderr
root = tmp_path / "infospaces" / "cli-archive"
source = tmp_path / "src.md"
source.write_text("# src\n", encoding="utf-8")
add = run_cli(
"add-artifact", str(root), str(source), "--kind", "source", "--title", "Src",
)
assert add.returncode == 0, add.stderr
archive = run_cli("archive", str(root), "--note", "via cli")
assert archive.returncode == 0, archive.stderr
record = json.loads(archive.stdout)
assert record["note"] == "via cli"
assert record["manifest_digest"].startswith("blake3:")
listing = run_cli("archive-list", str(root))
assert listing.returncode == 0, listing.stderr
assert json.loads(listing.stdout)["archives"][0]["package_id"] == record["package_id"]
listing_with_retention = run_cli(
"archive-list", str(root), "--with-retention",
)
assert listing_with_retention.returncode == 0, listing_with_retention.stderr
annotated = json.loads(listing_with_retention.stdout)["archives"]
assert annotated[0]["retention"]["effective_class"] == "release-evidence"
target = tmp_path / "restored"
restore = run_cli(
"restore",
record["package_id"],
"--target",
str(target),
"--from",
str(root),
)
assert restore.returncode == 0, restore.stderr
result = json.loads(restore.stdout)
assert result["file_count"] == record["file_count"]
assert (target / "infospace.yaml").is_file()