refactor(jarca-ledger): implement BAT-010 backend core layering

This commit is contained in:
Jarca
2026-07-03 07:03:17 +09:00
parent 02380a88ab
commit 7ac25a6cf7
36 changed files with 4892 additions and 4113 deletions
@@ -0,0 +1,50 @@
# BAT-010 Design
## Layering design
- `jarca_ledger_mcp/application/shared.py`
- 공통 helper, validation, clock, dedupe, parser imports, shared policy를 보유한다.
- `jarca_ledger_mcp/application/system_services.py`
- health/status/backup/account create-list 등 system/persistence-adjacent service를 담당한다.
- `jarca_ledger_mcp/application/asset_account_services.py`
- account update, manual asset, snapshot, liquidity/net-worth read/write helper를 담당한다.
- `jarca_ledger_mcp/application/transaction_report_services.py`
- transaction CRUD, budgets, monthly/category/fixed-cost/reporting helper를 담당한다.
- `jarca_ledger_mcp/application/import_transfer_services.py`
- import preview/import, self-transfer/cancel reconciliation, cross-transfer matching을 담당한다.
- `jarca_ledger_mcp/application/categorization_services.py`
- category rules, preview/apply, uncategorized workbench, ledger categories를 담당한다.
## Persistence design
- `jarca_ledger_mcp/infrastructure/sqlite/connection.py`
- DB path resolution, sqlite connect, `db()` context manager를 담당한다.
- `jarca_ledger_mcp/infrastructure/sqlite/schema.py`
- schema version, DDL, additive column maps를 보유한다.
- `jarca_ledger_mcp/infrastructure/sqlite/migrations.py`
- idempotent/additive migration과 `init_db()`를 담당한다.
- top-level `jarca_ledger_mcp/db.py`는 compatibility shim으로 유지한다.
## Adapter / gateway design
- `jarca_ledger_mcp/adapters/mcp/server.py`
- 실제 FastMCP registration 위치.
- top-level `jarca_ledger_mcp/server.py`
- CLI entrypoint shim.
- `jarca_ledger_mcp/adapters/streamlit/service_gateway.py`
- dashboard가 실제로 사용하는 service surface만 재노출한다.
- `dashboard/app.py`
- gateway import를 통해 backend core를 호출한다.
## Compatibility design
- top-level `service.py`는 public API를 그대로 재노출한다.
- monkeypatch 관성을 위해 `service._current_kst_datetime` 변경이 shared/application modules에 전파되도록 module proxy를 유지한다.
- 기존 tests/import callers는 `from jarca_ledger_mcp import service` / `from jarca_ledger_mcp.db import init_db` 경로를 그대로 쓴다.
## Verification design
- `python -m py_compile`로 새 package tree syntax check.
- `pytest -q`로 full regression 및 AppTest 포함 회귀 검증.
- `hermes mcp test jarca-ledger`로 MCP tool discovery/registration 확인.
- Browser smoke로 synthetic fixture DB 기반 `요약`, `자산/마감` 렌더 및 console JS error 유무 확인.
@@ -0,0 +1,61 @@
# BAT-010 Implementation
## Plan summary
BAT-010 implements the confirmed dcy-010 Phase 0~3 scope by extracting application services, SQLite infrastructure, and thin MCP/Streamlit adapter seams while preserving legacy import paths and response contracts.
## Changed files
- `jarca_ledger_mcp/service.py`
- `jarca_ledger_mcp/db.py`
- `jarca_ledger_mcp/server.py`
- `jarca_ledger_mcp/application/__init__.py`
- `jarca_ledger_mcp/application/shared.py`
- `jarca_ledger_mcp/application/system_services.py`
- `jarca_ledger_mcp/application/asset_account_services.py`
- `jarca_ledger_mcp/application/transaction_report_services.py`
- `jarca_ledger_mcp/application/import_transfer_services.py`
- `jarca_ledger_mcp/application/categorization_services.py`
- `jarca_ledger_mcp/infrastructure/__init__.py`
- `jarca_ledger_mcp/infrastructure/sqlite/__init__.py`
- `jarca_ledger_mcp/infrastructure/sqlite/connection.py`
- `jarca_ledger_mcp/infrastructure/sqlite/schema.py`
- `jarca_ledger_mcp/infrastructure/sqlite/migrations.py`
- `jarca_ledger_mcp/adapters/__init__.py`
- `jarca_ledger_mcp/adapters/mcp/__init__.py`
- `jarca_ledger_mcp/adapters/mcp/server.py`
- `jarca_ledger_mcp/adapters/streamlit/__init__.py`
- `jarca_ledger_mcp/adapters/streamlit/service_gateway.py`
- `dashboard/app.py`
- `docs/discovery/dcy-010_20260702_backend-core-layering-mcp-adapter-realignment.md`
- `docs/project-structure.md`
- `docs/runtime-flows.md`
- `docs/srs/index.md`
- `docs/srs/Non-Functional/req-013_backend-core-layering-sqlite-infrastructure-service-facade-compatibility.md`
- `docs/srs/Interface/req-014_mcp-streamlit-adapter-seam-realignment-contract-preserving-gateway.md`
- `docs/batches/index.md`
- `docs/batches/bat-010_20260703_backend-core-layering-phase-0-3/index.md`
- `docs/batches/bat-010_20260703_backend-core-layering-phase-0-3/planning.md`
- `docs/batches/bat-010_20260703_backend-core-layering-phase-0-3/design.md`
- `docs/batches/bat-010_20260703_backend-core-layering-phase-0-3/implementation.md`
- `docs/batches/bat-010_20260703_backend-core-layering-phase-0-3/verification.md`
## Execution log
- 2026-07-03 00:15 KST: BAT-010 standard batch scaffold and REQ-013/REQ-014 문서를 생성하고 승인 상태로 정리했다.
- 2026-07-03 00:17 KST: `service.py` giant implementation을 application modules로 분리하고 top-level facade compatibility / monkeypatch proxy를 추가했다.
- 2026-07-03 00:17 KST: `db.py` responsibilities를 `infrastructure/sqlite`로 분리하고 기존 init/commit/rollback semantics를 유지하는 shim을 추가했다.
- 2026-07-03 00:18 KST: MCP adapter package, Streamlit gateway, baseline docs를 반영하고 regression fixes(`SCHEMA_VERSION import`, `db()` init semantics, underscore helper imports, service monkeypatch proxy)를 정리했다.
## Validation
- `python -m py_compile jarca_ledger_mcp/*.py jarca_ledger_mcp/application/*.py jarca_ledger_mcp/infrastructure/sqlite/*.py jarca_ledger_mcp/adapters/mcp/*.py jarca_ledger_mcp/adapters/streamlit/*.py dashboard/app.py` → PASS
- `pytest -q` → PASS (`42 passed, 4 skipped`)
- `hermes mcp test jarca-ledger` → PASS (`Connected`, `Tools discovered: 56`)
- Browser smoke: synthetic fixture DB(`/tmp/fixture.sqlite3`)로 Streamlit `요약`과 `자산/마감` 페이지 렌더 확인, console JS errors 없음.
## Remaining risks / follow-up notes
- Phase 4+ write/import/advanced workflow의 더 세밀한 domain/service 분리는 아직 TODO다.
- Streamlit gateway는 현재 필요한 surface만 노출하지만, 후속 phase에서 page/domain 기준으로 더 세분화할 수 있다.
- application modules는 여전히 큰 편이므로 후속 phase에서 deeper repository/use-case split이 필요하다.
@@ -0,0 +1,37 @@
# BAT-010 Backend core layering (Phase 0~3)
- Profile: standard
- Status: release-candidate
- Included REQ: REQ-013, REQ-014
- Discovery: dcy-010
- Owner: 렛저장인
- Date (KST): 2026-07-03
## 목표
REQ-013/REQ-014에 따라 `service.py` giant implementation과 `db.py` persistence 집중을 application / infrastructure / adapter seam으로 분리한다. 동시에 top-level facade/shim 호환, MCP tool contract, Streamlit 핵심 화면, additive migration discipline을 유지해 Phase 4+ 분리의 안전한 기반을 만든다.
## 산출물
- `jarca_ledger_mcp/application/` phase-0~3 service modules + shared helpers
- `jarca_ledger_mcp/infrastructure/sqlite/` connection/schema/migration split
- `jarca_ledger_mcp/adapters/mcp/server.py` + top-level `server.py` shim
- `jarca_ledger_mcp/adapters/streamlit/service_gateway.py` + dashboard import seam 정렬
- `service.py` / `db.py` compatibility facade & shim
- full pytest / MCP discovery / browser smoke evidence
- `docs/project-structure.md`, `docs/runtime-flows.md` baseline updates
- BAT-010 planning/design/implementation/verification documents
## 범위 제한
- Phase 4+ transactional write/import/advanced workflow 분리
- 신규 HTTP adapter 추가
- response envelope, tool names, dashboard IA 재설계
- destructive DB migration
## 문서
- `planning.md`: Phase 0~3 delivery plan, risks, validation gate
- `design.md`: layering/seam/shim design
- `implementation.md`: changed files, execution log, validation commands
- `verification.md`: AC mapping, PASS/FAIL, REQ Implemented decision
@@ -0,0 +1,34 @@
# BAT-010 Planning
## Scope
- Implement REQ-013: application/infrastructure split, SQLite persistence extraction, service/db compatibility facade.
- Implement REQ-014: MCP adapter split, Streamlit service gateway, contract-preserving verification.
- Update architecture baseline docs to reflect the new layering and remaining Phase 4+ TODOs.
- Use synthetic/temp data only for verification evidence.
## Dependencies
- dcy-010 confirmed Discovery and approved/implemented REQ-013/REQ-014.
- Existing BAT-009 runtime behavior, response contract expectations, and Streamlit dashboard pages.
- Additive/idempotent SQLite migration discipline from REQ-002.
## Work plan
1. Split `service.py` implementation into application modules and preserve top-level facade compatibility.
2. Split `db.py` responsibilities into `infrastructure/sqlite` and preserve existing import paths.
3. Introduce MCP adapter package and Streamlit service gateway.
4. Update baseline docs (`docs/project-structure.md`, `docs/runtime-flows.md`).
5. Run py_compile, full pytest, MCP discovery, and browser smoke against synthetic fixture DB.
6. If evidence satisfies ACs, mark BAT-010 release-candidate and REQ-013/REQ-014 Implemented.
## Design Gate
- Result: standard batch 유지.
- Reason: package boundary 변화, compatibility shim, MCP adapter seam, Streamlit browser verification, baseline docs가 함께 변경되므로 planning/design/verification depth가 필요하다.
## Risks and mitigations
- Hidden helper coupling after module split: mitigate by full pytest, monkeypatch compatibility, and targeted smoke on boundary-sensitive flows.
- DB initialization drift after infrastructure split: mitigate by preserving init/commit/rollback semantics and verifying schema initialization through tests.
- MCP/dashboard contract drift: mitigate by `hermes mcp test jarca-ledger`, Streamlit AppTest/full pytest, and browser smoke on key pages.
@@ -0,0 +1,45 @@
# BAT-010 Verification
## Verification summary
Status: PASS
Evidence was collected against synthetic/temp data only. No production ledger DB mutation or real financial files were used.
## Acceptance criteria mapping
### REQ-013
- AC-1: PASS — `jarca_ledger_mcp/application/` 아래 shared/system/asset-account/transaction-report/import-transfer/categorization modules가 생성됐다.
- AC-2: PASS — `jarca_ledger_mcp/infrastructure/sqlite/` 아래 connection/schema/migrations split이 생성됐고 top-level `db.py` shim이 유지된다.
- AC-3: PASS — `jarca_ledger_mcp/service.py`는 compatibility facade로 축소됐으며 현재 40 lines 수준이다(실질 구현은 application modules로 이동).
- AC-4: PASS — `pytest -q` full regression PASS (`42 passed, 4 skipped`), 기존 `service`/`db` import paths 유지.
- AC-5: PASS — `docs/project-structure.md`와 `docs/runtime-flows.md`가 새 계층 구조와 shim/gateway 흐름을 반영한다.
### REQ-014
- AC-1: PASS — `jarca_ledger_mcp/adapters/mcp/server.py` 도입 후 `hermes mcp test jarca-ledger` PASS, tools discovered 56.
- AC-2: PASS — `jarca_ledger_mcp/adapters/streamlit/service_gateway.py` 도입 후 `dashboard/app.py`가 gateway import를 사용한다.
- AC-3: PASS — full pytest PASS로 Streamlit AppTest / contract regression 포함 회귀가 유지된다.
- AC-4: PASS — local browser smoke에서 `요약`과 `자산/마감` 페이지 렌더를 확인했고 console JS errors가 없었다.
- AC-5: PASS — baseline docs와 BAT-010 implementation/verification 문서가 새 adapter seam을 반영한다.
## Requirement state decision
- REQ-013: Implemented.
- REQ-014: Implemented.
## Validation evidence
- `python -m py_compile jarca_ledger_mcp/*.py jarca_ledger_mcp/application/*.py jarca_ledger_mcp/infrastructure/sqlite/*.py jarca_ledger_mcp/adapters/mcp/*.py jarca_ledger_mcp/adapters/streamlit/*.py dashboard/app.py` → PASS
- `pytest -q` → PASS (`42 passed, 4 skipped`)
- `hermes mcp test jarca-ledger` → PASS (`Connected`, `Tools discovered: 56`)
- Browser smoke on Streamlit `요약` / `자산/마감` → PASS, JS errors 없음
## Remaining issues
### Hard blockers
- None.
### Non-blocking follow-up
- Phase 4+ transactional write services, import pipeline deeper split, advanced workflow separation, narrower adapter surfaces remain tracked as GitHub Project TODO items.
+1
View File
@@ -17,3 +17,4 @@
| 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/) |
| BAT-009 | standard | release-candidate | REQ-011, REQ-012 | dcy-009 | [bat-009_20260701_manual-asset-crud-snapshot-close](bat-009_20260701_manual-asset-crud-snapshot-close/) |
| BAT-010 | standard | release-candidate | REQ-013, REQ-014 | dcy-010 | [bat-010_20260703_backend-core-layering-phase-0-3](bat-010_20260703_backend-core-layering-phase-0-3/) |
@@ -5,9 +5,9 @@
- 문서 ID: dcy-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow
- 이슈명: Jarca Ledger 대시보드 운영 허브 사양 정립 (이번 달 현금흐름 + 자산 유동성 + 운영 미처리)
- 작성 시각(KST): 2026-06-29 14:02 KST
- 마지막 갱신 시각(KST): 2026-06-29 20:03 KST
- 마지막 갱신 시각(KST): 2026-06-30 20:43 KST
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 후속 Discovery 참조: docs/discovery/dcy-008_20260630_asset-snapshot-manual-valuation-system.md
- 생성된 REQ 참조: REQ-005 (`docs/srs/Interface/req-005_dashboard-operations-hub-liquidity-monthly-cashflow.md`)
# 1. 계획 상태 요약
@@ -0,0 +1,219 @@
# 0. 문서 상태
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
- 상태: confirmed
- 문서 ID: dcy-010_20260702_backend-core-layering-mcp-adapter-realignment
- 이슈명: Jarca Ledger 백엔드 코어 분리 및 layered 아키텍처(application/domain/infrastructure/adapters) 재편, MCP adapter 재정렬
- 작성 시각(KST): 2026-07-02 23:32
- 마지막 갱신 시각(KST): 2026-07-02 23:56
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: `REQ-013`, `REQ-014`
# 1. 계획 상태 요약
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
- 연계 문서 현황:
- 관련 blueprint: `.hermes/plans/2026-07-02_jarca-ledger-backend-refactor-blueprint.md` (근거 리팩터링 청사진)
- 관련 baseline 문서: `docs/project-structure.md`, `docs/runtime-flows.md`
- 해석:
- `jarca_ledger_mcp/service.py`가 약 3,378줄로 비대해져 MCP/Streamlit/SQLite 접근/import/reporting/reconciliation/asset 워크플로우가 하나의 함수 네임스페이스에 과결합된 상태이며, 향후 HTTP 등 인터페이스 확장 전에 layered backend 경계 도입이 필요하다.
- 이번 Discovery는 MCP를 제거하지 않고 adapter로 재정의하면서 application/domain/infrastructure/adapters 경계로 단계적(strangler-fig) 재편하는 구조 개선 주기를 신규로 시작할지 판정하고, baseline 문서(project-structure/runtime-flows) 갱신 요구까지 포함한 초안을 만든다.
# 2. 요구사항 판정 결과
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
- 판정: 신규
- 근거
- 기존 dcy-001~009는 기능/운영 워크플로우(분류·환급, 백업·복구, 잔액대조, E2E evidence, 대시보드, CI, MCP 응답 계약, 자산 스냅샷/CRUD)를 다루며, service.py 분해와 layered backend 아키텍처 재편이라는 구조 리팩터링 주제를 다룬 Discovery는 없다.
- dcy-007(MCP response contract)은 응답 계약의 안정성을 다루지만 내부 계층 구조 분해나 persistence 분리를 정의하지 않아 문제 정의·범위·성공 기준이 실질적으로 다르다.
> **판정이 `완전 중복`인 경우**: 이하 섹션 작성을 중단하고 `# 9. 파일 처리 결과`에 중복 판정 사유와 참조 문서 경로를 기록한 뒤 문서를 닫는다. REQ 초안 작성으로 넘기지 않는다.
# 3. 문제점의 요약
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
- 현재 상태
- `jarca_ledger_mcp/service.py`(약 3,378줄, blueprint 기준 약 126 함수)가 사실상 애플리케이션 전체 역할을 하며 비즈니스 규칙·오케스트레이션·SQL·mutation 안전·import·reporting·cross-transfer가 한 모듈에 집중되어 있다.
- `jarca_ledger_mcp/server.py`(약 296줄, 약 57개 MCP tool wrapper)와 `dashboard/app.py`(약 1,811줄)가 모두 `from jarca_ledger_mcp import service`로 동일한 넓은 함수 네임스페이스에 직접 결합되어 있다.
- `jarca_ledger_mcp/db.py`(약 333줄)에 schema·connection·additive migration helper가 한 파일에 섞여 있어 persistence 경계가 비즈니스 규칙과 분리되어 있지 않다.
- baseline 문서는 이미 존재한다(`docs/project-structure.md`, `docs/runtime-flows.md`). 다만 이번 구조 재편은 패키지 레이아웃과 런타임 호출 흐름을 크게 바꾸므로 두 baseline 문서의 갱신 갭이 발생한다.
- 기대 상태
- MCP는 유지하되 `adapters/mcp`로 위치를 재정의하고, Streamlit은 `adapters/streamlit` gateway를 통해 좁은 표면에만 의존하는 thin adapter가 된다.
- domain/application/infrastructure/adapters 경계가 명시되고, service.py는 호환 facade로 축소되거나 호출부 이관 후 제거되며, 각 도메인 영역이 독립적으로 이해·테스트 가능해진다.
- 이해관계자
- 문서 작성자(AI agent) 및 리팩터링 실행 subagent
- 리뷰어/승인자(Hermes 및 저장소 소유자)
- MCP/Streamlit 사용자(마이그레이션 중 동작 유지가 보장되어야 하는 소비자)
- 제약사항
- 마이그레이션 전 구간에서 MCP tool 이름/주요 응답 필드/카테고리·현금흐름 의미/import 증빙 필드/additive-only migration/Streamlit 페이지 동작이 호환 계약으로 유지되어야 한다.
- big-bang rewrite 금지, 금융 데이터 특성상 destructive migration은 승인 없이 금지하고 fixture/복사 DB만 사용한다.
## 주요 사용자 시나리오
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
- 개발자가 특정 도메인(예: 자산 스냅샷) 로직을 수정할 때 3,378줄 service.py 전체를 탐색하지 않고 해당 application service와 repository만 열어 변경·테스트하려는 상황.
- 운영자가 DB I/O/integrity 장애를 진단할 때, persistence 계층이 분리되어 있어 문제를 전체 애플리케이션 경로가 아닌 infrastructure/sqlite 경계로 국소화하려는 상황.
- 향후 HTTP/FastAPI adapter를 추가하려는 개발자가 비즈니스 로직을 다시 이동시키지 않고 `application/*` 위에 adapter 패키지만 추가하려는 상황.
# 4. 이번에 정의할 변경
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
- service.py 분해: 비즈니스 로직을 domain(types/invariants/validation), application(use case/orchestration), infrastructure(sqlite repo/parsers)로 이관하고 service.py는 임시 호환 facade로 축소한다.
- persistence 분리: db.py의 schema/connection/migration을 `infrastructure/sqlite`로 분리하고 테이블/use-case별 repository를 도입한다.
- MCP/Streamlit thin adapter화: MCP tool 등록을 `adapters/mcp/server.py`로 이동해 top-level server.py는 shim으로 남기고, Streamlit은 `adapters/streamlit/service_gateway.py`의 좁은 gateway 호출로 전환한다.
- baseline 문서 갱신 요구: 새 패키지 레이아웃과 런타임 호출 경로 변화를 반영해 `docs/project-structure.md`와 `docs/runtime-flows.md`를 갱신한다.
- 단계적 migration 원칙 확정: strangler-fig 순서(foundations → read-only/reporting → write CRUD → import → cross-transfer/reconciliation/assets → adapter narrowing)로 blast radius를 최소화한다.
## 영향 범위
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
- 코드 영향:
- `jarca_ledger_mcp/service.py`, `db.py`, `server.py` 분해 및 호환 facade/shim화
- 신규 패키지 `jarca_ledger_mcp/{domain,application,infrastructure,adapters}/*` 도입
- `dashboard/app.py`의 `service.*` 광범위 의존을 gateway 호출로 축소, `pyproject.toml` 패키지 discovery/entrypoint 정합성 유지
- 테스트를 repository/application-service/adapter 계층으로 재배치(Layer A/B/C)
- 문서 영향:
- `docs/project-structure.md`, `docs/runtime-flows.md` 갱신 요구
- 신규 Discovery 및 `docs/discovery/index.md` 행 추가
- 운영 영향:
- 마이그레이션 중 MCP/Streamlit 동작 유지 필요, 배포 절차 자체 변경은 없음(entrypoint 호환 유지)
- destructive migration 금지, DB 안전 정책(복사/fixture DB) 유지
# 5. 요구사항 목록
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
## 기능 요구사항
- FR-1: `jarca_ledger_mcp`에 domain/application/infrastructure/adapters 경계를 도입하고 service.py의 로직을 도메인별 application service와 repository로 이관하되, service.py는 호환 facade로 남겨 기존 호출부가 깨지지 않게 한다.
- FR-2: db.py의 schema/connection/additive migration을 `infrastructure/sqlite`(connection/schema/migrations/row_mapping + 테이블별 repo)로 분리하고, db.py는 호환 shim으로 유지한다.
- FR-3: MCP tool 등록을 `adapters/mcp`로 이동해 top-level `server.py`를 thin shim으로 만들고, Streamlit이 `adapters/streamlit/service_gateway.py`의 좁은 gateway에만 의존하도록 전환한다.
- FR-4: 구조/런타임 baseline 갭을 해소하기 위해 `docs/project-structure.md`와 `docs/runtime-flows.md`에 신규 계층 구조와 호출 흐름 갱신 요구를 반영한다.
## 비기능 요구사항
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
- NFR-1: 호환 계약 안정성 — MCP tool 이름, 주요 응답 필드(`success`, `count`, `items`, `error`, `validation`, `matched_count` 등), 카테고리·현금흐름 의미, import 증빙 필드(`raw_summary`/`counterparty`/`raw_payload`/`external_id`/`import_batch_id`), additive-only migration, Streamlit 페이지 동작/순서가 의도적 버전 변경·승인 없이 바뀌지 않는다.
- NFR-2: 단계적 안전성 — 각 phase는 독립적으로 검증 가능해야 하고(pytest 회귀 + py_compile), 한 번에 하나의 도메인 슬라이스만 이관해 blast radius를 최소화한다.
- NFR-3: DB 안전성 — 리팩터링 중 fixture/복사 DB만 사용하고 승인 없는 destructive migration을 수행하지 않는다.
## 범위 경계
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
- In Scope
- blueprint 기준 Phase 0~3 우선: behavioral baseline 고정 → 새 skeleton/compat seam 도입 → persistence 분리 → read-only application service 추출
- baseline 문서(project-structure/runtime-flows) 갱신 요구
- 단계적(strangler-fig) migration 원칙 정의
- Out of Scope
- 즉시 FastAPI/HTTP 전환(향후 adapter 준비만, 강제하지 않음)
- Postgres/원격 DB 이관
- 멀티유저/권한 체계 도입
- 원격 운영(remote ops) 재설계
- Phase 4 이후의 write/import/advanced workflow/adapter narrowing 구현(별도 후속 REQ·Batch·GitHub Project TODO로 추적)
# 6. 리스크/가정 목록
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
## 리스크
- R-1 (High): service.py 내부 숨은 결합 — 공유 helper 이관 시 예상치 못한 호출부가 깨질 수 있다.
- 완화 방안: 리팩터링 전에 characterization/contract 회귀 테스트를 먼저 추가하고, helper는 첫 소비자와 함께 이동한다.
- R-2 (High): MCP/Streamlit 계약 drift — tool 응답 형태나 페이지 동작이 조용히 바뀔 수 있다.
- 완화 방안: 리팩터링 전 응답 형태(shape) 테스트와 Streamlit 페이지/서비스 통합 테스트를 확보하고 adapter gateway로 페이지 단위 점진 이관한다.
- R-3 (Medium): 신규 repository 간 SQL/row mapping 중복으로 유지보수성 저하.
- 완화 방안: row mapping과 공통 predicate를 중앙화(`row_mapping.py`)하고 repository 규칙(메시지 포매팅 금지, dict/dataclass 반환)을 강제한다.
## 가정
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
- A-1: 기존 pytest 스위트(transaction/import/rules/dashboard 회귀 등)가 동작하며 baseline 고정과 회귀 검증의 기준으로 사용할 수 있다.
- A-2: 현재 SQLite schema가 additive-oriented이므로 destructive 변경 없이 계층 분리를 진행할 수 있다.
- A-3: MCP는 이미 spirit 상 adapter(server.py가 대부분 thin wrapper)이므로 adapter 재정렬이 큰 동작 변경 없이 가능하다.
## 오픈 질문
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
- OQ-1: 이번 리팩터링을 전체 phase(1~7)를 한 주기로 다룰지, blueprint Batch A(Phase 0~3)만 첫 배치로 축소해 시작할지?
- 상태: Answered
- 처리 방안: 사용자 승인에 따라 Discovery 범위를 Phase 0~3 우선 배치로 고정하고, Phase 4+는 GitHub Project TODO와 후속 REQ/Batch로 분리 추적한다.
- 종료 조건: `# 5. 요구사항 목록 > 범위 경계`에 1차 범위가 Phase 0~3으로 반영되었으므로 종료한다.
- OQ-2: 마이그레이션 완료 판정 기준(service.py 목표 라인 수 ~400~600줄 축소 vs 완전 제거) 중 어느 것을 Definition of Done으로 채택할지?
- 상태: Answered
- 처리 방안: 사용자 승인에 따라 1차 완료 기준을 service.py 호환 facade 수준(~400~600줄) 축소로 채택하고, 완전 제거는 후속 단계 최적화로 남긴다.
- 종료 조건: 성공 기준 S-2와 REQ handoff 기준에 facade 축소 DoD가 반영되었으므로 종료한다.
### 오픈 질문 처리 기준
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
- 처리 원칙 1: 질문만 남기지 말고 반드시 `상태`, `처리 방안`, `종료 조건`을 함께 적는다.
- 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
- 처리 원칙 3: High 영향 질문은 `Deferred` 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.
# 7. 초기 성공 기준
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
- S-1: 마이그레이션 전 구간에서 MCP와 Streamlit이 동작하며 기존/대체 회귀 테스트가 통과한다.
- S-2: 1차 리팩터링 완료 시 service.py가 호환 glue/facade 수준(blueprint §11 기준 ~400~600줄)으로 축소되고, db.py는 shim 또는 얇은 compatibility entry로 축소된다.
- S-3: 각 주요 도메인 영역이 자체 application service와 persistence 경계를 갖고, `docs/project-structure.md`·`docs/runtime-flows.md`가 신규 계층 구조를 반영하도록 갱신되어 이후 REQ/Batch 산출물이 baseline 문서를 참조할 수 있다.
## 측정 방식 및 데이터 출처
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
- S-1 측정 방식: 각 phase 종료 시 pytest 회귀 스위트 + MCP tool contract 테스트 전부 통과 확인 / 데이터 출처: `tests/` 실행 결과, `docs/discovery/dcy-007` 응답 계약
- S-2 측정 방식: `wc -l jarca_ledger_mcp/service.py`, `db.py` 라인 수와 import 의존성 그래프를 확인하고 1차 목표를 facade 수준 축소로 검증 / 데이터 출처: 리팩터링 후 소스 파일, blueprint §11 DoD
- S-3 측정 방식: 각 도메인 application service/repository 존재 여부 및 baseline 문서 diff 확인 / 데이터 출처: `jarca_ledger_mcp/{application,infrastructure}/*`, `docs/project-structure.md`, `docs/runtime-flows.md`
# 8. REQ로 넘기기 전 확인 체크
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
- [x] 범위(In Scope/Out of Scope) 확정
- 기준: 사용자 승인에 따라 Phase 0~3 우선 범위와 Out of Scope 항목이 문서에 반영되어 있으며, Phase 4+는 별도 TODO/후속 REQ로 추적한다.
- [x] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-2의 1차 DoD를 service.py facade 수준 축소로 채택했고, 세부 baseline 테스트 목록은 REQ/Phase 0 planning에서 계측 대상으로 명시한다.
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-1, R-2는 REQ/Batch planning 단계에서 characterization/contract 테스트 선행 책임자를 지정하는 비차단 항목으로 정리되었고, Discovery 범위·성공 기준·검증 시나리오 확정은 완료되었다.
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ 목록의 항목이 모두 `Answered`이거나 `Deferred` 처리 완료
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: 사용자 승인에 따라 저장소 소유자 `진환 고`가 Confirmed By로 기록되었다.
# 9. 파일 처리 결과
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
- 처리 결과: 갱신
- 생성 경로: docs/discovery/dcy-010_20260702_backend-core-layering-mcp-adapter-realignment.md
- 참조 문서:
- .hermes/plans/2026-07-02_jarca-ledger-backend-refactor-blueprint.md (근거 blueprint)
- stage-pilot/templates/project-structure.md (baseline 갱신 논리 템플릿 참조)
- stage-pilot/templates/runtime-flows.md (baseline 갱신 논리 템플릿 참조)
# 10. 사용자 결정 필요 항목 요약
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
## DECIDE
- 없음 — 이번 반복 범위는 blueprint 기준 Phase 0~3 우선으로 고정한다.
- 없음 — 1차 완료 기준은 service.py를 ~400~600줄 호환 facade 수준으로 축소하는 것으로 채택한다.
## CONFIRM
- 없음 — MCP tool 이름/주요 응답 필드/import 증빙 필드/Streamlit 페이지 동작은 이번 리팩터링 동안 호환 계약으로 동결한다.
## DATA
- REQ 단계 계측: Phase 0 baseline 고정용 현재 통과 pytest 스위트 목록과 커버 대상 MCP tool 집합은 REQ/Phase 0 planning 산출물에서 명시한다.
# 11. Discovery Freeze
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
## 섹션 참조
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
## Freeze 플래그
- Handoff Decision: 승인
- Handoff Rationale: 사용자 승인에 따라 1차 범위를 Phase 0~3으로 축소하고 1차 완료 기준을 service.py facade 수준 축소로 확정했다. 호환 계약 동결 정책도 반영되었고, 남은 baseline 테스트 상세 목록과 리스크 책임자 지정은 REQ/Phase 0 planning 단계 비차단 계측·할당 항목으로 정리되었다.
- Ready for REQ Drafting: true
- Confirmed By: 진환 고
- Confirmed At (KST): 2026-07-02 23:56 KST
+1
View File
@@ -15,3 +15,4 @@
| dcy-007_20260630_mcp-response-contract-ci-contract-versioning | 20260630 | MCP response contract / CI contract versioning 정립 | [Discovery](./dcy-007_20260630_mcp-response-contract-ci-contract-versioning.md) | confirmed |
| dcy-008_20260630_asset-snapshot-manual-valuation-system | 20260630 | 자산 스냅샷/수기 자산 테이블/평가 체계 정립 | [Discovery](./dcy-008_20260630_asset-snapshot-manual-valuation-system.md) | confirmed |
| dcy-009_20260701_manual-asset-crud-month-end-snapshot-close-workflow | 20260701 | 수기 자산 CRUD + 월말 스냅샷 생성/마감 운영 워크플로우 | [Discovery](./dcy-009_20260701_manual-asset-crud-month-end-snapshot-close-workflow.md) | confirmed |
| dcy-010_20260702_backend-core-layering-mcp-adapter-realignment | 20260702 | Jarca Ledger 백엔드 코어 분리 및 layered 아키텍처 재편, MCP adapter 재정렬 | [Discovery](./dcy-010_20260702_backend-core-layering-mcp-adapter-realignment.md) | confirmed |
+75 -41
View File
@@ -4,21 +4,21 @@
- Baseline Seed: `.stage-pilot/bootstrap/baseline.yaml`
- Status: draft
- Owner: Jarca / project maintainer
- Last Updated (KST): 2026-06-30
- Source Discovery / Batch: bootstrap-baseline, BAT-006, BAT-007
- Last Updated (KST): 2026-07-03
- Source Discovery / Batch: bootstrap-baseline, BAT-006, BAT-007, BAT-010
## Project Summary
- Jarca 가계부 전용 MCP 서버와 Streamlit 대시보드로 계좌/카드 거래, 예산, 분류 규칙, import, 현금흐름 분석을 SQLite에 저장하고 운영한다.
- Jarca 가계부 전용 MCP 서버와 Streamlit 대시보드로 계좌/카드 거래, 예산, 분류 규칙, import, 자산/스냅샷, 현금흐름 분석을 SQLite에 저장하고 운영한다.
## Primary Domain
- Korean household finance ledger and transaction reconciliation
- Korean household finance ledger, reconciliation, and household asset tracking
## Tech Stack
- Python >=3.11
- FastMCP / MCP server
- SQLite
- Streamlit dashboard
- pandas / plotly dashboard dependencies
- Streamlit
- pandas / plotly
- PyMuPDF, openpyxl, msoffcrypto-tool
- pytest
- ruff
@@ -53,9 +53,28 @@ jarca-ledger-mcp/
│ └── app.py
├── jarca_ledger_mcp/
│ ├── __init__.py
│ ├── db.py
│ ├── service.py
│ ├── server.py
│ ├── service.py # compatibility facade
│ ├── db.py # compatibility shim
│ ├── server.py # compatibility shim / CLI entrypoint
│ ├── application/
│ │ ├── __init__.py
│ │ ├── shared.py
│ │ ├── system_services.py
│ │ ├── asset_account_services.py
│ │ ├── transaction_report_services.py
│ │ ├── import_transfer_services.py
│ │ └── categorization_services.py
│ ├── infrastructure/
│ │ └── sqlite/
│ │ ├── __init__.py
│ │ ├── connection.py
│ │ ├── schema.py
│ │ └── migrations.py
│ ├── adapters/
│ │ ├── mcp/
│ │ │ └── server.py
│ │ └── streamlit/
│ │ └── service_gateway.py
│ ├── kb_bank_pdf.py
│ ├── toss_bank_pdf.py
│ ├── toss_securities_pdf.py
@@ -64,7 +83,9 @@ jarca-ledger-mcp/
│ ├── contract_snapshots/
│ │ └── response_contract_v1_0.json
│ ├── test_contract_regression.py
│ ├── test_dashboard_app.py
│ ├── test_service.py
│ ├── test_asset_snapshots.py
│ ├── test_categorization.py
│ ├── test_category_rule_filters.py
│ ├── test_cross_account_transfer_matching.py
@@ -93,60 +114,72 @@ jarca-ledger-mcp/
```
## Package / Module Responsibilities
- `jarca_ledger_mcp/db.py`
- SQLite connection, schema initialization, migrations, row conversion, and database path resolution.
- `jarca_ledger_mcp/service.py`
- Domain service layer for accounts, transactions, categories, category rules, imports, reports, asset liquidity/net-worth aggregation, transfer matching, and manual link operations.
- Legacy-compatible top-level service facade. Public call sites continue to import from here while implementation lives in application modules.
- `jarca_ledger_mcp/db.py`
- Legacy-compatible top-level DB shim. Public call sites continue to import `init_db`, `db`, `resolve_db_path` from here.
- `jarca_ledger_mcp/server.py`
- Legacy-compatible MCP entrypoint shim that delegates to `adapters.mcp.server`.
- `jarca_ledger_mcp/application/shared.py`
- Shared helpers, validation, KST clock, dedupe policy, parser imports, and cross-domain constants.
- `jarca_ledger_mcp/application/system_services.py`
- Health/status/backup/account create-list/update and persistence-adjacent service behavior.
- `jarca_ledger_mcp/application/asset_account_services.py`
- Account mutation, manual asset, asset snapshot, liquidity/net-worth logic.
- `jarca_ledger_mcp/application/transaction_report_services.py`
- Transaction CRUD, budgets, monthly/category/fixed-cost/investable cashflow reports.
- `jarca_ledger_mcp/application/import_transfer_services.py`
- Import preview/import, self-transfer/cancel matching, cross-account transfer workflow.
- `jarca_ledger_mcp/application/categorization_services.py`
- Category rules, categorization preview/apply, uncategorized workbench, ledger categories.
- `jarca_ledger_mcp/infrastructure/sqlite/connection.py`
- DB path resolution, sqlite connection setup, context manager semantics.
- `jarca_ledger_mcp/infrastructure/sqlite/schema.py`
- Schema version, DDL, additive column definitions.
- `jarca_ledger_mcp/infrastructure/sqlite/migrations.py`
- Additive/idempotent migration routines and DB initialization.
- `jarca_ledger_mcp/adapters/mcp/server.py`
- FastMCP tool registration layer that exposes service functions to Hermes/MCP clients.
- `jarca_ledger_mcp/kb_bank_pdf.py`
- KB 국민은행 encrypted PDF transaction parsing.
- `jarca_ledger_mcp/toss_bank_pdf.py`
- Toss Bank transaction-history PDF parsing.
- `jarca_ledger_mcp/toss_securities_pdf.py`
- Toss Securities transaction-history PDF parsing and transaction preview conversion.
- `jarca_ledger_mcp/seoulpay_xlsx.py`
- SeoulPay encrypted XLSX parsing and preview conversion.
- `jarca_ledger_mcp/adapters/streamlit/service_gateway.py`
- Narrow dashboard-facing service surface used by `dashboard/app.py`.
- Parser modules (`kb_bank_pdf.py`, `toss_bank_pdf.py`, `toss_securities_pdf.py`, `seoulpay_xlsx.py`)
- Source-file parsing and preview conversion only.
- `dashboard/app.py`
- Streamlit operator UI for the operations hub summary, transaction filtering, uncategorized triage, rule management, review queues, categories, accounts/liquidity bucket editing, and import operations.
- Streamlit operator UI for the operations hub summary, 자산/마감, 거래내역, 확인필요 큐, 미분류, 분류 규칙, 카테고리, 계좌, Import pages.
- `tests/`
- Regression tests for service behavior, parser behavior, categorization, category rules, cross-account transfer matching, ledger category semantics, MCP response contract compatibility, and Streamlit AppTest dashboard smoke.
- `tests/contract_snapshots/`
- Representative MCP response contract metadata/schemas. Version `1.0` is used by `tests/test_contract_regression.py` and intentionally asserts required subset/types so optional additive fields remain compatible.
- Regression tests for facade compatibility, service behavior, parser behavior, categorization, transfer matching, MCP contract compatibility, and Streamlit AppTest smoke.
- `.github/workflows/ci.yml`
- GitHub Actions workflow for `pull_request`, `push` to `main`, and `workflow_dispatch`; stable required gate jobs are `pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, `db-safety-smoke`, `contract-regression`, and `lint`.
- `scripts/ci_fixture_db.py`
- Synthetic fixture DB builder for CI/AppTest/smoke checks. Refuses the default operating DB path and seeds only synthetic ledger data.
- Synthetic fixture DB builder for CI/AppTest/browser smoke.
- `scripts/ci_dashboard_http_smoke.py`
- CI HTTP smoke wrapper that creates a synthetic fixture DB, starts Streamlit on loopback, polls HTTP 200 readiness, and cleans up the process with status-only JSON output.
- Synthetic-db Streamlit readiness smoke wrapper.
- `scripts/ci_db_safety_smoke.py`
- CI DB safety wrapper that runs restore smoke through temp synthetic source/backup/target DB paths only and emits status-only JSON.
- Synthetic-db backup/restore safety smoke wrapper.
- `scripts/streamlit_browser_evidence.py`
- Local browser evidence helper that creates a synthetic fixture DB, starts Streamlit with that DB, and captures Chrome screenshot/DOM snapshot artifacts for QC.
- Local browser evidence helper using synthetic/validation DBs only.
- `scripts/ledger_restore_smoke.py`
- Script-only restore smoke helper for validation DBs; restore remains outside the MCP tool surface.
- Script-only restore smoke helper; restore remains outside the MCP tool surface.
## Dependency Rules
- `server.py` should remain a thin tool adapter and delegate business logic to `service.py`.
- `adapters/mcp/server.py` should remain a thin tool adapter and delegate business logic to `service.py` facade/application modules.
- `dashboard/app.py` should call `adapters/streamlit/service_gateway.py`, not giant implementation modules directly.
- `service.py` / `db.py` / `server.py` shims preserve public import paths while new implementation lives under `application/`, `infrastructure/`, `adapters/`.
- Parser modules should parse source documents into structured records and avoid direct UI responsibilities.
- `dashboard/app.py` may call service functions but should not duplicate core classification/report semantics.
- DB schema migrations should be additive and idempotent by default.
## Shared Boundaries
- `cashflow_kind` is the boundary between actual household income/expense and account movement/exclusion semantics.
- `liquidity_bucket` is the account-level boundary for asset liquidity/composition/debt/net-worth views; `dashboard/app.py` displays it and `service.py` computes it from `accounts.last_balance`.
- `liquidity_bucket` is the account/manual-asset boundary for liquidity/composition/debt/net-worth views.
- Import evidence fields (`source`, `external_id`, `raw_summary`, `raw_payload`, `counterparty`, `import_batch_id`) are audit boundaries and should be preserved.
- Manual reconciliation is represented through memo tags and narrow transaction updates, not broad rewrites.
- Browser evidence artifacts are generated from synthetic/validation DBs and should not embed real financial identifiers or raw source payloads.
- CI gate fixtures are generated at runtime under temp space by `scripts/ci_fixture_db.py`; CI and AppTest must not read the default operating DB path.
- MCP contract regression fixtures use pytest `tmp_path` synthetic DBs and committed schema metadata only; they must not read the operating DB or expose runtime `contract_version` fields.
- Branch protection required-check contract is documented in `docs/interface-contract.md` and represented by stable workflow job names. Applying branch protection remains an external GitHub repository setting.
- CI gate fixtures are generated at runtime under temp space by `scripts/ci_fixture_db.py`; CI/AppTest/browser smoke must not read the default operating DB path.
## Current Gaps / Planned Changes
- Local browser evidence capture exists for core Streamlit pages; browser screenshot/visual regression remains local evidence rather than a required CI gate.
- CI workflow now covers pytest/AppTest/HTTP smoke/DB safety smoke plus BAT-007 contract-regression/lint required-check jobs; branch protection enforcement remains a repository setting outside this codebase.
- Long-term policy for accounts without downloadable statements remains partially operational/documentation work.
- Separate automatic asset snapshots/manual asset tables are not present; BAT-005 uses account rows as the manual asset/liability source.
- Application modules are now separated by phase-0~3 domain seams, but deeper repository/use-case split remains future Phase 4+ work.
- Streamlit gateway is intentionally narrow for current pages; further page/domain-specific gateways may follow in later phases.
- HTTP adapter and remote ops re-architecture remain out of scope.
## Update Triggers
- Update this document when package boundaries change.
@@ -157,5 +190,6 @@ jarca-ledger-mcp/
- 2026-06-26 22:24 KST: Created by `bootstrap-baseline` from repository observation.
- 2026-06-29 KST: REQ-004/BAT-004 기준으로 `scripts/streamlit_browser_evidence.py` 및 browser evidence artifact 구조 추가.
- 2026-06-29 KST: REQ-005/BAT-005 기준으로 dashboard operations hub와 service asset liquidity aggregation 책임 추가.
- 2026-06-30 KST: REQ-006/BAT-006 기준으로 GitHub Actions CI workflow, synthetic fixture DB builder, AppTest/HTTP/DB safety smoke wrappers, CI-vs-local-browser evidence 경계 추가.
- 2026-06-30 KST: REQ-007/REQ-008/BAT-007 기준으로 MCP response contract snapshot/test 위치와 `contract-regression`/`lint` required-check job 계약 추가.
- 2026-06-30 KST: REQ-006/BAT-006 기준으로 GitHub Actions CI workflow, synthetic fixture DB builder, AppTest/HTTP/DB safety smoke wrappers 추가.
- 2026-06-30 KST: REQ-007/REQ-008/BAT-007 기준으로 MCP response contract snapshot/test 위치와 required-check job 계약 추가.
- 2026-07-03 KST: REQ-013/REQ-014/BAT-010 기준으로 application / infrastructure / adapter layering, compatibility facade/shim, Streamlit gateway 구조를 반영했다.
+53 -69
View File
@@ -4,19 +4,19 @@
- Baseline Seed: `.stage-pilot/bootstrap/baseline.yaml`
- Status: draft
- Owner: Jarca / project maintainer
- Last Updated (KST): 2026-06-30
- Source Discovery / Batch: bootstrap-baseline, BAT-006, BAT-007, BAT-008
- Last Updated (KST): 2026-07-03
- Source Discovery / Batch: bootstrap-baseline, BAT-006, BAT-007, BAT-008, BAT-010
- Primary Runtime: mixed
## Project Summary
- Jarca 가계부 전용 MCP 서버와 Streamlit 대시보드로 계좌/카드 거래, 예산, 분류 규칙, import, 현금흐름 분석을 SQLite에 저장하고 운영한다.
- Jarca 가계부 전용 MCP 서버와 Streamlit 대시보드로 계좌/카드 거래, 예산, 분류 규칙, import, 현금흐름 분석, 자산/스냅샷 운영을 SQLite에 저장하고 제공한다.
## Purpose
- This document is the current execution-flow baseline for the project.
- It is a cross-cutting reference document for Discovery, REQ, Batch Design, Batch Verification, and implementation updates.
## Scope
- Covers representative command, MCP, dashboard, import, and reconciliation flows.
- Covers representative command, MCP, dashboard, import, reconciliation, snapshot, and verification flows.
- Does not duplicate full acceptance criteria or low-level implementation details.
## Covered Entry Points
@@ -27,56 +27,48 @@
## Shared Components
- SQLite database at `~/.local/share/jarca/ledger.sqlite3` by default.
- `jarca_ledger_mcp.service` as the central domain/service layer.
- `jarca_ledger_mcp/service.py` compatibility facade over application modules.
- `jarca_ledger_mcp/infrastructure/sqlite/*` for DB path resolution, schema, migration, and connection lifecycle.
- `jarca_ledger_mcp/adapters/mcp/server.py` for MCP registration.
- `jarca_ledger_mcp/adapters/streamlit/service_gateway.py` for dashboard-facing service calls.
- Parser modules for KB/Toss/SeoulPay sources.
- Category and `cashflow_kind` semantics shared by reports and UI.
## Flow: `MCP server tool call`
1. Hermes MCP client invokes a tool exposed by `jarca_ledger_mcp.server`.
2. `server.py` forwards arguments to the matching `service.py` function.
3. `service.py` opens the SQLite database path from env/default, validates inputs, and performs the operation.
4. Service returns a dictionary payload with success data or error information.
5. Hermes surfaces the tool result to the user or uses it in a follow-up workflow.
1. Hermes MCP client invokes a tool exposed by `jarca_ledger_mcp.server` / `jarca_ledger_mcp.adapters.mcp.server`.
2. MCP adapter forwards arguments to the matching `jarca_ledger_mcp.service` facade function.
3. Service facade routes into the relevant application module.
4. Application code opens SQLite through `infrastructure/sqlite` helpers, validates inputs, and performs the operation.
5. Application returns a dictionary payload with success data or error information.
6. Hermes surfaces the tool result to the user or uses it in a follow-up workflow.
## Flow: `Streamlit dashboard operation`
1. Operator starts `streamlit run dashboard/app.py --server.address 0.0.0.0 --server.port 8501`.
2. Dashboard reads the configured/default SQLite database.
3. User navigates to 요약, 거래내역, 확인필요 큐, 미분류, 분류 규칙, 카테고리, 계좌, or Import pages.
4. UI calls service functions for reads, updates, imports, categorization, and manual linking.
5. UI displays success/error status and refreshes visible tables.
3. User navigates to 요약, 자산/마감, 거래내역, 확인필요 큐, 미분류, 분류 규칙, 카테고리, 계좌, or Import pages.
4. UI calls `jarca_ledger_mcp.adapters.streamlit.service_gateway` rather than giant implementation modules directly.
5. Gateway delegates to the service facade/application modules and returns data to the page.
6. UI displays success/error status and refreshes visible tables.
## Flow: `Dashboard operations hub overview`
1. Operator opens the `요약` page.
2. Dashboard resolves the KST current month and calls `get_monthly_cashflow_report(month)` using `transactions.transaction_date` and `ledger_categories.cashflow_kind` semantics.
2. Dashboard calls `get_monthly_cashflow_report(month)` using `transactions.transaction_date` and `ledger_categories.cashflow_kind` semantics.
3. Dashboard calls `get_asset_liquidity_report`, which uses the shared valuation helper: account-backed assets from `accounts.last_balance` plus active `manual_assets` rows when present.
4. If `manual_assets` and `asset_snapshots` are empty, the report keeps the REQ-005 account-only fallback and preserves existing totals/keys.
5. Dashboard renders sections in REQ-005 priority order: this-month actual income/expense/net/savings rate, asset liquidity, asset composition, budget/spend, operational warnings.
6. Missing `last_balance` or `manual_assets.valuation_amount` values are excluded from asset totals and surfaced as warnings; transaction sums are not used to estimate non-transaction assets.
6. Missing `last_balance` or `manual_assets.valuation_amount` values are excluded from asset totals and surfaced as warnings.
## Flow: `CI validation gates`
1. GitHub Actions starts `.github/workflows/ci.yml` on `pull_request`, `push` to `main`, or `workflow_dispatch`.
2. `lint` installs dev dependencies and runs `ruff check .` with the conservative BAT-007 required rule set.
3. `contract-regression` installs dev dependencies and runs `pytest tests/test_contract_regression.py`; the test uses synthetic pytest temp DBs and `tests/contract_snapshots/response_contract_v1_0.json` metadata for health/status, list, report, and mutation success envelope scopes.
4. `pytest-regression` installs the project with dev/dashboard extras and runs `pytest --ignore=tests/test_dashboard_app.py` so service/parser/regression failures fail the workflow.
5. `dashboard-app-test` runs `pytest tests/test_dashboard_app.py`; the AppTest test creates a synthetic fixture DB through `scripts/ci_fixture_db.py`, sets `JARCA_LEDGER_DB_PATH`, and asserts core dashboard pages render without Streamlit exceptions.
6. `dashboard-http-smoke` runs `scripts/ci_dashboard_http_smoke.py`; the wrapper creates a temp fixture DB, starts Streamlit on loopback, polls for HTTP 200, then terminates the process.
7. `db-safety-smoke` runs `scripts/ci_db_safety_smoke.py`; the wrapper creates temp source/backup/target DB files and invokes `scripts/ledger_restore_smoke.py` without touching the operating DB path.
8. Any required job failure fails the workflow. Stable required-check job names are `pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, `db-safety-smoke`, `contract-regression`, and `lint`; actual branch protection toggle enforcement is configured outside the repository.
9. CI logs are status-oriented and synthetic-data-only; they must not contain real account identifiers, raw payloads, statement filenames, balances from the operating DB, or passwords.
## Flow: `Asset snapshot and net-worth read model`
1. Month-end manual close records item-level rows in `asset_snapshots` from the live authority sources: account `last_balance` rows and `manual_assets` valuations. BAT-008 stores the schema and service helper but does not expose snapshot creation as an MCP mutation.
2. `get_asset_snapshot_report(basis_date)` first checks for snapshot rows on the requested basis date.
3. If snapshot rows exist, the report computes bucket totals, total assets, total liabilities, and net worth from the stored rows.
4. If no snapshot rows exist, the report falls back to live authority sources (`accounts.last_balance` + active `manual_assets`) and marks `source='live_authority'`.
5. `get_net_worth_timeseries(start_date, end_date, months)` reads distinct stored snapshot basis dates and applies the same asset-buckets-minus-loan formula to every point.
6. `list_manual_assets` exposes current manual asset/liability rows for read-only MCP consumers. Manual asset CRUD and snapshot creation MCP tools remain future scope.
## Flow: `Asset snapshot and net-worth workflow`
1. Operator can open `자산/마감` in Streamlit or call snapshot tools through MCP.
2. `preview_asset_snapshot_close(basis_date)` computes authority-source items from `accounts.last_balance` + active `manual_assets` without DB side effects.
3. `close_asset_snapshot(basis_date, source_note, memo, correction_reason)` writes append-only snapshot rows with correction metadata when re-closing the same basis date.
4. `get_asset_snapshot_report(basis_date)` first checks stored snapshot rows; if none exist, it falls back to live authority sources and marks `source='live_authority'`.
5. `get_net_worth_timeseries(start_date, end_date, months)` reads stored snapshot basis dates and applies the same asset-buckets-minus-loan formula to every point.
## Flow: `Import preview and import`
1. Operator chooses or supplies a supported source file: KB Bank PDF, Toss Bank PDF, Toss Securities PDF, SeoulPay XLSX, or CSV.
1. Operator chooses a supported source file: KB Bank PDF, Toss Bank PDF, Toss Securities PDF, SeoulPay XLSX, or CSV.
2. Preview function parses the file and reports counts, sample rows, duplicate status, and classification preview.
3. On explicit import, service creates/fetches the target account, inserts non-duplicate transactions, writes raw provenance, and records an import batch.
4. Reports and dashboard views read the inserted rows using category and cashflow semantics.
3. On explicit import, application code creates/fetches the target account, inserts non-duplicate transactions, writes provenance, and records an import batch.
4. Reports and dashboard views read the inserted rows using shared category/cashflow semantics.
5. If an import is wrong, rollback uses import batch data and soft-deletes by default.
## Flow: `Categorization rule lifecycle`
@@ -86,7 +78,7 @@
4. Category `cashflow_kind` controls actual cashflow/report inclusion.
## Flow: `Cross-account and cancel/original reconciliation`
1. Preview functions search candidate pairs by amount, date, direction, account/source compatibility, and text similarity.
1. Preview helpers search candidate pairs by amount, date, direction, account/source compatibility, and text similarity.
2. Operator reviews candidates in the dashboard or runs the automatic matcher where confidence rules are satisfied.
3. Matched self-transfers become `이체 / 본인계좌이동`; investment movements become `이체 / 투자이동`; ambiguous rows remain `확인필요`.
4. Cancel/refund rows can be linked to original expense rows; memo tags preserve manual link evidence.
@@ -97,26 +89,23 @@
3. Risky import, rollback, categorization apply, reconciliation, or migration work is tested first against a synthetic fixture DB or repo-external validation DB copy selected through `JARCA_LEDGER_DB_PATH`.
4. Backup list/status/hash checks use `list_ledger_backups` and `get_ledger_backup_status`.
5. Restore execution is not an MCP flow. Restore smoke uses `scripts/ledger_restore_smoke.py` or the runbook and requires explicit approval before overwriting an existing target DB.
6. Verification evidence must not include full transaction dumps, full merchant/counterparty dumps, account identifiers, raw_payload, original financial file contents, or passwords.
## Flow: `Statement-unavailable manual balance reconciliation`
1. Operator follows the dcy-002 safety standard first: capture DB status, create or confirm a recent backup, and run preview/reconcile on a validation DB before operating on the production DB.
2. Operator marks accounts with `statement_status`: `manual_only` for statement-unavailable accounts, `partial_import` for accounts that require manual supplementation, or `import_supported` where normal statement import is sufficient.
3. Operator records manual evidence transactions with `source='manual'`, positive amount, date, account, and non-sensitive memo/source context.
4. Operator runs `get_balance_reconciliation_report` from MCP/service or the dashboard account page. If `baseline_at` is `YYYY-MM`, the snapshot time is that month end at 23:59 KST; ad-hoc reconciliation uses the user-supplied snapshot timestamp.
5. The report compares `baseline_balance`/`last_balance` with the latest `balance_after` if available, otherwise with net signed account transactions. Absolute tolerance is 1,000 KRW; 0.1% of baseline is reference-only.
6. `OK` rows need no immediate triage. `REVIEW_NEEDED` rows present manual decision candidates: `계좌이동`, `실지출`, `실수입`, `투자이동`, `분석제외`, `확인필요`.
7. This flow does not run automatic scheduling, notifications, restore, or auto-confirming mutation.
## Flow: `CI validation gates`
1. GitHub Actions starts `.github/workflows/ci.yml` on `pull_request`, `push` to `main`, or `workflow_dispatch`.
2. `lint` runs `ruff check .` with the conservative required rule set.
3. `contract-regression` runs `pytest tests/test_contract_regression.py` with synthetic pytest temp DBs and committed contract snapshots.
4. `pytest-regression` runs the non-dashboard regression suite.
5. `dashboard-app-test` runs `pytest tests/test_dashboard_app.py` using `scripts/ci_fixture_db.py` synthetic DB setup.
6. `dashboard-http-smoke` starts Streamlit against a synthetic DB and polls readiness.
7. `db-safety-smoke` runs restore smoke through temp source/backup/target DB paths only.
8. Any required job failure fails the workflow. Stable required-check job names remain `pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, `db-safety-smoke`, `contract-regression`, and `lint`.
## Flow: `Streamlit browser evidence capture`
1. Operator runs `scripts/streamlit_browser_evidence.py` from the repository root.
2. The script creates a synthetic fixture DB under the evidence output directory and refuses to use the default operating DB path.
3. Streamlit starts with `JARCA_LEDGER_DB_PATH=<fixture.sqlite3>` on a local loopback port.
4. `/usr/bin/google-chrome-stable` runs in headless mode through Chrome DevTools Protocol and opens target dashboard pages such as `분류 규칙`, `확인필요 큐`, and `Import`.
5. After page title text is rendered, the script stores PNG screenshots, DOM snapshots, and `manifest.json` in the evidence directory.
6. UI/dashboard-impacting batches must attach browser screenshot/snapshot evidence. Non-UI batches may treat it as optional, and docs-only batches do not require browser evidence.
7. If Chrome/browser capture is unavailable, AppTest + HTTP 200 fallback evidence is allowed only as `PASS_WITH_NOTES` with the environment failure and remaining browser-evidence item recorded.
8. Browser screenshot/DOM evidence is not a required GitHub Actions gate in BAT-006. Run it locally for visual/layout-sensitive dashboard changes, while non-visual backend/CI changes can rely on pytest/AppTest/HTTP/DB safety gates.
1. Operator runs `scripts/streamlit_browser_evidence.py` or starts Streamlit against a synthetic fixture DB.
2. The script/flow refuses to use the default operating DB path for evidence capture.
3. Browser smoke opens key pages such as `요약`, `자산/마감`, `분류 규칙`, `확인필요 큐`, and `Import`.
4. Verification records rendered headings/content and browser console JS error absence.
5. UI/dashboard-impacting batches must attach browser screenshot/DOM evidence or equivalent browser smoke notes.
## Flow Constraints
- Preview before broad import or categorization mutation where supported.
@@ -125,20 +114,14 @@
- Avoid destructive migrations without a backup/approval plan.
- Before broad DB mutation, capture DB status evidence and create or confirm a recent backup.
- Restore execution is script/runbook only and must not be exposed as an MCP tool.
- Manual balance reconciliation must remain preview/report-first unless a future REQ approves persistent review routing and confirmation mutation.
- Streamlit browser evidence must use synthetic/validation DBs only and must not screenshot/log real account numbers, balances, source financial filenames, passwords, full merchant/counterparty dumps, or raw payloads.
- CI gates must use synthetic/temp DBs only. The default operating DB path (`~/.local/share/jarca/ledger.sqlite3`) is forbidden for AppTest, HTTP smoke, and DB safety smoke wrappers.
- Contract regression must use synthetic/temp DBs only and must not expose runtime contract-version fields in MCP success payloads unless a future approved REQ changes the policy.
- Branch protection required-check drift should be audited against the six stable workflow job names documented in `docs/interface-contract.md`; changing a job name requires an explicit REQ/BAT note because it can break external repository settings.
- Dashboard and MCP asset valuation reports must use the shared service valuation policy: `accounts.last_balance` plus `manual_assets`, asset buckets minus loan, and missing valuation exclusion.
- Snapshot read tools are read-only in BAT-008. Snapshot creation and manual asset CRUD MCP mutation tools require a future REQ.
- Sensitive passwords are runtime inputs and must not be committed.
- Streamlit browser evidence must use synthetic/validation DBs only and must not screenshot/log real account numbers, source financial filenames, passwords, full merchant/counterparty dumps, or raw payloads.
- CI gates must use synthetic/temp DBs only. The default operating DB path is forbidden for AppTest, HTTP smoke, and DB safety smoke wrappers.
- Top-level `service.py`, `db.py`, and `server.py` import paths remain compatibility surfaces until a future approved REQ retires them.
## Current Gaps / Planned Changes
- Browser-level evidence is available locally through `scripts/streamlit_browser_evidence.py`; it remains outside required CI gates by design.
- Contract-regression and lint gates are now part of the required-check contract for CI; full MCP tool-surface snapshot coverage remains deferred beyond the representative health/status/list/report/mutation scope.
- Dedicated reconciliation workbench and persistent review-decision queue are deferred beyond REQ-003.
- Separate asset snapshot/manual asset tables and automatic market/real-estate valuation are deferred beyond REQ-005.
- Browser-level evidence exists locally and remains outside required CI gates by design.
- Phase 4+ write/import/advanced workflow service thinning and narrower adapters are deferred to GitHub Project TODO items.
- HTTP adapter and remote ops re-architecture remain deferred.
## Update Triggers
- Update this document when a command entry point changes.
@@ -151,5 +134,6 @@
- 2026-06-29 KST: REQ-003/BAT-003 기준으로 statement-unavailable 수동 잔액대조 flow 추가.
- 2026-06-29 KST: REQ-004/BAT-004 기준으로 Streamlit browser evidence capture flow 및 UI evidence gate 기준 추가.
- 2026-06-29 KST: REQ-005/BAT-005 기준으로 dashboard operations hub 및 `liquidity_bucket`/`last_balance` 자산 유동성 flow 추가.
- 2026-06-30 KST: REQ-006/BAT-006 기준으로 GitHub Actions CI validation gates, synthetic fixture DB boundary, AppTest/HTTP/DB safety smoke flow, local browser evidence boundary 추가.
- 2026-06-30 KST: REQ-007/REQ-008/BAT-007 기준으로 `contract-regression`/`lint` CI required-check flow, contract snapshot scope, branch protection drift boundary 추가.
- 2026-06-30 KST: REQ-006/BAT-006 기준으로 GitHub Actions CI validation gates, synthetic fixture DB boundary, AppTest/HTTP/DB safety smoke flow 추가.
- 2026-06-30 KST: REQ-007/REQ-008/BAT-007 기준으로 contract-regression/lint CI required-check flow 추가.
- 2026-07-03 KST: REQ-013/REQ-014/BAT-010 기준으로 service facade → application, db shim → infrastructure/sqlite, MCP/Streamlit adapter seam flow를 반영했다.
@@ -0,0 +1,84 @@
# REQ-014 MCP/Streamlit adapter seam realignment and contract-preserving gateway
- Status: Implemented
- Type: Interface
- Priority: High
- Owner: 렛저장인
- Source Discovery: `docs/discovery/dcy-010_20260702_backend-core-layering-mcp-adapter-realignment.md`
- Batch: BAT-010
- Created At (KST): 2026-07-03 00:15 KST
- Approved At (KST): 2026-07-03 00:15 KST
- Implemented At (KST): 2026-07-03 00:18 KST
## 1. 배경
`dcy-010`은 MCP server와 Streamlit dashboard가 giant service namespace에 직접 결합돼 있어 내부 구조 개선이 곧 external contract drift로 이어질 위험이 크다고 판단했다. Phase 0~3에서는 adapter를 얇게 만들되, tool names/response envelope/dashboard 핵심 페이지를 유지하는 것이 핵심이다.
## 2. Intent
이 REQ의 목적은 MCP와 Streamlit을 제거하는 것이 아니라, backend core 위에 얹힌 thin adapter로 재정렬하는 것이다. 즉 `server.py`와 `dashboard/app.py`가 더 좁은 seam을 통해 application core를 호출하면서도 기존 사용자의 운영 경험과 계약은 유지해야 한다.
## 3. 범위
### In Scope
- `jarca_ledger_mcp/adapters/mcp/server.py`를 도입하고 top-level `server.py`를 compatibility shim으로 유지한다.
- `jarca_ledger_mcp/adapters/streamlit/service_gateway.py`를 도입하고 dashboard가 이 gateway를 통해 service functions를 호출하도록 정렬한다.
- MCP tool discovery 결과(56 tools)와 success envelope contract를 유지한다.
- Streamlit 핵심 페이지(`요약`, `자산/마감` 포함)가 synthetic fixture DB에서 렌더링돼야 한다.
- baseline docs와 batch verification evidence에 adapter seam을 반영한다.
### Out of Scope
- 신규 MCP 도구 추가나 응답 schema 변경.
- 새로운 Streamlit IA/디자인 재구성.
- HTTP adapter/REST route 추가.
## 4. Requirement
### FR-1. MCP adapter thinness
- 실제 FastMCP registration은 `jarca_ledger_mcp/adapters/mcp/server.py`에 위치해야 한다.
- top-level `jarca_ledger_mcp/server.py`는 CLI/script entrypoint 호환을 위한 shim이어야 한다.
- MCP 도구명/설명/인자 contract는 유지돼야 한다.
### FR-2. Streamlit gateway seam
- `dashboard/app.py`는 giant service implementation이 아니라 명시적 gateway module을 통해 service functions를 사용해야 한다.
- gateway는 dashboard가 실제로 필요한 surface만 노출해야 한다.
- dashboard 렌더 결과는 synthetic fixture DB 기준 기존 핵심 페이지를 깨지 않아야 한다.
### FR-3. Contract-preserving verification
- full pytest regression과 MCP tool discovery가 PASS 해야 한다.
- browser-level smoke는 synthetic fixture DB에서 `요약`과 구조 변경 영향 페이지(`자산/마감`)를 렌더링해야 한다.
- 브라우저 콘솔 JS 에러가 없어야 한다.
### FR-4. Documentation alignment
- `docs/project-structure.md`와 `docs/runtime-flows.md`는 adapter/gateway seam과 compatibility shim 구조를 반영해야 한다.
- BAT verification 문서는 synthetic-only evidence와 잔여 Phase 4+ TODO를 기록해야 한다.
## 5. Acceptance Criteria
- AC-1: `jarca_ledger_mcp/adapters/mcp/server.py`와 top-level `server.py` shim이 존재하고 `hermes mcp test jarca-ledger`가 PASS 한다.
- AC-2: `jarca_ledger_mcp/adapters/streamlit/service_gateway.py`가 도입되고 `dashboard/app.py`가 이를 사용한다.
- AC-3: full pytest가 PASS 하고 Streamlit AppTest smoke가 계속 PASS 한다.
- AC-4: local browser smoke에서 `요약`과 `자산/마감` 페이지가 렌더링되고 console JS errors가 없다.
- AC-5: baseline docs와 batch evidence 문서가 새 adapter seam을 반영한다.
## 6. Impacted Area
- `jarca_ledger_mcp/server.py`
- `jarca_ledger_mcp/adapters/mcp/server.py`
- `jarca_ledger_mcp/adapters/streamlit/service_gateway.py`
- `dashboard/app.py`
- `tests/test_dashboard_app.py`
- `tests/test_contract_regression.py`
- `docs/project-structure.md`
- `docs/runtime-flows.md`
## 7. Notes
- 이 REQ는 MCP/Streamlit surface와 operator-facing 호출 경계를 다루므로 `Interface` 타입으로 관리한다.
- gateway는 현재 dashboard가 사용하는 surface만 노출하며, Phase 4+에서 더 좁은 domain-specific gateway로 재분할할 수 있다.
## 8. Change Log
- 2026-07-03 00:15 KST: dcy-010 confirmed 범위를 바탕으로 Proposed 초안을 생성.
- 2026-07-03 00:15 KST: 사용자 승인 방향(계약 동결, adapter thinness, Phase 0~3 우선)에 따라 Approved로 전환.
- 2026-07-03 00:18 KST: MCP adapter split, Streamlit gateway 도입, browser smoke, MCP discovery, full pytest PASS를 근거로 Implemented로 전환.
@@ -0,0 +1,87 @@
# REQ-013 Backend core layering / SQLite infrastructure split and service facade compatibility
- Status: Implemented
- Type: Non-Functional
- Priority: High
- Owner: 렛저장인
- Source Discovery: `docs/discovery/dcy-010_20260702_backend-core-layering-mcp-adapter-realignment.md`
- Batch: BAT-010
- Created At (KST): 2026-07-03 00:15 KST
- Approved At (KST): 2026-07-03 00:15 KST
- Implemented At (KST): 2026-07-03 00:18 KST
## 1. 배경
`dcy-010` confirmed Discovery는 `jarca_ledger_mcp/service.py`와 `jarca_ledger_mcp/db.py`에 과도하게 집중된 책임 때문에 MCP adapter, Streamlit dashboard, SQLite persistence가 하나의 giant module 집합에 강결합돼 있다고 판단했다. Phase 0~3 우선 범위에서는 기능 계약을 바꾸지 않고도 backend core의 최소 layering을 세워 이후 write/import/advanced workflow 분리를 안전하게 이어갈 수 있어야 한다.
## 2. Intent
이 REQ의 목적은 서비스/DB 코드를 한 번에 다시 쓰는 것이 아니라, 기존 동작을 유지한 채 `application` / `infrastructure/sqlite` / compatibility facade 경계를 도입해 코드 탐색성, 변경 안전성, 후속 분리 가능성을 확보하는 것이다.
## 3. 범위
### In Scope
- `jarca_ledger_mcp/service.py`를 compatibility facade로 축소하고 실제 구현을 application 모듈로 분리한다.
- 공통 helper/정책을 `jarca_ledger_mcp/application/shared.py`로 정리한다.
- SQLite connection/schema/migration 책임을 `jarca_ledger_mcp/infrastructure/sqlite/`로 이동한다.
- 기존 `jarca_ledger_mcp/db.py` import surface는 compatibility shim으로 유지한다.
- additive/idempotent migration discipline과 기존 테스트 contract를 유지한다.
- `docs/project-structure.md`, `docs/runtime-flows.md`에 새 경계를 반영한다.
### Out of Scope
- FastAPI/HTTP adapter 추가.
- write/import/advanced workflow의 본격적인 Phase 4+ domain 분리.
- 데이터 모델 semantics 변경, 응답 envelope 변경, destructive migration.
## 4. Requirement
### FR-1. Application service decomposition
- 기존 `service.py`의 기능 구현은 최소한 `system`, `asset/account`, `transaction/report`, `import/transfer`, `categorization` 성격의 application 모듈로 분리돼야 한다.
- shared helper와 공통 정책(`cashflow_kind`, KST clock, dedupe, validation helpers)은 application shared module로 이동해야 한다.
- 분리 후에도 기존 `from jarca_ledger_mcp import service` 사용자는 동일 함수명을 계속 사용할 수 있어야 한다.
### FR-2. SQLite infrastructure split
- 기존 `db.py`의 connection/schema/migration 책임은 `jarca_ledger_mcp/infrastructure/sqlite/`로 이동해야 한다.
- top-level `db.py`는 기존 import 경로를 유지하는 compatibility shim이어야 한다.
- `db()` context manager는 기존처럼 초기화/commit/rollback semantics를 유지해야 한다.
- 스키마 migration은 additive/idempotent 원칙을 유지해야 한다.
### FR-3. Service facade compatibility
- `jarca_ledger_mcp/service.py`는 thin facade여야 하며 구현 디테일 대신 application 모듈을 재노출해야 한다.
- 기존 pytest/service call sites와 monkeypatch 관성(`_current_kst_datetime`)은 깨지지 않아야 한다.
- facade 축소 결과는 더 이상 giant implementation file이 아니어야 한다.
### FR-4. Baseline documentation alignment
- `docs/project-structure.md`는 새 package tree와 책임 경계를 반영해야 한다.
- `docs/runtime-flows.md`는 service facade, Streamlit gateway, MCP adapter, SQLite infrastructure flow를 반영해야 한다.
- 문서는 Phase 4+가 GitHub Project TODO로 남아 있음을 드러내야 한다.
## 5. Acceptance Criteria
- AC-1: `jarca_ledger_mcp/application/` 아래에 phase-0~3 범위의 application service modules와 shared module이 생성된다.
- AC-2: `jarca_ledger_mcp/infrastructure/sqlite/` 아래에 connection/schema/migration 분리가 생성되고 top-level `db.py` shim이 동작한다.
- AC-3: `jarca_ledger_mcp/service.py`는 compatibility facade로 축소되고 600줄 이하를 유지한다.
- AC-4: 전체 pytest regression이 PASS 하며 기존 service/db import surface가 유지된다.
- AC-5: `docs/project-structure.md`, `docs/runtime-flows.md`가 새 계층 구조를 반영한다.
## 6. Impacted Area
- `jarca_ledger_mcp/service.py`
- `jarca_ledger_mcp/db.py`
- `jarca_ledger_mcp/application/*`
- `jarca_ledger_mcp/infrastructure/sqlite/*`
- `tests/test_service.py`
- `tests/test_asset_snapshots.py`
- `docs/project-structure.md`
- `docs/runtime-flows.md`
## 7. Notes
- 이 REQ는 주로 maintainability/architecture boundary와 compatibility behavior를 다루므로 `Non-Functional`로 관리한다.
- write/import/advanced workflow의 deeper split은 dcy-010에서 별도 Phase 4+ TODO로 남겼다.
## 8. Change Log
- 2026-07-03 00:15 KST: dcy-010 confirmed 범위를 바탕으로 Proposed 초안을 생성.
- 2026-07-03 00:15 KST: 사용자 승인 방향(Phase 0~3 우선, facade 축소, 계약 동결)에 따라 Approved로 전환.
- 2026-07-03 00:18 KST: application/infrastructure split, facade/shim compatibility, full pytest PASS, MCP tool discovery, browser smoke evidence를 근거로 Implemented로 전환.
+6 -2
View File
@@ -28,12 +28,12 @@
| **Integration** | 외부 시스템 연동, 플러그인, 확장 | KB/토스/서울페이 import |
| **Interface** | 사용자 상호작용 방식 | MCP tool, Streamlit UI |
| **Migration** | 데이터 이전, 버전 업그레이드 | SQLite schema migration |
| **Non-Functional** | 성능, 보안, 확장성, 호환성, 신뢰성 | 개인정보/금융정보 보호, UX 성능 |
| **Non-Functional** | 성능, 보안, 확장성, 호환성, 신뢰성 | 개인정보/금융정보 보호, UX 성능, 아키텍처 유지보수성 |
| **Testing** | 테스트 전략, QA 요구사항, 검증 방식 | parser regression, report validation |
## Next Requirement ID
- Current: `REQ-013`
- Current: `REQ-015`
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
## Requirement Register
@@ -52,6 +52,8 @@
| REQ-010 | Asset snapshot/net worth read MCP and dashboard alignment | Interface | Implemented | High | 렛저장인 | [req-010_asset-snapshot-net-worth-read-mcp-and-dashboard-alignment.md](Interface/req-010_asset-snapshot-net-worth-read-mcp-and-dashboard-alignment.md) |
| REQ-011 | Manual asset CRUD / month-end snapshot write model | Data | Implemented | High | 렛저장인 | [req-011_manual-asset-crud-month-end-snapshot-write-model.md](Data/req-011_manual-asset-crud-month-end-snapshot-write-model.md) |
| REQ-012 | Manual asset CRUD / snapshot close MCP and operator dashboard alignment | Interface | Implemented | High | 렛저장인 | [req-012_manual-asset-crud-snapshot-close-mcp-and-operator-dashboard-alignment.md](Interface/req-012_manual-asset-crud-snapshot-close-mcp-and-operator-dashboard-alignment.md) |
| REQ-013 | Backend core layering / SQLite infrastructure split and service facade compatibility | Non-Functional | Implemented | High | 렛저장인 | [req-013_backend-core-layering-sqlite-infrastructure-service-facade-compatibility.md](Non-Functional/req-013_backend-core-layering-sqlite-infrastructure-service-facade-compatibility.md) |
| REQ-014 | MCP/Streamlit adapter seam realignment and contract-preserving gateway | Interface | Implemented | High | 렛저장인 | [req-014_mcp-streamlit-adapter-seam-realignment-contract-preserving-gateway.md](Interface/req-014_mcp-streamlit-adapter-seam-realignment-contract-preserving-gateway.md) |
## Recent Change Log Summary
@@ -76,3 +78,5 @@
- 2026-07-01 09:00 KST: dcy-009 confirmed 범위를 `REQ-011`(manual asset CRUD / month-end snapshot write model)과 `REQ-012`(manual asset CRUD / snapshot close MCP and operator dashboard alignment) Proposed 상태로 생성했다.
- 2026-07-01 09:02 KST: `REQ-011`, `REQ-012`를 승인 게이트 재검토 후 Approved로 전환했다.
- 2026-07-01 10:52 KST: BAT-009 구현·검증 evidence, full pytest PASS, MCP tool discovery, browser smoke를 근거로 `REQ-011`, `REQ-012`를 Implemented로 전환했다.
- 2026-07-03 00:15 KST: dcy-010 confirmed 범위를 `REQ-013`, `REQ-014`로 생성하고 승인 상태를 정리했다.
- 2026-07-03 00:18 KST: BAT-010 구현·검증 evidence, full pytest PASS, MCP tool discovery, browser smoke를 근거로 `REQ-013`, `REQ-014`를 Implemented로 전환했다.