feat: add ledger backup safety workflow

confirm dcy-002 and add REQ/BAT evidence

add ledger backup/status metadata workflow

document restore as runbook/script-only guarded operation

add synthetic DB backup/restore smoke tests
This commit is contained in:
Jarca
2026-06-27 10:40:06 +09:00
parent 0f881b245f
commit 67d8d4871b
19 changed files with 1035 additions and 8 deletions
@@ -0,0 +1,62 @@
# BAT-002 Implementation
## Plan Summary
Implemented REQ-002 as a staged additive operational-safety change. No transaction schema migration, operating DB restore, or broad data rewrite was performed.
## Changed Files
- `jarca_ledger_mcp/service.py`
- Added metadata-only DB status helpers.
- Added SQLite online backup creation with retention enforcement.
- Added backup list/status/hash functions.
- Added explicit restore policy metadata (`mcp_restore_exposed: false`).
- `jarca_ledger_mcp/server.py`
- Added MCP tools: `get_ledger_db_status`, `create_ledger_backup`, `list_ledger_backups`, `get_ledger_backup_status`.
- Did not add any restore MCP tool.
- `scripts/ledger_restore_smoke.py`
- Added script-only restore smoke helper with overwrite approval token.
- `tests/test_backup_workflow.py`
- Added synthetic DB tests for metadata-only status, backup create/list/status/hash, retention pruning, and sensitive evidence exclusion.
- `docs/runbooks/ledger-backup-restore.md`
- Added operator runbook and QC checklist.
- `docs/runtime-flows.md`
- Added operational backup/validation/restore smoke runtime flow.
- `docs/data-model.md`
- Added `LedgerBackupArtifact` and metadata-only persistence notes.
- `docs/interface-contract.md`
- Added backup/status MCP contract and restore non-exposure rule.
- StagePilot docs
- `docs/srs/Non-Functional/req-002_operational-safety-backup-restore-validation-db.md`
- `docs/srs/index.md`
- `docs/discovery/dcy-002_20260627_operational-safety-backup-restore-validation-db.md`
- `docs/batches/index.md`
- `docs/batches/bat-002_20260627_operational-safety-backup-restore-validation-db/*`
## Execution Log
- RED test first:
- Command: `source .venv/bin/activate && pytest tests/test_backup_workflow.py -q`
- Initial result: failed with missing `service.get_ledger_db_status`, `create_ledger_backup`, `list_ledger_backups`, `get_ledger_backup_status` attributes.
- Implemented service/server/script/docs changes.
- Targeted test:
- Command: `source .venv/bin/activate && pytest tests/test_backup_workflow.py -q`
- Result: `3 passed`.
- Full syntax/test:
- Command: `source .venv/bin/activate && python -m py_compile jarca_ledger_mcp/*.py dashboard/app.py && pytest tests/test_backup_workflow.py -q && pytest -q`
- Result: backup tests passed, full suite passed with `...........ss.........ss [100%]`.
- Full pytest summary:
- Command: `source .venv/bin/activate && pytest -ra`
- Result: `20 passed, 4 skipped in 16.02s`; skips are sample KB/Toss PDF tests without sample files/passwords.
- Restore smoke:
- Command: created synthetic tmp DB, created backup via `service.create_ledger_backup`, then ran `python scripts/ledger_restore_smoke.py --backup <tmp backup> --target <tmp restored.sqlite3>`.
- Result: `success: true`, backup and restored target `integrity_check: ok`, table counts `{accounts: 1, transactions: 1, budgets: 0, import_batches: 0, category_rules: 0, ledger_categories: 0}`, health check `active_transactions: 1`.
- MCP surface check:
- Command: `source .venv/bin/activate && hermes mcp test jarca-ledger`
- Result: connected, `Tools discovered: 45`, including backup/status tools and no restore tool.
## Validation
- Synthetic DB tests confirmed no operating DB is needed for backup/status/list/hash verification.
- Restore smoke used `/tmp/...` source/target only, not `/root/.local/share/jarca/ledger.sqlite3`.
- Evidence captured only path, size, mtime, sha256, integrity, row counts, aggregate totals, and health/test results.
## Remaining Risks
- Real operating DB restore remains intentionally untested and requires separate explicit user approval.
- Browser/UI copy is not changed in this batch; operator safety guidance is runbook/MCP/script level only.