Files
jarca-ledger-mcp/docs/requirements/req-003_account-valuation-history.md

44 KiB

schema, id, title, revision, status, depends_on, applies_to, language, issue, implementation_pr
schema id title revision status depends_on applies_to language issue implementation_pr
gitea-agent.requirement/v1 REQ-003 계좌 기준일 평가 이력과 자산 마감 연동 1.2 approved
ledger
dashboard
mcp
ko #18 #19

REQ-003 — 계좌 기준일 평가 이력과 자산 마감 연동

1. 목적

계좌의 기준일 평가 증거를 append-only 이력으로 저장·선택하고, 검증 가능한 자산만 자산/마감 및 월정산의 역사적 순자산에 반영한다.

2. 배경

기존 schema 11→13의 additive 이력을 보존하고 이번 종합 정산 확장은 schema 14를 additive하게 적용한다. 계좌 잔액·import 결과는 실제 증거가 없는 한 unverified 후보이고, 기존 거래·인증·메뉴 계약을 바꾸지 않는다.

3. 범위

포함

  • account_valuations, asset_snapshot_runs, asset_snapshots 확장과 exact/as_of 선택
  • idempotency, append-only correction, 투자 optional metrics, loan 대표 부채
  • read-only preview, explicit approval conditional close, immutable evidence, correction/re-close
  • 계좌·import 후보, reconciliation, MCP·Dashboard·월정산 공통 read model

제외

  • credential/실제 금융 데이터, 자동 추정 거래 생성, fake investment transaction, 거래 원장 재작성
  • 외부 자산 가격 API, 환율 변환, 자동 승인, unverified/future/unknown 값 승인
  • Dashboard 인증·logout·cookie, MCP 인증의 신규 도입, 기존 메뉴 순서 변경

4. 용어와 기준

  • verified leaf: supersedes_id로 더 최신 child가 없는 verification_status=verified 행.
  • exact: 요청 basis_date와 일치하는 verified leaf만 선택한다.
  • as_of: 미래를 보지 않고 요청일 이전 최신 verified leaf를 선택하며 carry-forward와 age를 표시한다.
  • last_balance: opening evidence가 아니라 외부 현재값과 비교하는 external current comparison이다. opening evidence와 balance_after가 모두 없으면 INCOMPLETE_CALCULATION이다.
  • principal 물리 필드: principal은 investment의 reported principal amount 또는 loan의 outstanding principal이며, reported_principal_kind와 reported_principal_label만 source-declared 의미를 보존한다. generic label 필드는 없다.
  • 대표 부채: valuation_kind가 loan/loan_balance이고 representative_liability_definition이 principal_only 또는 principal_interest_fees인 하나의 대표 금액만 합산한다. loan purpose, definition, ambiguity는 report에 보존한다.
  • 정본 구현: jarca_ledger_mcp/application/account_valuation_service.py, assets_service.py, reconciliation_service.py, migrations.py 및 named tests.

5. 요구사항

REQ-003.01 — additive 이력과 legacy 보존

schema 11의 테이블·필드·거래를 삭제하거나 추측 이주하지 않고 schema 13의 account_valuations, asset_snapshot_runs를 추가하며 asset_snapshots에 필요한 필드를 additive하게 확장한다. migration은 idempotent다. Issue #18의 유일한 schema 확장은 additive/idempotent 13→14이며 기존 자산 전용 run의 미저장 종합 총계는 NULL로 유지하고 backfill하지 않는다.

영향 범위

  • 인터페이스: 기존 service/MCP 응답 envelope 유지, 신규 valuation/snapshot fields additive
  • 영속성: SQLite schema 11→12, 기존 rows 보존
  • 후속 사용 주체: service, MCP tool, Dashboard gateway/page, 월정산
  • 마이그레이션: migrate_db()의 CREATE TABLE IF NOT EXISTS/ADD COLUMN
  • 호환성: REQ-001 auth, cookie/logout, REQ-002 메뉴 10개/order, query/shell/browser evidence 유지
  • 배포 전제 조건: SQLite write 권한과 기존 DB 백업

REQ-003.02 — verified leaf 선택과 범위

exact/as_of 선택은 account/currency/valuation_kind scope를 적용하고 future, unverified, rejected, unknown을 authority에서 제외한다. newer unverified candidate는 warning으로 남기며 investment는 total_value, balance, cash_balance 우선, loan은 loan_balance, loan, balance 우선이다.

영향 범위

  • 인터페이스: select_account_valuation(), _account_valuation_items(), report warnings
  • 영속성: 상태·기준일·통화·kind와 candidate evidence 보존
  • 후속 사용 주체: snapshot preview/close, Dashboard 계좌·자산, reconciliation
  • 마이그레이션: 없음
  • 호환성: 기존 last_balance fallback은 basis-date 없는 legacy report에서만 유지
  • 배포 전제 조건: ISO date와 허용 currency/kind

REQ-003.03 — idempotency와 append-only correction

