cli: 'aborist burn-kindergarten' — mass-burn fresh providence rows

Per fox: useful while iterating on retrieval/verifier knobs to wipe
recent test runs without finding each cache_key. Mirrors the mesh-sync
kindergarten window so what's still un-broadcast (private to this peer)
is exactly what's safe to bust without confusing peers.

Surface:
  aborist burn-kindergarten [--kindergarten-seconds N]
                            [--reason '...'] [--by-actor X]
                            [--force] [--dry-run] [--verbose N]
  make burn-kindergarten [SECONDS=3600] [FORCE=1] [DRY_RUN=1] [REASON='why']

Behavior:
- Walks every shard, finds providence_cache rows with
  created_at >= now - SECONDS and falsification_state='live'.
- Each row goes through the existing _burn_cache_key — children gate
  honored unless --force, audit event written per successful burn,
  chain integrity preserved.
- Result JSON reports examined / burned / refused_has_children /
  not_found counts plus a verbose tail of per-row results.
- --dry-run reports without writing or auditing.
- --kindergarten-seconds 0 = burn every live row (test reset).

Tests (4): selective burn (old kept, fresh burned), dry-run writes
nothing, 0-second window burns everything, children-gate refusal
without --force. 353 passed, 1 skipped.

Operational note: this command does NOT propagate to peers (burn is
local kindergarten cleanup by design, see docs/mesh.md). If you want
the remote effect, falsify each record individually & let mesh sync
broadcast the falsifications instead.
This commit is contained in:
russell@unturf.com 2026-04-29 16:56:59 -04:00
parent d6f98929d7
commit 4573dbcf4e
No known key found for this signature in database
3 changed files with 286 additions and 1 deletions

View file

@ -31,7 +31,7 @@ SEARCH_Q ?= computer
ingest-grok ingest-grok-media \
ingest-self ingest-git ingest-hg \
verify search stats test docs chain-check chain-check-shards \
falsify burn inspect bootstrap-crawler test-crawler crawl-ingest \
falsify burn burn-kindergarten inspect bootstrap-crawler test-crawler crawl-ingest \
recrawl-check clean clean-db clean-data help
all: bootstrap fetch-cur ingest-cur verify stats ## bootstrap → fetch cur → ingest cur → verify → stats
@ -140,6 +140,18 @@ burn: bootstrap ## delete a leaf with no children. providence: KEY=<cache_key>;
echo "unknown KIND: $$kind (expected: providence|document|core)" >&2; exit 2; \
fi
# Mass-burn providence_cache rows younger than the kindergarten window.
# Mirrors mesh sync's kindergarten so what's still un-broadcast is what's
# safe to bust without confusing peers. Useful while iterating on
# retrieval/verifier tunings — wipe recent test runs in one shot.
KG_SECONDS ?= 3600
burn-kindergarten: bootstrap ## bust providence rows < SECONDS old [SECONDS=3600 FORCE=1 DRY_RUN=1 REASON='why']
$(ABORIST) --shards-dir $(SHARDS_DIR) burn-kindergarten \
--kindergarten-seconds $(KG_SECONDS) \
$(if $(REASON),--reason "$(REASON)",) \
$(if $(FORCE),--force,) \
$(if $(DRY_RUN),--dry-run,)
# Multi-source RAG query against the shard cluster.
# Usage: make query Q="What is anarcho-capitalism?"
QUERY_TOP_K ?= 8

View file

