feat(schema): self-heal databases left on an older schema without losing database-only data
Every command refused a tracker database on an older schema, and the reviewed migration only accepts schemas 13-18, so the unversioned schema-0 databases of early br / Go bd (and schemas 4/7/11) had no working path at all: even `br sync --import-only --rebuild` hit the same refusal. On one workstation 118 of 151 trackers were blocked. Startup now audits a stale database against issues.jsonl, schema-agnostic (plain SELECTs over whatever columns exist). An issue row is represented when the JSONL carries it as a tombstone, carries equivalent content, or carries a copy at least as new while the row was never marked dirty. - Everything represented: upgrade and continue, one stderr notice line. Schemas 13-18 use the reviewed migration (recovery bundle, undo); older ones rebuild from the audited JSONL snapshot, old family kept in .beads/.br_recovery. Explicitly read-only invocations never upgrade. - Anything database-only (absent from JSONL, unflushed edit, newer than JSONL, unreadable): mutations refuse, naming the issues and the single command `br doctor migrate-schema heal`; read-only commands read the JSONL directly (as --no-db) with clean JSON stdout. - New `br doctor migrate-schema heal [--dry-run] [--discard-db-only]`: migrates 13-18 in place, or rebuilds older schemas and re-adds the database-only issues as unflushed changes (#394 rule: superseded older edits stay in the backup only). Also: - Import verification now mirrors the orphan cleanup: a JSONL dependency edge to an issue that exists nowhere is dropped on import, but both post-import verifiers compared the raw payload (and the rebuild checked the raw dependency count), so every import or rebuild of such a JSONL failed; five real trackers carried such edges. - GH #520 follow-up: startup WAL-index recovery is gated on STARTUP_WAL_INDEX_RECOVERY (engine reads -shm AND the quarantine lock exists: linux/android/macos/ios), matching RecoveryLock::acquire, so FreeBSD and other Unix targets no longer enter a recovery that can only fail as unsupported. A new test pins the engine's reaction to the index stock SQLite writes (read-only BusyRecovery, filed as Dicklesworthstone/frankensqlite#431). - main records the process actor for the last-touched guard (GH #518). Tests: tests/e2e_schema_heal.rs with fixtures written by the released br 0.1.27 binary (schema 4) plus the shipped schema-16 fixture; unit tests for the classifier and legacy timestamp decoding; updated migration and chokepoint e2e expectations for the read-only fallback. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
J
Jeff Emanuel committed
91c61db30e39a390d0c48c3be8817fd0fc907cce
Parent: bbde2e4