같은 idempotency payload는 기존 row/run을 재사용하고 다른 payload는 idempotency_conflict다. source_ref와 fingerprint가 evidence idempotency를 이룬다. correction은 원본을 갱신하지 않고 단일 child를 append한다. correction parent scope는 same account/date/currency/purpose이고 cyclic parent와 self parent는 거절한다. parent당 one child만 허용하며 correction_reason은 필수다. reason 누락, parent 불일치, cyclic/self parent, 다른 child 및 DB 충돌은 no-write correction_conflict다. source_ref는 account-scoped evidence identity이며 explicit key가 있어도 evidence identity에 적용한다. key/source mismatch는 conflict다.

영향 범위

  • 인터페이스: register_account_valuation(), correct_account_valuation(), close_asset_snapshot()
  • 영속성: unique idempotency 및 single-correction indexes
  • 후속 사용 주체: retry caller와 history/report
  • 마이그레이션: unique indexes additive
  • 호환성: 성공/실패 envelope와 기존 error_code 유지
  • 배포 전제 조건: transaction-scoped SQLite write

REQ-003.04 — 금액·옵션·대출 의미

amount와 principal/interest/fee/cost_basis/net_contributed/profit은 exact integer이고 NULL은 미제공, 0은 명시적 영(0)이다. 투자 optional metrics는 저장하되 total valuation을 다시 합산하지 않는다. loan은 대표 정의를 필수로 하고 principal/interest/fee의 unknown을 NULL로 보존하며 대표 금액만 총부채에 합산한다.

영향 범위

  • 인터페이스: valuation input/output fields, validation errors
  • 영속성: INTEGER/REAL return_rate와 nullable optional columns
  • 후속 사용 주체: net-worth formula와 UI labels
  • 마이그레이션: 없음
  • 호환성: 기존 integer ledger amounts와 double-sum 금지
  • 배포 전제 조건: caller가 currency/definition을 명시

REQ-003.05 — current와 historical authority

current latest display와 historical authority를 분리한다. last_balance는 opening evidence가 아니라 external current comparison이며, balance_after/opening evidence가 모두 없으면 INCOMPLETE_CALCULATION이다. 늦게 도착한 historical row는 current를 과거로 되돌리지 않으며 cannot roll back newer current cache. account/import writes는 real dated evidence가 있을 때만 dated unverified candidate를 만든다. 대출 cache write는 대표 부채 정의가 없으면 이력 후보를 추가하지 않는다. 이때 current cache와 import 처리는 유지하고 정의를 추정하지 않는다. 기존 valuation이 있는 계좌를 loan으로 변환할 때는 모든 valuation에 유효한 대표 부채 정의가 있어야 하며, 위반 시 계좌와 이력을 변경하지 않고 validation_error를 반환한다. valuation이 없는 변환은 허용한다.

영향 범위

  • 인터페이스: account update/import candidate, list/select/report
  • 영속성: basis_date/recorded_at 분리
  • 후속 사용 주체: 계좌 화면, reconciliation, monthly settlement
  • 마이그레이션: 없음
  • 호환성: account name-only update는 candidate를 만들지 않음
  • 배포 전제 조건: source date와 source_ref/hash

REQ-003.06 — preview·close·approval

preview는 DB를 쓰지 않고 complete_exact, incomplete_draft, conditional 상태와 warnings/exclusions를 반환한다. close response는 run, approval, correction, warnings, exclusions를 포함한다. close는 need-based explicit approved_by, approval_reason, approval_reference와 flags를 요구하며 immutable run evidence를 기록한다. explicit dated manual asset은 검증된 manual evidence로 취급할 수 있다. unverified/future/unknown/ambiguous, including future unknown, 은 승인 없이 close할 수 없다.

영향 범위

  • 인터페이스: preview_asset_snapshot_close(), close_asset_snapshot() 및 error codes
  • 영속성: run applied/excluded/carried evidence와 approval metadata
  • 후속 사용 주체: Dashboard 자산/마감, MCP operator
  • 마이그레이션: asset_snapshot_runs 신규
  • 호환성: preview read-only, 기존 report response fields 유지
  • 배포 전제 조건: operator approval identity와 reference

REQ-003.07 — correction/re-close와 formula

같은 basis date close는 correction_reason을 요구하고 새 run/rows를 append한다. 최신 run만 historical report authority이며 순자산은 asset_buckets - loan, missing valuation은 transaction inference 없이 제외한다.

영향 범위

  • 인터페이스: close/report/timeseries
  • 영속성: correction_of_run_id, is_correction, closed_at
  • 후속 사용 주체: timeseries와 월정산
  • 마이그레이션: asset snapshot columns/indexes
  • 호환성: legacy asset_snapshots rows are readable
  • 배포 전제 조건: close transaction atomicity

REQ-003.08 — reconciliation과 monthly propagation