@ -1022,6 +1022,91 @@ def _burn_core_root(
return {"status": "not_found", "document_root": document_root_value, "kind": "core"}
def _cmd_burn_kindergarten(args: argparse.Namespace) -> int:
"""Burn all providence_cache rows younger than the kindergarten window.
Test-ergonomic mass burn: when iterating on retrieval/verifier knobs
you want to wipe recent test runs without finding each cache_key.
Mirrors the kindergarten window from `mesh sync` so what's still
"private" (un-broadcast) is also what's safe to bust without
confusing peers.
Each row goes through the standard `_burn_cache_key` so the
children gate is honored (use `--force` to override en masse).
Each successful burn writes one ``providence_burn`` audit event;
chain integrity is verifiable via `make chain-check-shards` after.
"""
import time as _time
from aborist.store import discover_shards
now = int(_time.time())
cutoff = now - max(0, args.kindergarten_seconds)
shards_dir = Path(args.global_shards_dir) if args.global_shards_dir else None
single_db = Path(args.db) if args.db else None
paths: list[Path] = (
discover_shards(shards_dir) if shards_dir else [single_db]
)
actor = args.by_actor or os.environ.get("USER", "unknown")
reason = args.reason or f"burn-kindergarten window={args.kindergarten_seconds}s"
examined = 0
burned = 0
refused = 0
not_found = 0
items: list[dict] = []
for sp in paths:
c = connect(sp)
try:
rows = c.execute(
"SELECT cache_key, created_at, audit_mode "
"FROM providence_cache "
"WHERE created_at >= ? AND falsification_state = 'live' "
"ORDER BY created_at DESC",
(cutoff,),
).fetchall()
finally:
c.close()
for r in rows:
examined += 1
if args.dry_run:
items.append({
"cache_key": r["cache_key"],
"audit_mode": r["audit_mode"],
"created_at": r["created_at"],
"would_burn": True,
})
continue
result = _burn_cache_key(
r["cache_key"],
reason=reason,
by_actor=actor,
shards_dir=shards_dir,
db_path=single_db,
force=bool(args.force),
)
status = result.get("status")
if status == "burned":
burned += 1
elif status == "refused_has_children":
refused += 1
else:
not_found += 1
items.append(result)
print(json.dumps({
"status": "dry_run" if args.dry_run else "burned",
"kindergarten_seconds": args.kindergarten_seconds,
"cutoff_at": cutoff,
"now": now,
"examined": examined,
"burned": burned,
"refused_has_children": refused,
"not_found": not_found,
"items": items[: args.verbose],
}, indent=2, ensure_ascii=False))
return 0
def _cmd_burn(args: argparse.Namespace) -> int:
"""CLI: burn a leaf with no children.
@ -2849,6 +2934,47 @@ def build_parser() -> argparse.ArgumentParser:
)
burn_cmd.set_defaults(func=_cmd_burn)
burn_kg_cmd = sub.add_parser(
"burn-kindergarten",
help=(
"burn every providence_cache record younger than the "
"kindergarten window — test-ergonomic mass cleanup that "
"matches the mesh-sync kindergarten window so only "
"un-broadcast records get busted"
),
)
burn_kg_cmd.add_argument(
"--kindergarten-seconds",
dest="kindergarten_seconds",
type=int,
default=3600,
help=(
"burn rows younger than this many seconds (default: 3600 = 1 "
"hour, mirrors mesh sync default). 0 = burn everything live."
),
)
burn_kg_cmd.add_argument(
"--reason", default=None,
help="reason text recorded in each providence_burn audit event",
)
burn_kg_cmd.add_argument(
"--by-actor", dest="by_actor", default=None,
help="who is burning (default: $USER)",
)
burn_kg_cmd.add_argument(
"--force", action="store_true",
help="burn even if rows have falsification children",
)
burn_kg_cmd.add_argument(
"--dry-run", dest="dry_run", action="store_true",
help="report what would burn without writing",
)
burn_kg_cmd.add_argument(
"--verbose", type=int, default=10,
help="include this many items in the result JSON (default: 10)",
)
burn_kg_cmd.set_defaults(func=_cmd_burn_kindergarten)
reclassify_cmd = sub.add_parser(
"reclassify",
help="re-run the verifier against existing live providence records "

View file

@ -274,6 +274,153 @@ def test_burn_cli_invocation(tmp_path, capsys):
assert payload["reason"] == "via cli"
def test_burn_kindergarten_burns_only_recent_rows(tmp_path, capsys):
"""Mass-burn rows younger than the window. Older rows are kept; the
test seeds two rows, backdates one to 2h ago, and asserts the
fresh one burns and the old one stays."""
import time as _time
from aborist.cli import build_parser
from aborist.store import transaction
db = tmp_path / "qa.db"
OLD = "ab" * 32
FRESH = "cd" * 32
_seed_providence_record(db, cache_key=OLD, question="old", audit_mode="STRICT")
_seed_providence_record(db, cache_key=FRESH, question="fresh", audit_mode="STRICT")
# Backdate OLD to 2h ago.
long_ago = int(_time.time()) - 7200
conn = connect(db)
try:
with transaction(conn):
conn.execute(
"UPDATE providence_cache SET created_at = ? WHERE cache_key = ?",
(long_ago, OLD),
)
finally:
conn.close()
parser = build_parser()
args = parser.parse_args([
"--db", str(db),
"burn-kindergarten",
"--kindergarten-seconds", "3600",
])
rc = args.func(args)
assert rc == 0
payload = json.loads(capsys.readouterr().out)
assert payload["status"] == "burned"
assert payload["examined"] == 1 # only FRESH met the cutoff
assert payload["burned"] == 1
conn = connect(db)
try:
rows = {r["cache_key"]: r for r in conn.execute(
"SELECT cache_key FROM providence_cache"
).fetchall()}
finally:
conn.close()
assert OLD in rows
assert FRESH not in rows
def test_burn_kindergarten_dry_run_writes_nothing(tmp_path, capsys):
"""--dry-run reports what would burn but doesn't write or audit."""
from aborist.cli import build_parser
db = tmp_path / "qa.db"
KEY = "ef" * 32
_seed_providence_record(db, cache_key=KEY)
conn = connect(db)
try:
events_before = conn.execute(
"SELECT COUNT(*) FROM audit_events"
).fetchone()[0]
finally:
conn.close()
parser = build_parser()
args = parser.parse_args([
"--db", str(db),
"burn-kindergarten",
"--kindergarten-seconds", "3600",
"--dry-run",
])
rc = args.func(args)
assert rc == 0
payload = json.loads(capsys.readouterr().out)
assert payload["status"] == "dry_run"
assert payload["examined"] == 1
assert payload["burned"] == 0
# Row still exists; audit chain unchanged.
conn = connect(db)
try:
still_there = conn.execute(
"SELECT 1 FROM providence_cache WHERE cache_key=?", (KEY,)
).fetchone()
events_after = conn.execute(
"SELECT COUNT(*) FROM audit_events"
).fetchone()[0]
finally:
conn.close()
assert still_there is not None
assert events_after == events_before
def test_burn_kindergarten_zero_seconds_burns_everything(tmp_path, capsys):
"""0-second window = burn every live providence_cache row."""
from aborist.cli import build_parser
db = tmp_path / "qa.db"
K1 = "11" * 32
K2 = "22" * 32
_seed_providence_record(db, cache_key=K1, question="q1")
_seed_providence_record(db, cache_key=K2, question="q2")
parser = build_parser()
args = parser.parse_args([
"--db", str(db),
"burn-kindergarten",
"--kindergarten-seconds", "0",
])
rc = args.func(args)
assert rc == 0
payload = json.loads(capsys.readouterr().out)
assert payload["burned"] == 2
conn = connect(db)
try:
remaining = conn.execute("SELECT COUNT(*) FROM providence_cache").fetchone()[0]
finally:
conn.close()
assert remaining == 0
def test_burn_kindergarten_respects_children_gate_without_force(tmp_path, capsys):
"""A row with falsifications is REFUSED unless --force. Audit chain
still grows on burned rows; refused rows leave no audit entry."""
from aborist.cli import build_parser
db = tmp_path / "qa.db"
BURNABLE = "33" * 32
HAS_KIDS = "44" * 32
_seed_providence_record(db, cache_key=BURNABLE)
_seed_providence_record(db, cache_key=HAS_KIDS)
_seed_falsification(db, cache_key=HAS_KIDS)
parser = build_parser()
args = parser.parse_args([
"--db", str(db),
"burn-kindergarten",
"--kindergarten-seconds", "3600",
])
rc = args.func(args)
assert rc == 0
payload = json.loads(capsys.readouterr().out)
assert payload["burned"] == 1
assert payload["refused_has_children"] == 1
def test_burn_cli_returns_non_zero_on_refused(tmp_path, capsys):
"""Non-zero exit when refused so scripts can detect and react."""
from aborist.cli import build_parser