diff --git a/dashboard/app.py b/dashboard/app.py index 3edd170..be9e9b8 100644 --- a/dashboard/app.py +++ b/dashboard/app.py @@ -825,12 +825,12 @@ def page_rules(db_path: str) -> None: c1, c2, c3 = st.columns(3) if c1.button("기본 규칙 seed/update"): - res = service.seed_default_category_rules() + res = service.seed_default_category_rules(db_path=db_path) clear_cache(); st.success(res) if c2.button("규칙 preview"): - st.session_state["rule_preview"] = service.preview_categorization(limit=5000) + st.session_state["rule_preview"] = service.preview_categorization(limit=5000, db_path=db_path) if c3.button("규칙 적용"): - res = service.apply_categorization_rules(limit=5000) + res = service.apply_categorization_rules(limit=5000, db_path=db_path) clear_cache(); st.success(f"{res.get('applied_count', 0)}건 적용") if "rule_preview" in st.session_state: prev = st.session_state["rule_preview"] diff --git a/docs/batches/bat-001_20260626_category-rule-ops/implementation.md b/docs/batches/bat-001_20260626_category-rule-ops/implementation.md new file mode 100644 index 0000000..996c0b6 --- /dev/null +++ b/docs/batches/bat-001_20260626_category-rule-ops/implementation.md @@ -0,0 +1,37 @@ +# BAT-001 Implementation + +## Status +- Status: implemented-qc-passed +- Last Updated (KST): 2026-06-26 23:41 + +## Changed Files +- `jarca_ledger_mcp/server.py` + - `delete_category_rule(rule_id: str)` MCP tool을 additive 등록했다. +- `dashboard/app.py` + - 분류 규칙 page의 `seed_default_category_rules`, `preview_categorization`, `apply_categorization_rules` 호출이 page-level `db_path`를 전달하도록 수정했다. +- `tests/test_category_rule_filters.py` + - category rule 삭제 성공, 삭제 후 list 제외, missing ID `not_found` regression을 추가했다. +- StagePilot 문서 + - `docs/srs/Interface/req-001_category-rule-ops.md` + - `docs/srs/index.md` + - `docs/batches/bat-001_20260626_category-rule-ops/*` + - `docs/batches/index.md` + - `docs/discovery/dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows.md` + +## Execution Log +- 2026-06-26 23:31 KST: batch-lite 계획 수립. +- 2026-06-26 23:33 KST: MCP delete tool, Streamlit db_path 전달, deletion regression test 구현. +- 2026-06-26 23:36 KST: service/pytest/MCP/Streamlit AppTest smoke 검증 완료. +- 2026-06-26 23:41 KST: 검수관 QC gate `PASS_WITH_NOTES` 확인, blocker 없음. + +## Validation During Implementation +- `python -m py_compile jarca_ledger_mcp/*.py dashboard/app.py`: pass. +- `pytest tests/test_category_rule_filters.py`: 3 passed. +- `pytest tests/test_cross_account_transfer_matching.py`: 3 passed. +- `pytest`: 17 passed, 4 skipped. +- `hermes mcp test jarca-ledger`: connected, 41 tools discovered, `delete_category_rule` discovered. +- Streamlit smoke: port 8502 synthetic fixture DB `/tmp/jarca-ledger-bat-001-smoke.sqlite3`; browser tool unavailable because local Chromium snap is not installed, so `streamlit.testing.v1.AppTest` was used. 분류 규칙 page rendered without exceptions; title/subheader/buttons/rule item were visible; preview/apply buttons ran without errors; default 운영 DB mtime unchanged. + +## Remaining Risks +- Browser screenshot evidence was not captured because `/usr/bin/chromium-browser` requires the unavailable chromium snap. AppTest + HTTP 200 smoke was used as substitute evidence. +- Import/rollback and cancel/refund workflows remain outside BAT-001 and should be handled by later REQ/BAT. diff --git a/docs/batches/bat-001_20260626_category-rule-ops/index.md b/docs/batches/bat-001_20260626_category-rule-ops/index.md new file mode 100644 index 0000000..6977849 --- /dev/null +++ b/docs/batches/bat-001_20260626_category-rule-ops/index.md @@ -0,0 +1,35 @@ +# BAT-001: Category Rule Ops Safety + +## 0. 문서 상태 +- BAT ID: BAT-001 +- Profile: batch-lite +- Status: release-candidate +- Source Discovery: dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows +- Included REQ: REQ-001 +- Created At (KST): 2026-06-26 23:31 +- Last Updated (KST): 2026-06-26 23:41 + +## 1. Scope +분류 규칙 lifecycle 중 가장 작고 검증 가능한 운영 액션을 안정화한다. + +### Included +- Streamlit 분류 규칙 seed/preview/apply 액션이 선택된 `db_path`를 사용하도록 수정. +- MCP tool surface에 `delete_category_rule` additive 등록. +- service-level category rule deletion regression test 추가. +- py_compile, pytest, MCP tool smoke, Streamlit screenshot/browser smoke 증거 기록. + +### Excluded +- 신규 parser 추가. +- 파괴적 SQLite migration. +- 운영 원본 DB 직접 mutation. +- import/rollback 전체 E2E와 취소/환급 연결 workflow 구현. + +## 2. Documents +- Planning: [planning.md](planning.md) +- Implementation: [implementation.md](implementation.md) +- Verification: [verification.md](verification.md) + +## 3. Notes +- 단일 REQ, additive MCP tool, UI DB-path bug fix만 포함하므로 `batch-lite`로 진행한다. +- 구현 중 구조/런타임 flow 변화가 커지면 design 문서를 추가한다. +- 검수관 QC gate 결과 `PASS_WITH_NOTES`; blocker 없음. Browser screenshot은 Chromium snap 부재로 대체 smoke(AppTest) 증거를 사용했다. diff --git a/docs/batches/bat-001_20260626_category-rule-ops/planning.md b/docs/batches/bat-001_20260626_category-rule-ops/planning.md new file mode 100644 index 0000000..1f88586 --- /dev/null +++ b/docs/batches/bat-001_20260626_category-rule-ops/planning.md @@ -0,0 +1,21 @@ +# BAT-001 Planning + +## 1. Goal +REQ-001의 분류 규칙 운영 액션을 작은 단위로 구현하고 검증한다. + +## 2. Design Gate +- Decision: design.md 생략, batch-lite 유지. +- Rationale: schema 변경이나 신규 runtime flow 없이 기존 service function을 MCP에 additive 등록하고 dashboard 호출부의 `db_path` 전달 누락을 수정하는 국소 변경이다. + +## 3. Implementation Plan +1. `server.py`에 `delete_category_rule(rule_id: str)` MCP tool을 추가한다. +2. `dashboard/app.py`의 분류 규칙 seed/update, preview, apply 호출에 `db_path=db_path`를 전달한다. +3. `tests/test_category_rule_filters.py`에 삭제 성공/삭제 후 list 제외/없는 ID 실패 regression을 추가한다. +4. 검증 명령을 실행하고 결과를 `verification.md`에 기록한다. +5. 검수관 QC gate PASS 후 REQ-001을 Implemented로 전환한다. + +## 4. Risks and Mitigations +- Risk: Streamlit smoke 중 기본 운영 DB를 건드릴 수 있음. + - Mitigation: `JARCA_LEDGER_DB_PATH`를 tmp synthetic DB로 지정해 dashboard를 실행한다. +- Risk: MCP tool 목록이 캐시되어 신규 tool이 보이지 않을 수 있음. + - Mitigation: `hermes mcp test jarca-ledger`로 새 프로세스 기준 smoke를 수행한다. diff --git a/docs/batches/bat-001_20260626_category-rule-ops/verification.md b/docs/batches/bat-001_20260626_category-rule-ops/verification.md new file mode 100644 index 0000000..57bfb3f --- /dev/null +++ b/docs/batches/bat-001_20260626_category-rule-ops/verification.md @@ -0,0 +1,47 @@ +# BAT-001 Verification + +## Status +- Ready for Release: true +- QC Verdict: PASS_WITH_NOTES +- Last Updated (KST): 2026-06-26 23:41 + +## Acceptance Mapping + +| REQ | AC | Evidence | Result | +| --- | --- | --- | --- | +| REQ-001 | AC-1 | `dashboard/app.py` diff: `seed_default_category_rules`, `preview_categorization`, `apply_categorization_rules` now receive `db_path=db_path`; Streamlit AppTest preview/apply smoke used `JARCA_LEDGER_DB_PATH=/tmp/jarca-ledger-bat-001-smoke.sqlite3` and default DB mtime stayed unchanged. | Pass | +| REQ-001 | AC-2 | `jarca_ledger_mcp/server.py` diff adds `delete_category_rule(rule_id: str)` MCP tool; `hermes mcp test jarca-ledger` discovered 41 tools including `delete_category_rule`. | Pass | +| REQ-001 | AC-3 | `pytest tests/test_category_rule_filters.py` includes `test_delete_category_rule_removes_rule_and_reports_missing_ids`; result: 3 passed. | Pass | +| REQ-001 | AC-4 | py_compile, targeted pytest, full pytest, and MCP smoke all passed; command outputs summarized below. | Pass | +| REQ-001 | AC-5 | Streamlit was run with synthetic fixture DB `/tmp/jarca-ledger-bat-001-smoke.sqlite3`; no original financial file/password/real account fixture used; `/root/.local/share/jarca/ledger.sqlite3` mtime before/after preview/apply smoke remained `1782190349`. | Pass with substitute smoke | + +## Evidence Log +- `python -m py_compile jarca_ledger_mcp/*.py dashboard/app.py` → exit 0. +- `pytest tests/test_category_rule_filters.py` → `3 passed in 1.77s`. +- `pytest tests/test_cross_account_transfer_matching.py` → `3 passed in 1.92s`. +- `pytest` → `17 passed, 4 skipped in 17.63s`. +- `hermes mcp test jarca-ledger` → connected in 2460ms; 41 tools discovered; `delete_category_rule` listed. +- Streamlit server smoke: + - port 8501 was occupied, so port 8502 was used. + - `curl -I http://127.0.0.1:8502` → `HTTP/1.1 200 OK`. + - Browser tool failed before page load because Chromium snap is not installed: `Chrome exited early ... requires the chromium snap to be installed`. + - Substitute AppTest smoke output: `exceptions 0`, title `⚙️ 분류 규칙`, subheader `현재 분류 규칙`, buttons `추가`, `변경`, `삭제`, `기본 규칙 seed/update`, `규칙 preview`, `규칙 적용`, synthetic rule text `스모크상점` present. + - AppTest preview/apply output: `preview_exceptions 0`, info `스캔 0건 · 매칭 0건 · 미분류 0건`, `apply_exceptions 0`, success `0건 적용`, default DB mtime unchanged. + +## Blocking Issues +- 없음. + +## QC Gate +- Reviewer profile: `jarcaledgerqc` (검수관) +- Command: `jarcaledgerqc chat -q "QC gate 요청..."` +- Verdict: `PASS_WITH_NOTES` +- Blockers: 없음 +- Notes: + - Browser-level screenshot evidence was unavailable because Chromium snap is not installed; AppTest + HTTP 200 was accepted as substitute evidence for this small BAT. + - Future Streamlit UX/E2E batches should restore real browser/screenshot evidence. + - Optional future strengthening: temp MCP subprocess create/delete/list actual tool-call E2E. + +## Notes +- No destructive migration. +- No direct mutation of the original 운영 DB during smoke; only mtime check was performed on default DB. +- Browser screenshot evidence was attempted but blocked by missing Chromium; AppTest + HTTP 200 evidence is the recorded semi-automated substitute. diff --git a/docs/batches/index.md b/docs/batches/index.md index 158750c..4fe964c 100644 --- a/docs/batches/index.md +++ b/docs/batches/index.md @@ -8,3 +8,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/) | diff --git a/docs/discovery/dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows.md b/docs/discovery/dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows.md new file mode 100644 index 0000000..533cc80 --- /dev/null +++ b/docs/discovery/dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows.md @@ -0,0 +1,216 @@ +# 0. 문서 상태 +이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다. + +- 상태: approved +- 문서 ID: dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows +- 이슈명: Streamlit 운영 UX/E2E 검증 및 분류·환급 워크플로우 안정화 +- 작성 시각(KST): 2026-06-26 22:38 +- 마지막 갱신 시각(KST): 2026-06-26 23:31 +- 대체됨: 없음 +- 후속 Discovery 참조: 없음 +- 생성된 REQ 참조: REQ-001 ([docs/srs/Interface/req-001_category-rule-ops.md](../srs/Interface/req-001_category-rule-ops.md)) + +# 1. 계획 상태 요약 +현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다. + +- 연계 문서 현황: + - 관련 점수 파일: 미생성 + - 관련 TODO/메모: 없음 +- 해석: + - StagePilot baseline 초기화 이후 첫 real Discovery로, 현재 저장소에는 Discovery/REQ/Batch/Release 이력이 없고 baseline 문서만 존재한다. + - 이번 Discovery는 Jarca Ledger MCP의 핵심 운영 화면인 Streamlit 대시보드에서 분류, 확인필요 큐, 취소/환급 원거래 연결, import/rollback 흐름을 실제 사용 가능한 수준으로 검증·안정화하는 범위를 정의한다. + +# 2. 요구사항 판정 결과 +이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다. + +- 판정: 신규 +- 근거 + - `docs/discovery/`에는 현재 `index.md`만 존재하며, 기존 Discovery 문서가 없어 중복 또는 갱신 대상이 없다. + - baseline 문서들이 공통적으로 Streamlit UX/E2E 검증, 분류 규칙 삭제, 취소/환급 원거래 연결, import E2E를 gap으로 기록하고 있어 독립 Discovery로 추적할 필요가 있다. + +# 3. 문제점의 요약 +문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다. + +- 현재 상태 + - `dashboard/app.py`에는 미분류 거래 수정, 연결거래찾기, 원거래 후보 dialog, 분류 규칙 생성/수정, Import 관련 UI가 구현되어 있고, `service.py`에는 `find_cancel_original_candidates`, `find_self_transfer_candidates`, import batch, categorization, `cashflow_kind` 관련 서비스 로직이 구현되어 있다. + - 그러나 baseline 문서 기준으로 브라우저 레벨 UX 검증, 실제 row 기반 import preview/import/rollback, 분류 규칙 삭제 및 취소/환급 원거래 연결 흐름에 대한 명시적 acceptance criteria와 E2E 검증 증거가 아직 없다. +- 기대 상태 + - 운영자가 Streamlit 대시보드에서 실제 가계부 DB를 대상으로 거래 분류, 규칙 생성/수정/삭제, 확인필요 큐 처리, 취소/환급 원거래 연결, import batch 조회/rollback을 끊김 없이 수행할 수 있다. + - `cashflow_kind`가 실제 수입/지출과 계좌이동·투자이동·분석제외를 올바르게 구분하며, UI 조작 후 월간 현금흐름/카테고리 지출/고정비 등 보고서가 일관된 결과를 보여준다. +- 이해관계자 + - Jarca Ledger 운영자: 실제 가계부 데이터를 검토하고 분류·정정·import를 수행하는 사용자 + - Jarca/Hermes Agent: MCP 도구와 서비스 레이어를 통해 거래 조회, 분류, 보고, 검증을 보조하는 자동화 주체 + - 프로젝트 유지보수자: Streamlit UI, service layer, parser, SQLite schema, 테스트/문서를 유지하는 개발 주체 +- 제약사항 + - 금융/가계부 데이터와 PDF/XLSX 원본은 민감정보이므로 테스트·문서·커밋에 실제 원본 데이터나 비밀번호를 남기지 않는다. + - 기존 MCP tool 성공 응답 shape와 `cashflow_kind` 의미론은 호환성을 우선하며, 파괴적 DB migration 없이 검증 가능한 개선부터 진행한다. + +## 주요 사용자 시나리오 +사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다. + +- 운영자가 새로 import한 거래 중 미분류/확인필요 항목을 Streamlit에서 확인하고, 개별 거래를 분류한 뒤 같은 merchant에 대한 규칙을 저장한다. +- 운영자가 카드 취소/환급 입금 거래를 선택하고 원거래 후보 dialog에서 실제 원거래를 연결한 뒤, 지출/환급 보고서가 중복 없이 반영되는지 확인한다. +- 운영자가 KB Bank, Toss Bank, Toss Securities, SeoulPay 자료를 preview/import하고, 잘못 들어온 batch를 rollback하여 DB와 UI 상태가 일관되게 복구되는지 확인한다. + +# 4. 이번에 정의할 변경 +이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다. + +- Streamlit 대시보드의 분류·규칙·확인필요 큐 UX를 운영 시나리오 단위로 검증하고, 발견된 결함을 REQ 후보로 분리한다. +- 취소/환급 원거래 후보 탐색과 수동 연결 workflow를 실제 데이터 샘플 또는 안전한 fixture로 검증하고, 보고서 반영 기준을 정의한다. +- import preview/import/rollback E2E와 `cashflow_kind` 기반 보고서 일관성 검증 범위를 정의하고, 필요한 테스트/문서 갱신 항목을 도출한다. + +## 영향 범위 +변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다. + +- 코드 영향: + - `dashboard/app.py`: 분류 규칙 UI, 미분류/확인필요 큐, 연결거래/원거래 후보 dialog, import/rollback 화면. + - `jarca_ledger_mcp/service.py`: category rule CRUD/apply, cancel-original candidate/linking, self-transfer matching, import batch rollback, report aggregation. + - `tests/`: service regression과 필요 시 dashboard smoke/E2E 또는 fixture 기반 검증 추가. +- 문서 영향: + - `docs/discovery/index.md`: 이번 Discovery 항목 추가. + - 후속 REQ/SRS 문서: UI acceptance criteria, E2E 검증 조건, 보고서 일관성 조건을 기능/비기능 요구로 분리. + - 필요 시 `docs/runtime-flows.md`와 `docs/interface-contract.md`: 검증 완료 후 확정된 Streamlit 운영 flow와 UI contract 갱신. +- 운영 영향: + - 실제 DB를 직접 건드리는 검증은 backup 또는 fixture DB에서 먼저 수행해야 한다. + - 사용자 운영 DB에 적용하는 변경은 import rollback과 category/report 결과를 재확인한 뒤 진행해야 한다. + +# 5. 요구사항 목록 +실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다. + +## 기능 요구사항 +- FR-1: Streamlit 분류 규칙 화면은 규칙 목록, 생성, 수정, 삭제, preview/apply 진입을 제공하고, 규칙 조건(`match_field`, `pattern`, `transaction_type`, `amount_min`, `amount_max`, `day_of_month`, `is_fixed`)이 운영자가 이해 가능한 순서로 표시되어야 한다. +- FR-2: 미분류/확인필요 큐 화면은 개별 거래 분류, 규칙 저장, 연결거래찾기, 원거래찾기 동작을 제공하고, 성공/실패 후 화면 상태가 최신 DB 상태와 일치해야 한다. +- FR-3: 취소/환급 거래는 후보 탐색, 후보 비교, 수동 연결, memo/tag evidence 기록, 보고서 반영까지 하나의 검증 가능한 workflow로 정의되어야 한다. +- FR-4: import preview/import/rollback 화면은 KB Bank PDF, Toss Bank PDF, Toss Securities PDF, SeoulPay XLSX의 preview-first 흐름과 batch rollback 결과를 사용자가 확인할 수 있어야 한다. +- FR-5: `cashflow_kind`가 실제 수입/지출·이체·투자이동·부채상환·분석제외를 구분하는 방식이 UI와 report 결과에서 일관되게 확인되어야 한다. + +## 비기능 요구사항 +성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다. + +- NFR-1: 실제 금융 데이터 검증은 민감정보를 노출하지 않는 fixture, masked sample, 또는 로컬 전용 DB 복사본을 사용해야 한다. +- NFR-2: 기존 MCP tool의 성공 응답 shape와 DB schema는 후속 REQ에서 명시적으로 승인하지 않는 한 깨지지 않아야 한다. +- NFR-3: 브라우저 레벨 검증은 재현 가능한 체크리스트 또는 자동화 가능한 smoke/E2E 절차로 남아야 한다. + +## 범위 경계 +이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다. + +- In Scope + - Streamlit 대시보드의 분류 규칙, 미분류/확인필요 큐, 취소/환급 원거래 연결, import/rollback, report 반영 검증. + - service layer와 UI 간 contract mismatch, stale cache/rerun 문제, success/error 표시 문제, `cashflow_kind` 표시/집계 오류의 발견과 REQ화. + - 안전한 fixture 또는 로컬 DB 복사본 기반의 E2E 검증 절차 수립. +- Out of Scope + - 신규 금융기관 parser 추가. + - 파괴적 SQLite schema migration 또는 자동 backup framework 구현. + - CI/CD 전체 구축과 배포 서비스화. + - Jarca Ledger 외 Jarca 본체 MCP 기능 변경. + +# 6. 리스크/가정 목록 +불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다. + +## 리스크 +- R-1 (High): 실제 운영 DB에서 잘못된 import, rollback, category update를 실행하면 금융 데이터가 왜곡될 수 있다. + - 완화 방안: fixture DB 또는 운영 DB 복사본에서 먼저 재현하고, 실제 DB 검증 전 backup/rollback 절차를 확인한다. +- R-2 (Medium): Streamlit dialog/rerun/cache 동작이 서비스 성공 결과와 화면 표시를 불일치하게 만들 수 있다. + - 완화 방안: 각 UI action 후 DB 조회와 화면 refresh 결과를 함께 검증하는 acceptance criteria를 REQ에 포함한다. +- R-3 (Medium): 취소/환급 후보 탐색이 merchant/amount/date만으로 과도하게 매칭되어 잘못된 원거래를 추천할 수 있다. + - 완화 방안: 후보 점수, 동일 계좌/결제수단/텍스트 근거, 수동 확인 evidence를 UI에 노출하는 기준을 정의한다. + +## 가정 +아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다. + +- A-1: 현재 우선 목표는 기능 추가보다 이미 구현된 Streamlit 운영 workflow를 실제 사용 가능한 수준으로 검증·정리하는 것이다. +- A-2: 테스트와 문서에는 실제 금융 원본 파일, 비밀번호, 계좌 식별정보 전체를 포함하지 않는다. +- A-3: 후속 구현은 작은 REQ와 batch로 나누며, 기존 service success response shape와 `cashflow_kind` 의미론을 보존한다. + +## 오픈 질문 +답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다. + +- OQ-1: 브라우저 레벨 검증을 수동 체크리스트 중심으로 둘지, Playwright/Selenium 같은 자동화 smoke 테스트까지 포함할지 결정해야 한다. + - 상태: Answered + - 결정: semi-automated. 서비스/DB 레벨 회귀 테스트를 기본 증거로 삼고, Streamlit 핵심 흐름은 브라우저 또는 스크린샷 기반 smoke 검증을 병행한다. + - 반영 위치: NFR-3, S-1~S-3, 후속 REQ acceptance criteria 및 batch verification 계획. +- OQ-2: 실제 운영 DB 복사본을 사용한 검증을 허용할지, 완전 synthetic fixture DB만 사용할지 결정해야 한다. + - 상태: Answered + - 결정: synthetic fixture 우선. 실제 재현성이 필요한 경우에만 운영 DB 복사본을 사용하되, 원본 DB 직접 mutation은 금지하고 민감정보는 보고서/문서에 노출하지 않는다. + - 반영 위치: NFR-1, R-1 완화 방안, 후속 REQ 테스트 데이터 조건. + +### 오픈 질문 처리 기준 +- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다. +- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다. +- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다. +- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다. +- 처리 원칙 1: 질문만 남기지 말고 반드시 `상태`, `처리 방안`, `종료 조건`을 함께 적는다. +- 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다. +- 처리 원칙 3: High 영향 질문은 `Deferred` 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다. + +# 7. 초기 성공 기준 +이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다. + +- S-1: 분류 규칙 생성/수정/삭제와 preview/apply 흐름이 fixture 또는 검증 DB에서 오류 없이 수행되고, 처리 후 rule list와 transaction category 상태가 DB와 UI에서 일치한다. +- S-2: 취소/환급 원거래 연결 workflow가 후보 탐색, 수동 선택, memo/tag evidence 기록, 월간 cashflow/report 반영까지 재현 가능하게 검증된다. +- S-3: 각 import source의 preview/import/rollback 핵심 경로가 중복 처리, batch 기록, rollback 후 상태를 포함해 검증되고, 실패 시 사용자에게 원인을 표시한다. +- S-4: 후속 REQ 초안 작성에 필요한 acceptance criteria와 검증 데이터 정책이 확정되거나 명시적으로 Deferred 처리된다. + +## 측정 방식 및 데이터 출처 +각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다. + +- S-1 측정 방식: UI action 후 service 조회와 SQLite row 상태 비교 / 데이터 출처: fixture DB, `category_rules`, `transactions`, Streamlit 화면 캡처 또는 체크리스트 +- S-2 측정 방식: 연결 전후 transaction memo/category/type/report aggregate 비교 / 데이터 출처: fixture DB, `find_cancel_original_candidates`, `link_cancel_to_original`, monthly/category report output +- S-3 측정 방식: import batch 생성/row count/rollback status/duplicate skip count 확인 / 데이터 출처: masked sample files 또는 synthetic parser fixtures, `import_batches`, `transactions` +- S-4 측정 방식: Discovery review 결과와 후속 REQ register 확인 / 데이터 출처: `docs/discovery/`, `docs/srs/index.md`, 후속 REQ 문서 + +# 8. REQ로 넘기기 전 확인 체크 +REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다. + +- [x] 범위(In Scope/Out of Scope) 확정 + - 기준: Streamlit UX/E2E 검증이 이번 반복의 주 범위이고, 신규 parser/CI/DB migration은 제외 또는 후속 Discovery로 분리되는지 확인한다. +- [x] 성공 지표의 측정 방식/데이터 출처 확정 + - 기준: fixture DB, 운영 DB 복사본, masked sample 중 어떤 데이터를 사용할지 결정하고 각 성공 기준의 관찰 방법을 확정한다. +- [x] High 영향 리스크 우선순위 및 해소 책임자 지정 + - 기준: 실제 DB mutation 리스크에 대해 backup/fixture 우선 원칙과 검증 책임자를 지정한다. +- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인 + - 기준: OQ 목록에 `Open` 상태 항목이 0개이거나, 잔여 항목 전부 `Deferred` 처리 완료 +- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정 + - 기준: 사용자가 Discovery 내용을 확인하고 REQ draft 진행 가능 여부를 명시한다. + +# 9. 파일 처리 결과 +이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다. + +- 처리 결과: 생성 +- 생성 경로: docs/discovery/dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows.md +- 참조 문서: + - docs/project-structure.md + - docs/runtime-flows.md + - docs/interface-contract.md + - docs/data-model.md + +# 10. 사용자 결정 필요 항목 요약 +사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다. + +## DECIDE +- 결정 완료: 브라우저 검증 수준은 **semi-automated**로 한다. 서비스/DB 회귀 테스트를 기본 증거로 삼고, Streamlit 핵심 흐름은 브라우저 또는 스크린샷 기반 smoke 검증을 병행한다. +- 결정 완료: 검증 데이터 정책은 **synthetic fixture 우선 + 필요 시 운영 DB 복사본 허용**으로 한다. 원본 운영 DB 직접 mutation은 금지한다. + +## CONFIRM +- 확인 완료: 이번 Discovery 범위는 Streamlit UX/E2E 안정화, 분류 규칙, 확인필요 큐, 취소/환급 연결, import/rollback 검증으로 한정한다. 신규 parser/CI/파괴적 DB migration은 제외한다. +- 확인 완료: REQ draft 및 구현 batch 진행 가능. 사용자의 명시 승인이 꼭 필요한 고위험 DB/범위 변경만 사용자에게 재확인한다. 그 외 세부 승인과 구현 진행은 자르카가 대리 승인한다. + +## DATA +- 후속 작업 원칙: 민감정보를 제거한 synthetic fixture를 우선 사용하고, 실제 재현성이 필요한 경우 운영 DB 복사본에서만 검증한다. + +# 11. Discovery Freeze +REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다. + +## 섹션 참조 +- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록) +- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록) +- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준) +- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크) +- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약) + +## Freeze 플래그 +- Handoff Decision: 승인 +- Handoff Rationale: 사용자 승인에 따라 자르카 추천안(semi-automated 검증, synthetic fixture 우선 + 필요 시 운영 DB 복사본, Streamlit UX/E2E 안정화 범위)을 확정했다. 후속 REQ 초안, batch 구현, 검수관 verification gate를 거쳐 confirm-req-implemented 단계까지 진행한다. +- Ready for REQ Drafting: true +- Confirmed By: 진환 고 / 자르카 +- Confirmed At (KST): 2026-06-26 23:27 diff --git a/docs/discovery/index.md b/docs/discovery/index.md index 8dab12d..b7c390e 100644 --- a/docs/discovery/index.md +++ b/docs/discovery/index.md @@ -6,3 +6,4 @@ | ID | 날짜 | 이슈명 | 문서 | 상태 | |---|---|---|---|---| +| dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows | 20260626 | Streamlit 운영 UX/E2E 검증 및 분류·환급 워크플로우 안정화 | [Discovery](./dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows.md) | approved | diff --git a/docs/srs/Interface/req-001_category-rule-ops.md b/docs/srs/Interface/req-001_category-rule-ops.md new file mode 100644 index 0000000..076f5bc --- /dev/null +++ b/docs/srs/Interface/req-001_category-rule-ops.md @@ -0,0 +1,58 @@ +# REQ-001: 분류 규칙 운영 액션의 DB 경로 일관성 및 삭제 노출 + +## 0. 문서 상태 +- ID: REQ-001 +- Status: Implemented +- Type: Interface +- Priority: High +- Owner: 렛저장인 +- Source Discovery: dcy-001_20260626_streamlit-ux-e2e-categorization-refund-workflows +- Created At (KST): 2026-06-26 23:31 +- Last Updated (KST): 2026-06-26 23:41 + +## 1. Intent +Discovery dcy-001의 첫 구현 범위로, Streamlit `분류 규칙` 화면의 가장 작은 운영 lifecycle인 목록/삭제/preview/apply가 선택된 검증 DB에만 작동하도록 안정화한다. 동시에 이미 존재하는 service-layer 규칙 삭제 기능을 MCP tool surface에도 노출해 UI와 MCP contract가 어긋나지 않게 한다. + +## 2. Requirement +- Streamlit `분류 규칙` 페이지에서 기본 규칙 seed/update, 규칙 preview, 규칙 적용 액션은 현재 UI가 사용하는 `db_path`를 명시적으로 전달해야 한다. +- 규칙 삭제는 service layer와 Streamlit UI뿐 아니라 MCP tool surface에서도 사용할 수 있어야 한다. +- 기존 `create_category_rule`, `update_category_rule`, `list_category_rules`, `preview_categorization`, `apply_categorization_rules` 성공 응답 shape는 유지한다. +- 원본 운영 DB 직접 mutation, 파괴적 SQLite migration, 민감 금융 원본/비밀번호 fixture 저장은 금지한다. + +## 3. Acceptance Criteria +- AC-1: `dashboard/app.py`의 분류 규칙 seed/update, preview, apply 버튼이 모두 page-level `db_path`를 전달한다. +- AC-2: `server.py`가 `delete_category_rule(rule_id: str)` MCP tool을 제공하고, service의 기존 삭제 성공 payload(`success`, `deleted_rule_id`)를 유지한다. +- AC-3: fixture DB에서 규칙 생성 후 삭제하면 active/all rule list에서 해당 규칙이 제거되고, 없는 rule 삭제는 `not_found` 실패를 반환한다. +- AC-4: 변경 후 다음 검증이 통과한다: `python -m py_compile jarca_ledger_mcp/*.py dashboard/app.py`, 관련 pytest, 전체 pytest, `hermes mcp test jarca-ledger`. +- AC-5: Streamlit smoke 검증은 synthetic fixture DB 또는 사본 DB를 사용하며, 원본 운영 DB를 직접 변경하지 않았다는 증거를 batch verification에 남긴다. + +## 4. Impacted Area +- `dashboard/app.py`: 분류 규칙 페이지 액션의 DB path 전달. +- `jarca_ledger_mcp/server.py`: delete category rule MCP tool 등록. +- `tests/`: category rule deletion regression. +- `docs/interface-contract.md`: 필요 시 MCP rule CRUD contract 갱신. + +## 5. Compatibility / Safety Notes +- SQLite schema 변경 없음. +- MCP 기존 tool signature 변경 없음. 신규 tool은 additive change다. +- 기존 service success response shape는 변경하지 않는다. +- 검증 fixture에는 synthetic merchant/category만 사용한다. + +## 6. Change Log +- 2026-06-26 23:31 KST: REQ 생성 및 구현 기준 Approved 상태로 등록. +- 2026-06-26 23:41 KST: BAT-001 구현/검증 및 검수관 PASS_WITH_NOTES 근거로 Implemented 전환. + +## 7. Implementation Evidence +- Batch: [BAT-001 Category Rule Ops Safety](../../batches/bat-001_20260626_category-rule-ops/) +- Code evidence: + - `dashboard/app.py`: 분류 규칙 seed/preview/apply 호출에 `db_path=db_path` 전달. + - `jarca_ledger_mcp/server.py`: `delete_category_rule(rule_id)` MCP tool additive 등록. + - `tests/test_category_rule_filters.py`: category rule 삭제 regression 추가. +- Verification evidence: + - `python -m py_compile jarca_ledger_mcp/*.py dashboard/app.py`: pass. + - `pytest tests/test_category_rule_filters.py`: 3 passed. + - `pytest tests/test_cross_account_transfer_matching.py`: 3 passed. + - `pytest`: 17 passed, 4 skipped. + - `hermes mcp test jarca-ledger`: connected, 41 tools discovered including `delete_category_rule`. + - Streamlit substitute smoke: synthetic DB `/tmp/jarca-ledger-bat-001-smoke.sqlite3`, HTTP 200, AppTest exceptions 0, 분류 규칙 page and preview/apply buttons verified; default 운영 DB mtime unchanged. +- QC gate: `jarcaledgerqc chat -q ...` returned `PASS_WITH_NOTES`; blocker 없음. Notes: browser screenshot unavailable due Chromium snap absence; future batches should restore real browser/screenshot evidence. diff --git a/docs/srs/index.md b/docs/srs/index.md index e8fa934..db1ecb7 100644 --- a/docs/srs/index.md +++ b/docs/srs/index.md @@ -33,14 +33,16 @@ ## Next Requirement ID -- Current: `REQ-001` +- Current: `REQ-002` - Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다. ## Requirement Register | ID | Title | Type | Status | Priority | Owner | Link | | --- | --- | --- | --- | --- | --- | --- | +| REQ-001 | 분류 규칙 운영 액션의 DB 경로 일관성 및 삭제 노출 | Interface | Implemented | High | 렛저장인 | [req-001_category-rule-ops.md](Interface/req-001_category-rule-ops.md) | ## Recent Change Log Summary -- 없음 +- 2026-06-26 23:31 KST: dcy-001 후속 첫 구현 범위로 REQ-001을 Approved 상태로 등록. +- 2026-06-26 23:41 KST: BAT-001 검증 및 검수관 PASS_WITH_NOTES 근거로 REQ-001을 Implemented 상태로 전환. diff --git a/jarca_ledger_mcp/server.py b/jarca_ledger_mcp/server.py index ea438c6..a4e4b44 100644 --- a/jarca_ledger_mcp/server.py +++ b/jarca_ledger_mcp/server.py @@ -154,6 +154,11 @@ def update_category_rule(rule_id: str, match_field: str, pattern: str, category: """Update an existing ledger transaction categorization rule.""" return service.update_category_rule(rule_id, match_field, pattern, category, subcategory, transaction_type, match_type, priority, person, is_fixed, confidence, memo, amount_min, amount_max, day_of_month, active) +@mcp.tool() +def delete_category_rule(rule_id: str) -> dict: + """Delete a transaction categorization rule.""" + return service.delete_category_rule(rule_id) + @mcp.tool() def preview_categorization(limit: int = 1000, only_uncategorized: bool = True) -> dict: """Preview category-rule matches before writing categories to transactions.""" diff --git a/tests/test_category_rule_filters.py b/tests/test_category_rule_filters.py index 44cac04..adac549 100644 --- a/tests/test_category_rule_filters.py +++ b/tests/test_category_rule_filters.py @@ -103,3 +103,34 @@ def test_update_category_rule_allows_condition_edits(tmp_path): assert rule["amount_max"] == 150000 assert rule["day_of_month"] == 10 assert rule["active"] is False + + +def test_delete_category_rule_removes_rule_and_reports_missing_ids(tmp_path): + db_path = str(tmp_path / "ledger.sqlite3") + service.health_check(db_path) + created = service.create_category_rule( + match_field="merchant", + match_type="contains", + pattern="삭제대상", + transaction_type="expense", + category="쇼핑", + subcategory="생활용품", + db_path=db_path, + ) + assert created["success"] is True + rule_id = created["rule"]["id"] + + before = service.list_category_rules(active_only=False, db_path=db_path) + assert any(rule["id"] == rule_id for rule in before["rules"]) + + deleted = service.delete_category_rule(rule_id, db_path=db_path) + assert deleted == {"success": True, "deleted_rule_id": rule_id} + + after_all = service.list_category_rules(active_only=False, db_path=db_path) + after_active = service.list_category_rules(active_only=True, db_path=db_path) + assert all(rule["id"] != rule_id for rule in after_all["rules"]) + assert all(rule["id"] != rule_id for rule in after_active["rules"]) + + missing = service.delete_category_rule(rule_id, db_path=db_path) + assert missing["success"] is False + assert missing["error_code"] == "not_found"