investment가 verified valuation으로 관리되면 reconciliation status는 VALUATION_MANAGED이고 fake transaction을 만들지 않는다. report는 unavailable investment를 명시한다. investment unavailable과 reconciliation completeness를 report에 명시한다. regular ledger calculation은 calculation_basis/calculation_basis_at provenance를 유지한다. monthly settlement는 read-only로 동일 snapshot/report를 전파하며 no fake writes를 보장한다. live/current, closed, timeseries, monthly 각각은 actual valuation_date, age, carry, verification, run, approval, correction, warnings, exclusions를 보존한다. monthly_source_snapshot_id는 snapshot_run_id의 read-model alias이며 persistence field가 아니다.

영향 범위

  • 인터페이스: get_balance_reconciliation_report(), get_monthly_settlement_summary()
  • 영속성: reconciliation session/transaction은 valuation 때문에 자동 변경하지 않음
  • 후속 사용 주체: 월정산·overview·reconciliation UI
  • 마이그레이션: 없음
  • 호환성: existing reconciliation statuses and reports additive
  • 배포 전제 조건: service read model available

canonical D04-D06 contract: snapshot close aliases valuation_date, age_days, verification_status, evidence_kind, and monthly_source_snapshot_id at the top level while preserving the same meanings in nested provenance. Replay requires request-fingerprint equality and reconstructs from persisted evidence, never replay-only reused fields. Recursive close_metadata is allowlisted/redacted. Preview fingerprint input/output and authority drift rejection are mandatory. Dashboard missing DB is stable unavailable with no-create; initialized reads are no-write. Reconciliation requires opening evidence and coverage; incomplete state is INCOMPLETE_CALCULATION/reconciliation unavailable, with no inferred balance or fake transaction.

REQ-003.09 — shared application과 보안 경계

MCP와 Dashboard는 shared service/gateway를 사용한다. 기존 success/error envelope를 유지한다. Dashboard는 auth-before-DB이며 MCP auth를 claim하지 않는다. render는 read-only이고 implicit write를 일으키지 않는다.

영향 범위

  • 인터페이스: MCP @mcp.tool, Streamlit gateways/pages
  • 영속성: render/preview에는 write 없음; explicit form/close만 write
  • 후속 사용 주체: browser/operator/automation
  • 마이그레이션: 없음
  • 호환성: REQ-001 gate, cleanup/logout, MCP transport/auth scope 유지
  • 배포 전제 조건: Dashboard auth config, MCP runtime config

REQ-003.10 — privacy, compatibility, excluded scope

문서·tests·fixtures는 synthetic data만 사용하고 actual credential/account/financial data를 저장하지 않는다. Issue #12 밖의 OAuth, external prices, FX, automatic approval, transaction inference, redesign는 구현하지 않는다.

영향 범위

  • 인터페이스: redacted evidence와 generic errors
  • 영속성: synthetic source_note/memo only
  • 후속 사용 주체: tests/operator
  • 마이그레이션: 없음
  • 호환성: 기존 auth/menu/query/shell/browser evidence semantic inventory 0 missing
  • 배포 전제 조건: secret redaction과 fixture DB

