Skip to content

Conversation

@jdoucett
Copy link
Contributor

Phase 2, Slice 3 — aggregate reclaim (verify_target)

Required before any routed target can be reclaimed: a size-routed target ships as many
objects, so reclaim's per-log verify() sees only the newest object's few files and would
perpetually false-DRIFT every routed target. This makes reclaim reconstruct the whole
target from its manifest. Based directly on main (not stacked). Suite 196 → 209 green.

  • verify_target(manifest, holds, …) — expected set = ⋃ objects' arcnames, archived =
    Σ n_files, archived_bytes = Σ source_bytes; every object tar must be present
    (hsi_exists per object); newer-than-run is per-object (a live member drifts iff newer
    than the run of the object that owns it). Same verdict vocabulary + INV-E exclusion
    re-gate
    as verify() (the manifest carries the same exclusion block, so
    recheck_exclusions works on it directly). Returns the standard result dict + all_tars.
  • find_manifests(log_dir) — loads {log_dir}/_vault/*.manifest.json.
  • main()--scan skips routed object logs (routed_object marker) and verifies
    each manifest via verify_target; --config with size_routing resolves badge → manifest;
    --match filters manifests too. Legacy / non-routed logs flow through verify()
    unchanged.
  • delete_source — guarded re-confirm of all object tars before rm, not just one.

Tests — tests/test_routing_reclaim.py (13)

SAFE • count-drift (extra file) • newer-drift • bytes-drift • missing-tar → FAILED •
no-objects → FAILED • GONE • HOLD • INV-E no-longer-covered → DRIFTED • INV-E still-covered
subtraction → SAFE • find_manifests • multi-tar delete ABORT. Existing 196 unchanged.

Phase 2 status after this

Slices 1+2 in main; this is slice 3. Remaining: slice 4 (reship-monitor), slice 5 (full
restore). With 1–3, a routed target archives (append) and reclaims correctly.

🤖 Generated with Claude Code

reclaim.py must verify a size-routed target as an AGGREGATE or every routed target
perpetually false-DRIFTs (its newest log is one object of few files). Adds:

- verify_target(manifest, holds, …): reconstruct the whole target from its manifest —
  expected set = ⋃ objects' arcnames, archived = Σ n_files, archived_bytes =
  Σ source_bytes; require EVERY object tar present (hsi_exists per object); newer-than-run
  is PER-OBJECT (a live member drifts iff newer than the run of the object that owns it).
  Same verdict vocabulary + INV-E exclusion re-gate as verify() (the manifest carries the
  same `exclusion` block, so recheck_exclusions works on it directly). Returns the standard
  result dict + all_tars.
- find_manifests(log_dir): load {log_dir}/_vault/*.manifest.json (the aggregate indexes).
- main(): --scan skips routed OBJECT logs (routed_object marker) and verifies each manifest
  via verify_target; --config with size_routing resolves its badge → manifest → verify_target;
  --match filters manifests too. Legacy/non-routed logs flow through verify() unchanged.
- delete_source: guarded re-confirm of ALL object tars (all_tars) before rm, not just one.

Tests: tests/test_routing_reclaim.py (13) — SAFE / count-drift / newer-drift / bytes-drift /
missing-tar FAILED / no-objects FAILED / GONE / HOLD; INV-E no-longer-covered → DRIFTED and
still-covered subtraction → SAFE; find_manifests; multi-tar delete ABORT. Suite 196 → 209.

Based on main (NOT stacked). See docs/RFC_incremental_v2.md §2.6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Jarrod S Doucette (jdoucett) added a commit that referenced this pull request Jul 10, 2026
…holds)

Merging slice-3 (#21, verify_target) and typed holds (#22) composed a latent bug: #22 makes
load_holds return typed dict entries, but verify_target still did inline string matching
(h in source_folder), which raises TypeError on a dict hold — i.e. verify_target would crash
on any routed target the moment a reclaim_holds.json is used. Legacy .txt (string) holds were
unaffected, so the merged suite stayed green and it went unseen.

Route verify_target through the same hold_entry_for + classify_hold helpers verify() uses, so
routed targets get the full business/promote-pending/superseded tiering (HOLD / AWAITING-
APPROVAL) instead of a flat crash-prone string match. Tests: verify_target under a business
dict hold -> HOLD, promote-pending no-receipt -> HOLD, promote-pending valid-receipt ->
AWAITING-APPROVAL. Suite 235 -> 238.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jdoucett Jarrod S Doucette (jdoucett) deleted the feat/routing-reclaim branch July 10, 2026 02:22
Jarrod S Doucette (jdoucett) added a commit that referenced this pull request Jul 10, 2026
…typed-holds

fix(reclaim): verify_target tiers typed holds (composition bug from #21+#22)
Sign in to join this conversation on GitHub.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant