feat: implement asset snapshot and manual asset read model
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# BAT-008 Design
|
||||
|
||||
## Schema and persistence design
|
||||
|
||||
- Add `manual_assets` as a first-class table for non-transaction-derived assets/liabilities.
|
||||
- Add `asset_snapshots` as an append-only history table keyed by basis date plus evaluated item identity.
|
||||
- Migrations remain additive/idempotent; existing ledger/account flows must continue to operate when both tables are empty.
|
||||
|
||||
## Authority-source and valuation design
|
||||
|
||||
- Account-backed assets use `accounts.last_balance` as the live authority source.
|
||||
- Non-transaction assets/liabilities use `manual_assets` as the live authority source.
|
||||
- `asset_snapshots` is a historical record layer, not a competing primary input source.
|
||||
- Net worth formula remains: asset buckets (`cash`, `investment`, `deposit`, `real_estate`, `other`) minus `loan`; missing valuations are excluded, not inferred.
|
||||
|
||||
## Read model design
|
||||
|
||||
- Service layer should expose:
|
||||
- manual assets list/report helpers
|
||||
- basis-date asset snapshot read helpers
|
||||
- net-worth timeseries helpers
|
||||
- shared valuation/fallback helpers consumed by both MCP and dashboard
|
||||
- Snapshot read responses must expose basis date, bucket totals, liabilities, net worth, and inclusion/exclusion explanation fields.
|
||||
- Manual assets read responses must expose item name, value, basis date, `liquidity_bucket`, and memo/source metadata.
|
||||
|
||||
## Dashboard alignment design
|
||||
|
||||
- `dashboard/app.py` should consume the same valuation helpers as the MCP read tools.
|
||||
- If no manual assets/snapshots exist, dashboard behavior must safely fall back to REQ-005 account-based display.
|
||||
- Excluded/unvalued items should be explainable instead of silently counted.
|
||||
|
||||
## Verification design
|
||||
|
||||
- Add targeted regression tests for:
|
||||
- additive migration creation
|
||||
- manual asset persistence and retrieval
|
||||
- snapshot accumulation/read behavior
|
||||
- net-worth formula / exclusion policy
|
||||
- dashboard/MCP shared fallback behavior
|
||||
- Prefer synthetic SQLite fixtures and temp DBs only.
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
# BAT-008 Implementation
|
||||
|
||||
## Plan summary
|
||||
|
||||
BAT-008 implemented the new valuation authority model in `service.py`/`db.py`, exposed read-only MCP tools in `server.py`, aligned the Streamlit overview with the shared valuation helper, and updated baseline docs to match the confirmed Discovery/REQ scope.
|
||||
|
||||
## Changed files
|
||||
|
||||
- `jarca_ledger_mcp/db.py`
|
||||
- `SCHEMA_VERSION` → `9`
|
||||
- additive/idempotent tables: `manual_assets`, `asset_snapshots`
|
||||
- migration path updated so existing DBs create the new tables safely
|
||||
- `jarca_ledger_mcp/service.py`
|
||||
- shared valuation helpers for account/manual-asset/snapshot authority sources
|
||||
- new service APIs: `create_manual_asset`, `list_manual_assets`, `create_asset_snapshot`, `get_asset_snapshot_report`, `get_net_worth_timeseries`
|
||||
- `get_asset_liquidity_report` aligned to the same valuation policy while preserving REQ-005 fallback semantics
|
||||
- `jarca_ledger_mcp/server.py`
|
||||
- new read MCP tools: `list_manual_assets`, `get_asset_snapshot_report`, `get_net_worth_timeseries`
|
||||
- `get_asset_liquidity_report` contract text updated for shared valuation source behavior
|
||||
- `dashboard/app.py`
|
||||
- overview asset section now explains the valuation authority source and shows `manual_assets` rows when present
|
||||
- existing account-only fallback remains when new tables are empty
|
||||
- Documentation
|
||||
- `docs/data-model.md`
|
||||
- `docs/interface-contract.md`
|
||||
- `docs/runtime-flows.md`
|
||||
- `docs/project-structure.md`
|
||||
- Tests
|
||||
- new: `tests/test_asset_snapshots.py`
|
||||
- adjusted: `tests/test_service.py`
|
||||
|
||||
## Execution log
|
||||
|
||||
- 2026-06-30 21:26 KST: BAT-008 standard batch scaffold created from approved REQ-009/REQ-010.
|
||||
- 2026-06-30 21:3x~21:4x KST: implementation landed in `db.py`, `service.py`, `server.py`, `dashboard/app.py`, docs, and regression tests via `jarcaledgerdev` handoff + local verification orchestration.
|
||||
- 2026-06-30 21:40 KST: targeted compile/test validation passed.
|
||||
- 2026-06-30 21:41 KST: MCP stdio contract smoke check passed and discovered the three new read tools.
|
||||
- 2026-06-30 21:42 KST: synthetic end-to-end service probe confirmed live-authority fallback, snapshot read behavior, and net-worth timeseries output.
|
||||
|
||||
## Validation
|
||||
|
||||
### Command 1
|
||||
```bash
|
||||
cd /root/projects/jarca-ledger-mcp
|
||||
source .venv/bin/activate
|
||||
python -m py_compile jarca_ledger_mcp/*.py dashboard/app.py
|
||||
pytest tests/test_asset_snapshots.py tests/test_service.py tests/test_contract_regression.py -q
|
||||
```
|
||||
|
||||
Result:
|
||||
- `................. [100%]`
|
||||
- exit code `0`
|
||||
|
||||
### Command 2
|
||||
```bash
|
||||
cd /root/projects/jarca-ledger-mcp
|
||||
source .venv/bin/activate
|
||||
hermes mcp test jarca-ledger
|
||||
```
|
||||
|
||||
Result:
|
||||
- stdio connection success
|
||||
- tools discovered: `51`
|
||||
- new BAT-008 read tools discovered:
|
||||
- `list_manual_assets`
|
||||
- `get_asset_snapshot_report`
|
||||
- `get_net_worth_timeseries`
|
||||
|
||||
### Command 3
|
||||
```bash
|
||||
cd /root/projects/jarca-ledger-mcp
|
||||
source .venv/bin/activate
|
||||
python - <<'PY'
|
||||
from tempfile import TemporaryDirectory
|
||||
from pathlib import Path
|
||||
from jarca_ledger_mcp import service
|
||||
|
||||
with TemporaryDirectory() as td:
|
||||
db_path = str(Path(td) / 'ledger.sqlite3')
|
||||
service.create_ledger_account('현금', last_balance=1000000, last_synced_at='2026-06-30', liquidity_bucket='cash', db_path=db_path)
|
||||
service.create_ledger_account('투자', type='investment', last_balance=2000000, liquidity_bucket='investment', db_path=db_path)
|
||||
service.create_manual_asset('전세보증금', 300000000, '2026-06-30', 'real_estate', source_note='synthetic appraisal', db_path=db_path)
|
||||
service.create_manual_asset('주담대', 120000000, '2026-06-30', 'loan', source_note='synthetic loan balance', db_path=db_path)
|
||||
print(service.list_manual_assets(db_path=db_path))
|
||||
print(service.get_asset_snapshot_report('2026-06-30', db_path=db_path))
|
||||
service.create_asset_snapshot('2026-05-31', 'account', 'acct-a', '현금', 1000000, 'cash', source_note='synthetic', db_path=db_path)
|
||||
service.create_asset_snapshot('2026-05-31', 'manual_asset', 'manual-home', '부동산', 200000000, 'real_estate', source_note='synthetic', db_path=db_path)
|
||||
service.create_asset_snapshot('2026-05-31', 'manual_asset', 'manual-loan', '대출', 80000000, 'loan', source_note='synthetic', db_path=db_path)
|
||||
print(service.get_asset_snapshot_report('2026-05-31', db_path=db_path))
|
||||
print(service.get_net_worth_timeseries(start_date='2026-05-01', end_date='2026-06-30', db_path=db_path))
|
||||
PY
|
||||
```
|
||||
|
||||
Key result excerpts:
|
||||
- `list_manual_assets` returned `count=2` and `authority_source='manual_assets'`
|
||||
- live fallback snapshot report returned `source='live_authority'`, `total_assets=303000000`, `total_liabilities=120000000`, `net_worth=183000000`
|
||||
- stored snapshot report returned `source='asset_snapshots'`, `snapshot_count=3`, `net_worth=121000000`
|
||||
- timeseries returned `points[0].basis_date='2026-05-31'`, `points[0].net_worth=121000000`
|
||||
|
||||
## Remaining risks / follow-up notes
|
||||
|
||||
- Snapshot creation is intentionally service-only in BAT-008. MCP mutation exposure remains future scope by Discovery decision.
|
||||
- Manual-asset edit/delete workflows and month-end operator UX are still procedural/document-driven rather than exposed as first-class CRUD flows.
|
||||
- QC gate still needs to confirm REQ Implemented promotion before commit-stage handoff.
|
||||
@@ -0,0 +1,36 @@
|
||||
# BAT-008 Asset snapshots / manual assets / read MCP
|
||||
|
||||
- Profile: standard
|
||||
- Status: release-candidate
|
||||
- Included REQ: REQ-009, REQ-010
|
||||
- Discovery: dcy-008
|
||||
- Owner: 렛저장인
|
||||
- Date (KST): 2026-06-30
|
||||
|
||||
## 목표
|
||||
|
||||
REQ-009/REQ-010에 따라 `asset_snapshots`/`manual_assets` 저장 모델을 additive/idempotent하게 도입하고, 월말 수동 스냅샷 정책을 수용하는 집계 계층과 조회성 MCP(기준일 스냅샷/순자산 시계열/수기 자산 조회)를 구현한다. 동시에 대시보드 자산/순자산 표시가 동일 권위 소스와 동일 산식을 사용하도록 정렬한다.
|
||||
|
||||
## 산출물
|
||||
|
||||
- `asset_snapshots`, `manual_assets` additive/idempotent schema migration
|
||||
- 수기 자산 저장/조회, 스냅샷 누적, 순자산 시계열 집계 service 함수
|
||||
- 조회성 MCP tool/응답 계약: 기준일 자산 스냅샷, 순자산 시계열, 수기 자산 조회
|
||||
- `dashboard/app.py`의 자산/순자산 표시가 신규 모델과 안전 폴백을 함께 지원하도록 정렬
|
||||
- `docs/data-model.md`, `docs/interface-contract.md`, `docs/project-structure.md`, `docs/runtime-flows.md` baseline 갱신
|
||||
- BAT-008 planning/design/implementation/verification 문서와 synthetic-only 검증 evidence
|
||||
|
||||
## 범위 제한
|
||||
|
||||
- 외부 시세/부동산 자동 평가 연동
|
||||
- 스냅샷 생성 mutation MCP
|
||||
- 수기 자산 생성/수정/삭제 mutation MCP
|
||||
- 투자 조언/리밸런싱/세금/수익률 계산
|
||||
- 운영 DB 실데이터 기반 evidence
|
||||
|
||||
## 문서
|
||||
|
||||
- `planning.md`: delivery plan, dependency, risk, validation entry criteria
|
||||
- `design.md`: schema/authority-source/fallback/read-contract design
|
||||
- `implementation.md`: changed files, execution log, validation commands
|
||||
- `verification.md`: AC mapping, PASS/FAIL, REQ Implemented decision
|
||||
@@ -0,0 +1,37 @@
|
||||
# BAT-008 Planning
|
||||
|
||||
## Scope
|
||||
|
||||
- Implement REQ-009: `asset_snapshots`/`manual_assets` valuation model, authority-source rule, month-end manual snapshot policy support.
|
||||
- Implement REQ-010: asset snapshot read MCP, net-worth timeseries read MCP, manual assets read MCP, dashboard/MCP alignment.
|
||||
- Preserve REQ-005 safety fallback when new tables are empty.
|
||||
- Use synthetic/temp data only for tests and verification evidence.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- dcy-008 confirmed Discovery and approved REQ-009/REQ-010.
|
||||
- Existing REQ-005 dashboard liquidity/net-worth flow and `accounts.last_balance` authority-source baseline.
|
||||
- Existing MCP server/service pattern in `jarca_ledger_mcp/server.py`, `jarca_ledger_mcp/service.py`, `jarca_ledger_mcp/db.py`.
|
||||
- Additive/idempotent SQLite migration discipline and synthetic-only evidence policy.
|
||||
|
||||
## Work plan
|
||||
|
||||
1. Create BAT-008 standard batch docs and register entry.
|
||||
2. Add schema/model support for `manual_assets` and `asset_snapshots` with safe empty-table fallback.
|
||||
3. Add service-layer valuation helpers, snapshot accumulation/report functions, and dashboard alignment logic.
|
||||
4. Add read-only MCP tool endpoints/contracts for snapshot read, net-worth timeseries, and manual assets read.
|
||||
5. Update baseline docs (`docs/data-model.md`, `docs/interface-contract.md`, `docs/project-structure.md`, `docs/runtime-flows.md`).
|
||||
6. Run narrow validation first (targeted tests / lint / smoke as applicable), then broader regression if narrow gates pass.
|
||||
7. If evidence satisfies ACs, move BAT-008 to release-candidate and REQ-009/REQ-010 to Implemented.
|
||||
|
||||
## Design Gate
|
||||
|
||||
- Result: standard batch 유지.
|
||||
- Reason: schema, service, MCP contract, dashboard alignment, baseline docs를 함께 변경하며 interface/data/fallback 설계가 필요하다.
|
||||
|
||||
## Risks and mitigations
|
||||
|
||||
- Authority-source drift between dashboard and MCP: mitigate by centralizing valuation helpers and writing shared regression tests.
|
||||
- Empty new tables breaking existing REQ-005 flows: mitigate by explicit fallback tests and additive migrations only.
|
||||
- Double counting between account-backed assets and manual assets: mitigate by clearly separating source responsibility and documenting inclusion/exclusion rules.
|
||||
- Snapshot history ambiguity: mitigate by storing basis date, source type, and memo for every snapshot row.
|
||||
@@ -0,0 +1,64 @@
|
||||
# BAT-008 Verification
|
||||
|
||||
## Verification summary
|
||||
|
||||
Status: PASS
|
||||
|
||||
Evidence collected against synthetic/temp data only. No production ledger DB mutation or real financial files were used.
|
||||
|
||||
## Acceptance criteria mapping
|
||||
|
||||
### REQ-009
|
||||
|
||||
- AC-1: **PASS**
|
||||
- Evidence: `tests/test_asset_snapshots.py::test_asset_snapshot_schema_is_additive_and_idempotent`
|
||||
- `db.py` adds `manual_assets` and `asset_snapshots` via additive/idempotent schema + migration path.
|
||||
- AC-2: **PASS**
|
||||
- Evidence: `create_manual_asset`/`list_manual_assets` service path + synthetic probe output includes `name`, `valuation_amount`, `valuation_date`, `liquidity_bucket`, `source_note`.
|
||||
- AC-3: **PASS**
|
||||
- Evidence: `create_asset_snapshot` stores basis date/source target/amount/bucket/source note; `test_snapshot_rows_are_append_only_and_timeseries_uses_same_formula` proves append-only readback.
|
||||
- AC-4: **PASS**
|
||||
- Evidence: `valuation_policy` and `authority_source` fields distinguish `accounts.last_balance`, `manual_assets`, and `asset_snapshots`; docs updated in `docs/data-model.md` and `docs/runtime-flows.md`.
|
||||
- AC-5: **PASS**
|
||||
- Evidence: valuation helper formula is `asset_buckets - loan`; synthetic probe returned `303000000 - 120000000 = 183000000`; unvalued items are excluded rather than inferred.
|
||||
- AC-6: **PASS**
|
||||
- Evidence: `tests/test_asset_snapshots.py::test_asset_liquidity_report_empty_new_tables_preserves_req005_fallback` confirms empty new tables preserve REQ-005 path with `source='accounts.last_balance'`.
|
||||
- AC-7: **PASS**
|
||||
- Evidence: `docs/data-model.md`, `docs/runtime-flows.md`, and `docs/project-structure.md` now document the new tables, authority-source rule, and month-end snapshot flow.
|
||||
|
||||
### REQ-010
|
||||
|
||||
- AC-1: **PASS**
|
||||
- Evidence: `server.py` exposes `get_asset_snapshot_report`; synthetic probe returned `basis_date`, per-bucket totals, liabilities, net worth, included/excluded items.
|
||||
- AC-2: **PASS**
|
||||
- Evidence: `server.py` exposes `get_net_worth_timeseries`; service/test coverage proves identical formula per basis date.
|
||||
- AC-3: **PASS**
|
||||
- Evidence: `server.py` exposes `list_manual_assets`; synthetic probe returned name/value/date/bucket/source note payloads.
|
||||
- AC-4: **PASS**
|
||||
- Evidence: `dashboard/app.py` caption explicitly states shared valuation helper; `service.py` centralizes calculation for dashboard + MCP.
|
||||
- AC-5: **PASS**
|
||||
- Evidence: same fallback regression test as REQ-009 AC-6 plus `get_asset_liquidity_report` preserves existing keys while adding optional explanatory fields.
|
||||
- AC-6: **PASS**
|
||||
- Evidence: `docs/interface-contract.md`, `docs/runtime-flows.md`, and `docs/project-structure.md` updated for read MCP contract and dashboard/MCP alignment.
|
||||
|
||||
## Requirement state decision
|
||||
|
||||
- REQ-009: Implemented promotion confirmed.
|
||||
- REQ-010: Implemented promotion confirmed.
|
||||
|
||||
## Validation evidence
|
||||
|
||||
1. `python -m py_compile jarca_ledger_mcp/*.py dashboard/app.py` → pass
|
||||
2. `pytest tests/test_asset_snapshots.py tests/test_service.py tests/test_contract_regression.py -q` → pass (`................. [100%]`)
|
||||
3. `hermes mcp test jarca-ledger` → pass; stdio connected, 51 tools discovered, including the three new BAT-008 read tools
|
||||
4. synthetic service probe → pass; confirmed live-authority fallback, stored-snapshot read path, and net-worth timeseries output
|
||||
|
||||
## Remaining issues
|
||||
|
||||
### Hard blockers
|
||||
- None identified from the collected implementation evidence.
|
||||
|
||||
### Non-blocking follow-up
|
||||
- jarcaledgerqc final re-check returned: `PASS, REQ-009/REQ-010은 이제 Implemented로 전환해도 됩니다.`
|
||||
- fresh Streamlit browser smoke on `http://127.0.0.1:8511` rendered `schema v9`, `자산 유동성 현황`, and produced no browser console JS errors.
|
||||
- Snapshot creation/manual-asset CRUD MCP mutations remain intentionally out of scope for BAT-008.
|
||||
@@ -15,3 +15,4 @@
|
||||
| BAT-005 | standard | release-candidate | REQ-005 | dcy-005 | [bat-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow](bat-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow/) |
|
||||
| BAT-006 | standard | release-candidate | REQ-006 | dcy-006 | [bat-006_20260630_pytest-apptest-smoke-ci-automation](bat-006_20260630_pytest-apptest-smoke-ci-automation/) |
|
||||
| BAT-007 | standard | release-candidate | REQ-007, REQ-008 | dcy-007 | [bat-007_20260630_mcp-response-contract-ci-versioning](bat-007_20260630_mcp-response-contract-ci-versioning/) |
|
||||
| BAT-008 | standard | release-candidate | REQ-009, REQ-010 | dcy-008 | [bat-008_20260630_asset-snapshots-manual-assets-read-mcp](bat-008_20260630_asset-snapshots-manual-assets-read-mcp/) |
|
||||
|
||||
Reference in New Issue
Block a user