REQ-003.11 — 월정산 초안·승인·원자적 종합 마감 (Issue #15, #18)

월말 기준일의 durable draft는 자료가 없어도 생성되며 생성 자체로 run/rows를 만들지 않는다. 월별 활성 초안은 일반·정정 공통으로 하나다. 대상 구성은 명시적 refresh만 반영하고 검토는 frozen evidence의 version/fingerprint에 묶인다. Finalize는 drift를 거절하고 BEGIN IMMEDIATE에서 run, rows, completion/link를 함께 기록하며 동일 요청은 persisted response로 replay한다.

영향 범위

  • 인터페이스: shared settlement draft service, MCP tools, authenticated Dashboard gateway; monthly summary read-only
  • 영속성: schema 12→13 additive settlement_drafts, settlement_draft_items; legacy 11→12 보존
  • 후속 사용 주체: Dashboard operator, MCP caller, close reports/timeseries/monthly read model
  • 마이그레이션: additive/idempotent; historical drafts backfill 없음
  • 호환성: existing valuation/auth/menu/query/shell/MCP contracts 유지
  • 배포 전제 조건: explicit writer init, SQLite transaction, synthetic tests

6. 제약과 불변조건

  • Automatic exact/as_of selection and legacy direct close exclude future, unverified, rejected, and unknown valuations from authority. The sole future-valuation exception is an explicitly selected, verified manual_substitute in a settlement draft, with reason, review, approval, and original valuation/basis dates preserved; a future valuation is never chosen automatically. Unverified, rejected, or unknown amounts are never authority on any path.
  • 동일 idempotency payload는 같은 ID를 반환하고 다른 payload는 conflict다.
  • correction과 re-close는 append-only이고 parent당 child/run은 하나다.
  • NULL과 0을 구별하고 모든 금액은 integer precision이다.
  • loan representative만 liability sum에 들어가고 investment metrics는 double sum하지 않는다.
  • preview와 render는 DB write가 없고 close는 하나의 transaction 경계다.
  • current display와 historical basis authority는 분리된다.
  • Dashboard auth gate는 DB보다 먼저이며 MCP 인증은 이 요구사항이 주장하지 않는다.
  • 실제 credential·token·cookie·account data는 문서·로그·fixture에 없다.

7. 인수 기준

REQ-003.11-AC01 — 빈 월 초안 영속 생성

판정 기준

자료가 없어도 월말 기준일의 초안이 저장되고 생성만으로 snapshot run/rows가 생기지 않는다.

검증

tests/test_monthly_settlement_drafts.py::test_empty_draft_persists_without_closing_and_has_consistent_public_shape

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상 empty draft; 경계 no targets; 실패 invalid month

상태 불변조건

Create는 close side effect가 없다.

REQ-003.11-AC02 — 월말 기준일 및 create replay

판정 기준

월말만 허용하고 같은 key/payload는 idempotent, 다른 payload는 충돌한다.

검증

tests/test_monthly_settlement_drafts.py::test_basis_must_be_calendar_month_end_and_create_key_is_idempotent

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal month-end; boundary leap month; failure non-month-end

상태 불변조건

거절된 create는 초안을 추가하지 않는다.

판정 기준

Normal/correction 초안을 합쳐 월별 active는 하나이며 취소 후 월 재개가 가능하다.

검증

tests/test_monthly_settlement_drafts.py::test_one_active_draft_across_normal_and_correction_and_cancellation_reopens_month

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal competing create; boundary correction lineage; failure second active

상태 불변조건

Correction lineage는 명시적이고 유일하다.

REQ-003.11-AC04 — 입력·버전·행위자 검증

판정 기준

잘못된 값과 stale version을 거절하고 include/exclude 사유 및 actor를 기록한다.

검증

tests/test_monthly_settlement_drafts.py::test_input_validation_versions_actors_and_include_exclude_reason

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal valid mutation; boundary version; failure invalid type

상태 불변조건

거절된 mutation은 상태와 version을 변경하지 않는다.

REQ-003.11-AC05 — 명시적 target refresh와 read-only review

판정 기준

대상 membership은 refresh 전 고정되고 review는 읽기 전용이다.

검증

tests/test_monthly_settlement_drafts.py::test_targets_are_frozen_until_explicit_refresh_and_review_is_read_only

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal refresh; boundary changed sources; failure implicit refresh

상태 불변조건

Review는 DB 상태를 쓰지 않는다.

REQ-003.11-AC06 — exact, carry, manual substitute 날짜 구분

판정 기준

선택 모드는 valuation date와 basis date 차이를 보존하며 future substitute는 명시적이다.

검증

tests/test_monthly_settlement_drafts.py::test_explicit_future_substitute_and_late_recorded_exact_are_distinguished

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal exact; boundary future substitute; failure implicit future choice

상태 불변조건

date_difference_days = valuation_date - basis_date를 보존한다.

REQ-003.11-AC07 — unverified 후보는 authority 아님

판정 기준

Unverified 후보 금액은 표시 가능하나 authority amount는 NULL이며 verified child는 재선택을 요구한다.

검증

tests/test_monthly_settlement_drafts.py::test_unverified_candidate_has_no_authority_and_verified_child_requires_reselection

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal verified selection; boundary candidate; failure candidate authority

상태 불변조건

NULL은 0이나 verified 금액으로 대체되지 않는다.

REQ-003.11-AC08 — 수동 evidence drift와 future 차단

판정 기준

수동 source 변경은 명시적 refresh가 필요하고 future evidence가 자동 선택되지 않는다.

검증

tests/test_monthly_settlement_drafts.py::test_manual_sources_drift_requires_explicit_refresh_and_future_not_auto_selected

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal unchanged source; boundary drift; failure auto-select future

상태 불변조건

Frozen source hash는 refresh 전까지 불변이다.

REQ-003.11-AC09 — 승인 fingerprint 및 drift gate

판정 기준

Mutation/source drift는 approval을 무효화하고 finalize를 막는다.

검증

tests/test_monthly_settlement_drafts.py::test_approval_fingerprint_mutation_and_external_drift_block_finalize

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal unchanged approval; boundary drift; failure stale finalize

상태 불변조건

Finalize는 승인된 version/fingerprint만 받는다.

REQ-003.11-AC10 — 원자적 finalize 및 persisted replay

판정 기준

Finalize는 immutable provenance를 저장하고 동일 요청에 persisted response를 반환한다.

검증

tests/test_monthly_settlement_drafts.py::test_finalize_persists_immutable_provenance_replays_exact_response_and_conflicts_key

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal finalize; boundary replay; failure conflicting key

상태 불변조건

Replay는 저장된 결과를 재구성하며 중복 run을 만들지 않는다.

REQ-003.11-AC11 — DB 실패 rollback

판정 기준

SQLite write 실패 시 run, rows, completed 상태가 모두 rollback된다.

검증

tests/test_monthly_settlement_drafts.py::test_sqlite_trigger_failure_rolls_back_run_rows_and_completion

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal successful commit; boundary trigger failure; failure mid-transaction

상태 불변조건

부분 마감 결과는 남지 않는다.

REQ-003.11-AC12 — additive schema 14 migration

판정 기준

Migration은 legacy rows를 보존하고 반복 실행에 안전하다.

검증

tests/test_monthly_settlement_drafts.py::test_schema13_migration_is_additive_and_idempotent

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal new DB; boundary schema 12; failure repeated migration

상태 불변조건

Schema 11→12 의미와 데이터는 보존된다.

REQ-003.11-AC13 — 동시 create와 stale finalize

판정 기준

동시 create는 active uniqueness를 지키고 stale version finalize는 거절한다.

검증

tests/test_monthly_settlement_drafts.py::test_concurrent_create_and_stale_version_finalization_are_safe

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal sequential mutation; boundary concurrent create; failure stale finalize

상태 불변조건

한 달에 active 초안은 최대 하나다.

REQ-003.11-AC14 — draft adapter public contract

판정 기준

Gateway/MCP는 실제 service signature와 결과를 그대로 노출한다.

검증

tests/test_settlement_draft_adapters.py::test_gateway_exports_exact_contract_signature_and_delegates

검증 분류

  • 수준: integration
  • 백엔드: application
  • 케이스: normal delegation; boundary signature; failure service error

상태 불변조건

Adapter는 authority 판단이나 응답 변형을 하지 않는다.

REQ-003.11-AC15 — Dashboard auth 및 render 경계

판정 기준

인증은 DB 접근 전 수행되고 render는 lifecycle mutation을 하지 않는다.

검증

tests/test_settlement_draft_adapters.py::test_dashboard_authentication_entrypoint_precedes_database_resolution

검증 분류

  • 수준: integration
  • 백엔드: dashboard
  • 케이스: normal authenticated read; boundary unauthenticated; failure DB access before auth

상태 불변조건

인증 없는 요청은 DB에 접근하지 않는다.

REQ-003.11-AC16 — missing DB read no-create

판정 기준

Read-only gateway missing database를 생성하거나 migrate하지 않는다.

검증

tests/test_settlement_draft_adapters.py::test_read_only_gateway_calls_do_not_create_missing_database

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: normal initialized read; boundary missing DB; failure implicit create

상태 불변조건

Read path는 no mkdir, no migrate, no commit이다.

REQ-003.11-AC17 — legacy contract regression

판정 기준

Valuation correction, monthly summary read-only, menu/auth compatibility regressions remain green.

검증

tests/test_settlement_draft_adapters.py::test_service_reexports_every_lifecycle_operation

검증 분류

  • 수준: integration
  • 백엔드: application
  • 케이스: normal legacy call; boundary old schema; failure incompatible export

상태 불변조건

New draft operations are additive and legacy entrypoints remain available.

REQ-003.01-AC01 — migration and preservation

Cases (정상/경계/실패): 정상 schema 14 init twice; 경계 schema 11/13 legacy DB; 실패 malformed legacy DB.

State Invariants: legacy tables/rows remain; migration repeats without duplicate columns.

판정 기준: schema_version=14, 기존 및 종합 정산 tables/columns/indexes가 존재하고 old rows와 구버전 미보존 NULL을 유지한다.

검증: tests/test_account_valuations.py::test_account_valuation_schema_is_v12_and_idempotent, tests/test_issue_12_regressions.py::test_schema12_migration_preserves_legacy_rows_indexes_and_is_idempotent. 검증 분류: level=integration; backend=sqlite

REQ-003.02-AC01 — selection policy

Cases (정상/경계/실패): exact verified; as_of carry-forward; future/unverified/ambiguous excluded/warned.

State Invariants: no future/unverified row is selected.

판정 기준: exact returns same-date verified leaf; as_of returns latest prior verified and age/warning.

검증: tests/test_account_valuations.py::test_select_exact_and_as_of_excludes_unverified_and_reports_carry_forward, source _account_valuation_items(). 검증 분류: level=unit; backend=application

REQ-003.03-AC01 — idempotent correction

Cases (정상/경계/실패): same payload reuse; different payload conflict; second correction fails.

State Invariants: one row/child per operation and original immutable.

판정 기준: exact error codes and count 2 after one correction.

검증: tests/test_account_valuations.py::test_register_same_idempotency_payload_reuses_row_and_changed_payload_conflicts, tests/test_account_valuations.py::test_correction_is_append_only_reasoned_and_single_child, tests/test_issue_12_regressions.py::test_empty_conditional_close_is_closed_and_second_close_requires_correction. 검증 분류: level=integration; backend=sqlite

REQ-003.04-AC01 — precision and representative loan

Cases (정상/경계/실패): integer zero; NULL optional metrics; loan missing definition rejected.

State Invariants: no float amount and no double sum.

판정 기준: optional fields round-trip exactly and loan definition validation is enforced.

검증: test_correction_preserves_optional_fields_unless_overridden, _validate_common() and schema static checks. 검증 분류: level=static; backend=sqlite

REQ-003.05-AC01 — authority separation

Cases (정상/경계/실패): dated candidate; late historical candidate; name-only update.

State Invariants: current/latest display cannot roll backward and candidate is unverified.

판정 기준: dated account write creates only unverified candidate; name-only does not.

검증: test_account_balance_write_records_unverified_candidate_only_with_real_basis_date, test_account_name_only_update_does_not_create_candidate. 검증 분류: level=unit; backend=application

REQ-003.06-AC01 — preview and approval

Cases (정상/경계/실패): complete exact close; incomplete draft; missing approval rejected.

State Invariants: preview row count unchanged; no unsafe evidence approved.

판정 기준: preview is read-only and close returns conditional_approval_required without metadata.

검증: test_manual_asset_update_deactivate_preview_and_close_correction_policy, preview_asset_snapshot_close(). 검증 분류: level=integration; backend=application

REQ-003.07-AC01 — immutable close correction

Cases (정상/경계/실패): first close; same-date correction; second unreasoned close.

State Invariants: old run/rows remain and latest run alone reports.

판정 기준: correction_of_run_id, count, and net worth match.

검증: test_manual_asset_update_deactivate_preview_and_close_correction_policy, assets_service.py close path. 검증 분류: level=integration; backend=sqlite

REQ-003.08-AC01 — reconciliation/monthly

Cases (정상/경계/실패): regular ledger calculation; verified investment; missing baseline.

State Invariants: no fake transaction; provenance fields present; monthly read is non-mutating.

판정 기준: status VALUATION_MANAGED for verified investment and monthly summary reads report.

검증: tests/test_service.py::test_statement_status_account_update_and_balance_reconciliation, tests/test_issue_12_regressions.py::test_reconciliation_uses_corrected_investment_leaf_without_transaction_count_change, tests/test_service.py::test_monthly_settlement_summary_read_model_foundation. 검증 분류: level=unit; backend=application

REQ-003.09-AC01 — shared adapters and auth

Cases (정상/경계/실패): MCP stdio/http config; authenticated Dashboard render; unauthenticated gate.

State Invariants: auth precedes DB; render does not write; response envelope has success.

판정 기준: actual tool/gateway names resolve and existing auth/menu tests pass.

검증: tests/test_mcp_http_runtime.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_menu_transition.py, server.py and gateways. 검증 분류: level=integration; backend=dashboard

REQ-003.10-AC01 — privacy and compatibility

Cases (정상/경계/실패): synthetic fixture; redaction search; actual secret/data detection.

State Invariants: no credential/token/cookie/real financial data in docs/tests/evidence.

판정 기준: semantic inventory missing=0 and ambiguous=0; placeholder=0.

검증: tests/test_requirement_docs.py, auth/menu evidence tests, repository redaction/static search. 검증 분류: level=static; backend=dashboard

REQ-003.11-AC18 — 종합 저장 구조와 여섯 총계

판정 기준

같은 run의 헤더에 여섯 총계·월·통화·정책·버전·근거가 저장되고 양 상세는 같은 ID에 결속한다. 현금흐름 상세의 signed INTEGER, positive count, FK RESTRICT, unique5, NOT NULL 빈 분류를 검증한다.

검증

tests/test_comprehensive_settlement.py::test_closed_header_persists_six_totals_policy_and_versions; tests/test_settlement_cashflow_schema.py::test_cashflow_detail_constraints_preserve_blank_categories (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

REQ-003.11-AC19 — 환급·저축·이동·대출상환 정책

판정 기준

환급월의 원래 지출 분류에 음수 지출을 반영한다. 명시적 category/subcategory 저축 목록만 expense에 포함하고 debt_payment는 expense로 매핑한다. 일반 이체·초기잔액 조정·명시적 상품권 충전 제외를 우선하고 investment 전체를 포함하지 않는다.

검증

tests/test_settlement_cashflow_policy.py::test_explicit_savings_refund_and_movement_precedence; tests/test_settlement_cashflow_policy.py::test_refund_reduces_debt_payment_and_selected_savings; tests/test_settlement_cashflow_policy.py::test_month_boundaries_refund_month_negative_total_and_fx_blocker (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

REQ-003.11-AC20 — 미결정 분류 승인 차단

판정 기준

대응 분류 없는 환급과 미결정 unknown은 종합 승인·마감을 차단한다. 자산의 세 조건부 승인 플래그는 이 차단을 우회하지 못한다. 재분류 또는 명시적 포함/제외 판단 뒤 재검토하며 확정 제외는 차단하지 않는다.

검증

tests/test_settlement_cashflow_policy.py::test_unknown_classification_blocks_and_explicit_exclusion_unblocks; tests/test_settlement_cashflow_policy.py::test_unknown_cashflow_source_is_not_recorded_as_excluded (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

REQ-003.11-AC21 — 당시 계산 근거와 부채 부호

판정 기준

거래 원본 ID/날짜/type/금액/통화/분류와 적용 판단·금액, 선택 자산 평가 ID/원본 날짜/출처/적용 방식/포함·제외를 고정한다. 부채 총계는 기존 abs(sum(raw loans))이며 상세 applied_amount 기여도 합이 이에 일치하고 원본을 덮어쓰지 않는다.

검증

tests/test_comprehensive_settlement.py::test_asset_detail_preserves_raw_evidence_and_signed_liability_contributions; tests/test_comprehensive_settlement.py::test_source_snapshot_preserves_reproducible_values_without_raw_secrets (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

REQ-003.11-AC22 — 승인 지문의 모든 변경 입력

판정 기준

거래 추가·삭제·금액/분류 변경, 정책·집계 버전 변경, 선택 평가자료·포함/제외/미확정 변경을 지문에 결속한다. stale 승인으로는 마감하지 않고 정책 편집은 version 증가와 승인 무효화를 수행한다.

검증

tests/test_comprehensive_settlement.py::test_source_policy_and_category_drift_invalidate_approval; tests/test_settlement_cashflow_policy.py::test_policy_edit_persists_versions_and_invalidates_approval (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

REQ-003.11-AC23 — 원자적 저장과 실제 저장 일치

판정 기준

income/expense 상세 합, net_cashflow 차, 반영 자산/부채 상세 합, net_worth 차의 여섯 식과 승인 결과를 실제 저장된 헤더·상세·근거와 대조한다. 불일치·중간 실패는 run/양 상세/완료 상태/응답 전체를 롤백한다.

검증

tests/test_comprehensive_settlement.py::test_mid_close_failure_rolls_back_all_settlement_state; tests/test_comprehensive_settlement.py::test_close_rejects_stored_detail_sum_tampering (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

REQ-003.11-AC24 — 멱등 재시도와 동시 마감

판정 기준

동일 승인 요청·멱등 키는 하나의 run과 동일 저장 응답을 반환한다. 변경된 요청은 충돌하며 동시 요청에서도 중복 run이나 부분 완료가 없다.

검증

tests/test_comprehensive_settlement.py::test_concurrent_close_retries_return_one_saved_result; tests/test_comprehensive_settlement.py::test_replay_and_correction_keep_each_run_immutable (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

REQ-003.11-AC25 — 불변 이력과 정정 분리

판정 기준

완료 이후 원장 변경으로 기존 헤더·양 상세·근거·저장 응답을 바꾸지 않는다. 최신 parent·단일 child 계약에 따라 정정 run을 append하며 최초와 정정을 exact run으로 각각 조회한다.

검증

tests/test_comprehensive_settlement.py::test_completed_get_and_saved_snapshot_ignore_later_live_ledger_changes; tests/test_comprehensive_settlement.py::test_replay_and_correction_keep_each_run_immutable (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

REQ-003.11-AC26 — legacy NULL과 기존 초안 재승인

판정 기준

schema14 additive/idempotent 이관은 legacy 미보존 값을 NULL로 두며 현재 원장 소급 backfill이나 fake0을 하지 않는다. 기존 초안과 이력을 유지하지만 자산-only 과거 승인으로 종합 마감할 수 없고 새 검토/승인이 필요하다.

검증

tests/test_settlement_cashflow_schema.py::test_legacy_migration_is_idempotent_and_keeps_unstored_totals_null; tests/test_settlement_cashflow_schema.py::test_cashflow_detail_constraints_preserve_blank_categories; tests/test_comprehensive_settlement.py::test_legacy_asset_approval_requires_comprehensive_review_before_close; tests/test_comprehensive_settlement.py::test_legacy_reader_accepts_original_header_without_migration (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

REQ-003.11-AC27 — 공개 계약·출처 구분·읽기 보안

판정 기준

shared service→MCP/gateway→인증된 UI에서 여섯 총계·분류/경고와 저장 run/버전을 표시한다. 기존 자산-only 직접 API와 live 보고는 그대로이며 저장 조회는 readonly/no-create다. ROOT review/finalize, active get.review, completed get.final_response를 유지하고 인증정보/원본 민감 payload를 공개하지 않는다.

검증

tests/test_comprehensive_settlement_adapters.py::test_shared_adapters_expose_policy_and_saved_snapshot_contracts; tests/test_comprehensive_settlement.py::test_stored_reader_allowlists_source_root_and_nested_decision; tests/test_comprehensive_settlement.py::test_stored_reader_keeps_required_transaction_date; tests/test_comprehensive_settlement.py::test_saved_reader_rejects_unsupported_comprehensive_versions_without_writes; tests/test_comprehensive_settlement.py::test_saved_reader_does_not_mislabel_partial_version_metadata_as_legacy; tests/test_comprehensive_settlement_adapters.py::test_real_comprehensive_stdio_policy_close_and_saved_snapshot; tests/test_comprehensive_settlement_adapters.py::test_authenticated_dashboard_renders_saved_comprehensive_snapshot (실제 실행 기록과 최종 head에 결속하며 문서 기재 자체는 PASS가 아니다.)

검증 분류

  • 수준: integration
  • 백엔드: sqlite
  • 케이스: 정상·경계·실패 synthetic 자료

상태 불변조건

기존 REQ-003 권위·승인·호환성 의미를 유지하며 미검증 또는 실패 상태는 완료로 판정하지 않는다.

8. 오류 및 충돌 계약

공통 envelope는 성공 시 success: true, 실패 시 success: false, error_code, error를 유지한다. 주요 codes는 validation_error, not_found, idempotency_conflict, correction_conflict, ambiguous_valuation, conditional_approval_required, db_error다. SQLite unique/transaction conflict는 추가 write 없이 실패한다.

9. 데이터 및 인터페이스 계약

서비스는 jarca_ledger_mcp.service re-export를 통해 호출되고 MCP는 jarca_ledger_mcp.adapters.mcp.server tools를 노출한다. Dashboard는 adapters/streamlit/accounts_gateway.py, assets_gateway.py와 dashboard/page_views/accounts.py, assets.py를 사용한다. 실제 fields/signatures는 docs/interface-contract.md에 고정한다.

10. 하위 호환성

REQ-001의 auth gate·cookie/cleanup/logout, REQ-002의 메뉴 10개와 order(월정산, 요약, 자산/마감, 거래내역, 확인필요 큐, 미분류, 분류 규칙, 카테고리, 계좌, Import), query/shell/background와 browser evidence 계약을 삭제·축약하지 않고 additive하게 유지한다. 기존 MCP stdio/streamable-http transport와 success/error envelope를 유지한다.

11. 추적성

  • 근거 이슈: #12, #15, #18
  • 구현 PR: #13
  • Issue #15 구현 PR: #16
  • 관련 설계 문서: docs/data-model.md, docs/interface-contract.md, docs/project-structure.md, docs/runtime-flow.md
  • 관련 요구사항: REQ-001, REQ-002

Issue #18 종합 정산 확장 (revision 1.2; 구현 PR #19)

기존 요구사항·AC 식별자 및 공통 섹션은 유지하고 REQ-003.11만 확장한다. 월 수입·지출은 활성 거래의 [월초, 다음 월초) 범위, 자산·부채는 기존 월말 기준이다. 초안 정책은 draft.cashflow_policy에 저장하며 savings는 사용자가 명시한 category/subcategory pair만 인정한다. 생활비·선택 savings·debt_payment은 expense; 일반 transfer와 voucher top-up 제외가 우선하고 investment 전체 포함은 금지한다. Refund는 환급월 원래 지출 분류에 signed negative expense로 기록하며 과거 run을 수정하지 않는다. Unresolved unknown은 승인 blocker이며 asset conditional flag로 우회할 수 없고, 확정 제외는 blocker가 아니다. KRW only, no FX.

Run header는 settlement month/currency, 여섯 총계, policy JSON, aggregation/schema version, 당시 source JSON을 보존한다. settlement_cashflow_snapshots는 id, settlement_run_id, cashflow_kind, category, subcategory, amount, transaction_count, currency 8개 필드, signed INTEGER amount, positive count, FK RESTRICT, 5-field unique(run, cashflow_kind, category, subcategory, currency), run index를 갖는다. category/subcategory는 NOT NULL이며 빈 문자열은 그대로 구별한다. 기존 자산 상세와 authority/verified·exact/carry/substitute·missing/exclusion 근거를 유지한다.

동일 run의 income/expense grouped 합이 각 총계와 일치하고 net_cashflow=income_total-expense_total; 자산·부채는 포함 상세 합이며 net_worth=total_assets-total_liabilities다. Review fingerprint는 거래 당시 값·분류·정책/버전·grouped 결과/여섯 총계·선택 평가자료·포함/제외/미결정 상태를 포함하고 drift 시 finalize를 거부한다. Header, 두 상세 영역, evidence 및 draft completion/final response는 한 transaction이며 오류 시 모두 rollback. Equal idempotent request replays persisted response; changed request conflicts. Correction validates latest single parent and appends run/details; no historical overwrite. Legacy totals stay NULL, incomplete draft needs fresh review/approval.

Public service additions are set_settlement_cashflow_policy(draft_id,expected_version,updated_by,cashflow_policy,db_path=None) and get_monthly_settlement_snapshot(month='',run_id='',db_path=None); MCP omits db_path. Review/finalize fields are at response ROOT; active get nests review; completed get exposes ROOT final_response. Group key is cashflow_kind. Stored run snapshot differs from live report; existing asset-only API and direct-close meaning remain unchanged. Reads are readonly/no-create; Dashboard authenticates before DB. Synthetic tests cover all Issue inclusion cases; no product test result is asserted here.

  • DB lifecycle: writer/bootstrap은 명시적 init_db() 호출로만 생성·마이그레이션하고, read_db()는 SQLite mode=ro로 연다. missing DB는 db_not_initialized이며 read path는 no mkdir, no migrate, no commit이다. first-use write만 init_db를 수행하고 auth-before-DB 순서를 보존한다.