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,42 @@
# BAT-002 Design
## Architecture Summary
BAT-002 adds a small operational-safety layer around the existing SQLite ledger without changing transaction schemas. The service layer produces metadata-only DB evidence, creates backups with SQLite's online backup API, lists backup files, and validates one backup file. MCP exposes only non-destructive backup/status operations. Restore remains a repo script/runbook workflow requiring explicit user approval before overwriting a target DB.
## Changed Areas
- `jarca_ledger_mcp/service.py`
- New constants: default backup directory and restore policy.
- New helpers: sha256, file metadata, read-only SQLite status, safe table counts, aggregate totals.
- New public functions: `get_ledger_db_status`, `create_ledger_backup`, `list_ledger_backups`, `get_ledger_backup_status`.
- `jarca_ledger_mcp/server.py`
- New additive MCP tools for backup/status/list/hash only.
- No restore MCP tool.
- `scripts/ledger_restore_smoke.py`
- Script-only restore smoke helper with explicit overwrite approval token.
- Docs/tests
- Synthetic tests and operational runbook/baseline docs.
## Key Decisions
1. Backup creation uses SQLite online backup API rather than raw copy so WAL-mode DBs are copied consistently.
2. Backup evidence returns metadata only: path, size, mtime, sha256, integrity result, table row counts, and aggregate totals by cashflow kind.
3. Retention policy implements "recent 30 days OR recent 20 files" by pruning only files that are both older than the retention window and outside the newest count.
4. Restore is deliberately excluded from MCP. The script exists for fixture/validation DB restore smoke and requires `RESTORE_APPROVED` for overwriting any existing target.
5. Existing success response shapes are preserved; all new response fields are additive under new tools/functions.
## Edge Cases
- Missing source DB: backup/status returns `not_found` rather than initializing or mutating a new DB.
- Empty backup directory: list returns success with zero backups.
- Invalid/corrupt backup: backup status includes integrity error metadata and restore smoke fails before copying.
- Existing target restore path: restore smoke fails unless `--allow-overwrite --approval-token RESTORE_APPROVED` are supplied.
- Sensitive row data: metadata collection never selects merchant, counterparty, account number, raw_payload, or raw financial file content.
## Architecture Impact
- runtime-flows: updated with backup/status and restore-restricted operational flow.
- data-model: updated with backup artifact and evidence policy notes.
- interface-contract: updated with new MCP tools and explicit no-restore-MCP rule.
## Reference Doc Update Plan
- Update `docs/runtime-flows.md` with operational backup/validation/restore-smoke flow and high-risk flow constraints.
- Update `docs/data-model.md` with backup artifact/evidence persistence boundary.
- Update `docs/interface-contract.md` with additive backup/status tools and restore non-exposure.
- Add `docs/runbooks/ledger-backup-restore.md`.
@@ -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.
@@ -0,0 +1,33 @@
# BAT-002: 운영 안전 백업·복구·검증 DB 워크플로우
## Metadata
- BAT ID: BAT-002
- Profile: standard
- Status: release-candidate
- Source Discovery: dcy-002
- Included REQ: REQ-002
- Created At (KST): 2026-06-27 10:17
- Owner: 렛저장인
## Scope
- Add metadata-only DB status, backup create/list/status helpers.
- Expose backup/status/list/hash as additive MCP tools.
- Keep restore execution out of MCP and document script/runbook approval path.
- Verify with synthetic DB tests and safe restore smoke.
- Update runtime, data-model, and interface baseline docs.
## Out of Scope
- 실제 운영 DB restore 실행.
- 원본 운영 DB 직접 mutation.
- 파괴적 SQLite migration 또는 대량 data rewrite.
- 신규 금융기관 parser, CI/CD 구축, 배포 서비스화.
## Documents
- [planning.md](planning.md)
- [design.md](design.md)
- [implementation.md](implementation.md)
- [verification.md](verification.md)
## Notes
- Restore is intentionally script/runbook only; no `restore_*` MCP tool should be added.
- Evidence must remain metadata-only and exclude merchant/counterparty full dumps, raw_payload, account numbers, passwords, and source financial files.
@@ -0,0 +1,34 @@
# BAT-002 Planning
## Included REQ
- REQ-002: 운영 안전 백업·복구·검증 DB 워크플로우
## Delivery Plan
1. Add service helpers for non-sensitive DB status evidence and SQLite online backup creation.
2. Add additive MCP tools for backup creation, backup list, backup status/hash, and DB status.
3. Add a non-MCP restore smoke script requiring explicit approval for overwriting an existing target DB.
4. Add synthetic DB tests for status evidence, backup/list/status/hash, retention, and restore smoke policy.
5. Update runtime/data/interface docs and add a runbook.
6. Run targeted/full verification and request jarcaledgerqc QC gate.
## Dependencies
- Existing SQLite initialization and additive migration behavior in `jarca_ledger_mcp/db.py`.
- Existing service response convention: `{"success": True, ...}` and `fail(...)` error shape.
- Synthetic pytest tmp DBs for safe evidence.
## Design Gate
Design required: yes.
Reason: this batch changes the MCP interface surface and runtime/data safety baseline, and must explicitly preserve restore non-exposure and sensitive evidence restrictions.
## Milestones
- M1: REQ-002 Approved and BAT-002 scaffolded.
- M2: Backup/status/list/hash helpers and MCP tools implemented.
- M3: Restore smoke script and runbook documented without MCP restore surface.
- M4: Synthetic tests and docs evidence complete.
- M5: QC gate PASS/PASS_WITH_NOTES, then REQ-002 Implemented.
## Risks
- Accidentally exposing restore as MCP would violate dcy-002 policy.
- SQLite WAL copy must be consistent; implementation should use SQLite online backup API rather than raw file copy for backup creation.
- Evidence must not serialize sensitive row-level details.
@@ -0,0 +1,111 @@
# BAT-002 Verification
## Verification Status
- Status: approved
- Ready for Release: true
- Verified At (KST): 2026-06-27 10:33
- Verified By: 렛저장인
## Acceptance Mapping
### REQ-002 AC1
Backup creation must write timestamped SQLite files under `/root/.local/share/jarca/backups/ledger/` by default and return source/backup path, size, mtime, sha256, integrity, and table counts.
Evidence:
- `service.create_ledger_backup` uses default `DEFAULT_LEDGER_BACKUP_DIR = ~/.local/share/jarca/backups/ledger` and returns `source_db`, `backup`, `retention`, and `restore_policy`.
- `tests/test_backup_workflow.py::test_create_list_and_status_ledger_backup_with_retention` verifies backup file creation, sha256 presence, `integrity_check == "ok"`, and source/backup table count equality on synthetic DB.
Result: pass.
### REQ-002 AC2
Retention keeps recent 30 days or newest 20 backup files and reports pruned paths.
Evidence:
- `_apply_backup_retention` prunes only backups that are both outside newest count and older than day cutoff.
- `tests/test_backup_workflow.py::test_backup_retention_keeps_recent_count_and_prunes_older_extras` verifies retention with synthetic old backup files and checks `pruned_paths` length.
Result: pass.
### REQ-002 AC3
Backup list/status/hash is additive service/MCP surface and existing response shapes are preserved.
Evidence:
- New service functions: `list_ledger_backups`, `get_ledger_backup_status`, `get_ledger_db_status`.
- New MCP tools are additive in `server.py`.
- `hermes mcp test jarca-ledger` result: connected, `Tools discovered: 45`, including `get_ledger_db_status`, `create_ledger_backup`, `list_ledger_backups`, `get_ledger_backup_status`.
Result: pass.
### REQ-002 AC4
Restore execution is not MCP-exposed and is script/runbook only with explicit approval for overwrite.
Evidence:
- `server.py` contains no restore MCP tool.
- `hermes mcp test jarca-ledger` discovered backup/status tools and no restore tool.
- `scripts/ledger_restore_smoke.py` requires `--allow-overwrite --approval-token RESTORE_APPROVED` before overwriting an existing target.
- `docs/runbooks/ledger-backup-restore.md` documents restore as script-only and warns not to target the live operating DB without explicit user approval.
Result: pass.
### REQ-002 AC5
Validation uses synthetic fixture DB first; operating DB copy evidence is metadata-only if used.
Evidence:
- All tests use pytest `tmp_path` synthetic SQLite DBs.
- Restore smoke command used `/tmp/.../fixture.sqlite3`, `/tmp/.../backups/...sqlite3`, and `/tmp/.../restored.sqlite3`.
- No operating DB copy was needed.
Result: pass.
### REQ-002 AC6
Baseline docs reflect backup/status/list/hash/restore restrictions and sensitive evidence policy.
Evidence:
- `docs/runtime-flows.md` adds operational backup/validation/restore smoke flow and restore non-MCP constraint.
- `docs/data-model.md` adds `LedgerBackupArtifact` and metadata-only evidence notes.
- `docs/interface-contract.md` adds backup/status tools and restore non-exposure compatibility rule.
- `docs/runbooks/ledger-backup-restore.md` lists allowed/forbidden evidence and QC checklist.
Result: pass.
### REQ-002 AC7
Synthetic tests verify backup/status/list/hash/retention/restore smoke or health/status check without operating DB mutation.
Evidence:
- Command: `source .venv/bin/activate && pytest -ra`
- Result: `20 passed, 4 skipped in 16.02s`.
- Command: synthetic restore smoke with `scripts/ledger_restore_smoke.py`.
- Result: `success: true`, backup/target `integrity_check: ok`, matching table counts, `health_check.success: true`.
Result: pass.
### REQ-002 AC8
QC gate must verify operating DB mutation absence, restore MCP non-exposure, sensitive evidence absence, smoke evidence, tests/docs.
Evidence:
- This verification document and runbook include QC checklist.
- `jarcaledgerqc` QC gate returned `PASS`.
- QC summary confirmed no operating DB restore/direct mutation, backup/status/list/hash policy compliance, no restore MCP tool, synthetic restore smoke success, no real sensitive evidence exposure, and tests/docs evidence.
Result: pass.
## Evidence Summary
- Syntax: `python -m py_compile jarca_ledger_mcp/*.py dashboard/app.py` -> exit 0.
- Targeted tests: `pytest tests/test_backup_workflow.py -q` -> `3 passed`.
- Full tests: `pytest -ra` -> implementer `20 passed, 4 skipped in 16.02s`; QC rerun `20 passed, 4 skipped in 14.14s`.
- Restore smoke: synthetic tmp DB restore -> `success: true`, `integrity_check: ok`, table counts `{accounts: 1, transactions: 1, budgets: 0, import_batches: 0, category_rules: 0, ledger_categories: 0}`.
- MCP surface: `hermes mcp test jarca-ledger` -> connected, `Tools discovered: 45`, backup/status tools present, no restore tool listed.
## Sensitive Evidence Review
- No transaction row dumps, merchant/counterparty full dumps, account numbers, `raw_payload`, source financial files, or passwords were written into docs/tests/logs.
- Synthetic test data contains placeholder sentinel labels only and asserts those labels are not serialized in status evidence.
## QC Gate Result
- Verdict: PASS
- Reviewer: jarcaledgerqc
- Reviewed At (KST): 2026-06-27 10:33
- Summary: 검수관은 원본 운영 DB restore/direct mutation 없음, backup/status/list/hash 정책 정합성, restore MCP 비노출, synthetic restore smoke 성공, 민감정보 비노출, tests/docs evidence를 확인했다.
- Notes: 실제 backup MCP 호출은 운영 DB mutation을 피하기 위해 수행하지 않았고, service-level synthetic DB 테스트와 smoke로 대체 검증했다. 이는 비차단 참고사항이다.
## Blocking Issues
- None.
+1
View File
@@ -9,3 +9,4 @@
| BAT ID | Profile | Status | Included REQ | Discovery | Folder |
| --- | --- | --- | --- | --- | --- |
| BAT-001 | batch-lite | release-candidate | REQ-001 | dcy-001 | [bat-001_20260626_category-rule-ops](bat-001_20260626_category-rule-ops/) |
| BAT-002 | standard | release-candidate | REQ-002 | dcy-002 | [bat-002_20260627_operational-safety-backup-restore-validation-db](bat-002_20260627_operational-safety-backup-restore-validation-db/) |