feat: stabilize category rule operations

approve dcy-001 and add REQ/BAT evidence

pass selected DB path through Streamlit rule actions

expose delete_category_rule MCP tool

add category rule deletion regression
This commit is contained in:
Jarca
2026-06-27 00:02:06 +09:00
parent 97115f6acf
commit 0f881b245f
12 changed files with 459 additions and 5 deletions
+3 -3
View File
@@ -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"]
@@ -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.
@@ -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) 증거를 사용했다.
@@ -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를 수행한다.
@@ -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.
+1
View File
@@ -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/) |
@@ -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
+1
View File
@@ -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 |
@@ -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.
+4 -2
View File
@@ -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 상태로 전환.
+5
View File
@@ -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."""
+31
View File
@@ -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"