fix: preserve migrated legacy snapshot closes

This commit is contained in:
2026-10-08 03:30:59 +00:00
parent 432e6f851a
commit 4b8b74ba2a
34 changed files with 178 additions and 2128 deletions
@@ -1,486 +0,0 @@
{
"baseline_revision": "5813dd484196239ab1fac4ba066c5b4922fb33e6",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"items": [
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.01",
"semantic_key": "additive_schema_migration_and_legacy_preservation",
"meaning": "Schema 11 tables, fields, rows, and transactions remain readable; schema 12 adds valuation and snapshot structures and migration is idempotent.",
"reviewed_ids": [
"REQ-003.01",
"REQ-003.01-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"N01"
],
"planned_status": "strengthen_verification_only; semantics_unchanged",
"authorization": "Preserve additive migration, old-row retention, and repeatability; rework may make evidence stricter but may not delete, infer-migrate, or rename legacy data.",
"evidence_paths": [
"jarca_ledger_mcp/infrastructure/sqlite/migrations.py",
"docs/data-model.md",
"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"
],
"verification": "Reviewed head contains schema-12 documentation and named tests; REQ-001/REQ-002 requirement files are unchanged from baseline."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.02",
"semantic_key": "verified_leaf_exact_as_of_selection",
"meaning": "Selection is scoped by account, currency, and valuation kind; exact selects same-date verified leaf, as_of selects latest prior verified leaf, and future/unverified/rejected/unknown evidence is excluded or warned.",
"reviewed_ids": [
"REQ-003.02",
"REQ-003.02-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"C05"
],
"planned_status": "strengthen_authority_and_warning_contract; semantics_unchanged",
"authorization": "Only verified leaves remain authoritative; rework may expose more explicit warnings, exclusions, age, and scope but cannot broaden authority.",
"evidence_paths": [
"jarca_ledger_mcp/application/account_valuation_service.py",
"jarca_ledger_mcp/application/assets_service.py",
"docs/interface-contract.md",
"tests/test_account_valuations.py::test_select_exact_and_as_of_excludes_unverified_and_reports_carry_forward",
"tests/test_issue_12_regressions.py::test_preview_warns_about_unverified_candidate_when_verified_value_is_selected"
],
"verification": "Selection terminology, scope, and exclusion rules are documented and backed by focused tests."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.03",
"semantic_key": "idempotency_and_append_only_correction",
"meaning": "Equal payloads reuse existing rows or runs; changed payloads conflict; corrections append one reasoned child without mutating the parent; invalid parent scope, cycles, self-parent, duplicate child, and source/key conflicts are no-write failures.",
"reviewed_ids": [
"REQ-003.03",
"REQ-003.03-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"C06",
"D03"
],
"planned_status": "strengthen_conflict_and_replay_evidence; semantics_unchanged",
"authorization": "Preserve immutable history, one-child cardinality, request-fingerprint equality, and no-write conflict behavior.",
"evidence_paths": [
"jarca_ledger_mcp/application/account_valuation_service.py",
"jarca_ledger_mcp/application/assets_service.py",
"docs/data-model.md",
"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_review59_account.py::test_direct_correction_requires_matching_existing_parent_and_reason",
"tests/test_issue_12_review59_account.py::test_correction_rejects_self_parent_and_cycles_without_write",
"tests/test_issue_12_review59_reports.py::test_idempotent_snapshot_close_replay_returns_original_immutable_response"
],
"verification": "Current source and tests cover valuation correction, evidence identity, close replay, and no-write conflicts."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.04",
"semantic_key": "integer_amounts_nullable_optional_metrics_and_representative_liability",
"meaning": "Amounts and optional monetary fields use exact integers; NULL means not provided and zero means explicit zero; investment metrics are not double-counted; only the defined representative loan amount enters liabilities.",
"reviewed_ids": [
"REQ-003.04",
"REQ-003.04-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"N02"
],
"planned_status": "strengthen_field_mapping_and_formula_assertions; semantics_unchanged",
"authorization": "Preserve physical principal fields, NULL/zero distinction, loan definition requirement, and no double sum.",
"evidence_paths": [
"docs/data-model.md",
"docs/interface-contract.md",
"jarca_ledger_mcp/application/account_valuation_service.py",
"jarca_ledger_mcp/application/assets_service.py",
"tests/test_issue_12_review59_account.py::test_valuation_round_trip_preserves_null_zero_and_physical_amount_mapping",
"tests/test_issue_12_regressions.py::test_loan_representative_is_always_aggregated_as_liability",
"tests/test_issue_12_regressions.py::test_loan_balance_fallback_without_representative_definition_is_excluded"
],
"verification": "The reviewed contract explicitly distinguishes NULL, zero, principal meanings, and representative liability."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.05",
"semantic_key": "current_display_and_historical_authority_separation",
"meaning": "Historical dated evidence does not roll back newer current balance display; account/import writes create only dated unverified candidates when real evidence exists; name-only updates create none.",
"reviewed_ids": [
"REQ-003.05",
"REQ-003.05-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"N01",
"C05"
],
"planned_status": "strengthen_current_cache_and_candidate_provenance; semantics_unchanged",
"authorization": "Preserve separate current and historical decisions, unverified candidate status, and no inferred evidence.",
"evidence_paths": [
"jarca_ledger_mcp/application/accounts_write_services.py",
"jarca_ledger_mcp/application/imports_service.py",
"docs/runtime-flow.md",
"tests/test_account_valuations.py::test_account_balance_write_records_unverified_candidate_only_with_real_basis_date",
"tests/test_account_valuations.py::test_account_name_only_update_does_not_create_candidate",
"tests/test_issue_12_regressions.py::test_historical_account_update_preserves_current_and_adds_one_candidate",
"tests/test_issue_12_review59_account.py::test_import_historical_kb_does_not_replace_newer_current_balance"
],
"verification": "The reviewed head has explicit late-history/current-cache regression tests and runtime decision guards."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.06",
"semantic_key": "read_only_preview_and_explicit_conditional_close",
"meaning": "Preview performs no database write and reports complete, incomplete, or conditional state; unsafe evidence requires explicit approval metadata; close persists immutable run evidence atomically.",
"reviewed_ids": [
"REQ-003.06",
"REQ-003.06-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"D03",
"D04"
],
"planned_status": "strengthen_preview_fingerprint_and_approval_boundary; semantics_unchanged",
"authorization": "Preserve no-write preview, explicit approval, rejection of unsafe evidence, and atomic close.",
"evidence_paths": [
"jarca_ledger_mcp/application/assets_service.py",
"jarca_ledger_mcp/adapters/streamlit/assets_gateway.py",
"docs/interface-contract.md",
"tests/test_issue_12_regressions.py::test_preview_marks_approval_eligible_missing_valuation_as_incomplete_draft",
"tests/test_issue_12_review59_reports.py::test_dated_manual_asset_is_verified_and_preview_needs_approval",
"tests/test_issue_12_review59_reports.py::test_approved_carried_close_is_conditional_and_persists_provenance",
"tests/test_issue_12_review59_reports.py::test_close_rejects_authority_drift_since_preview"
],
"verification": "Preview/close boundaries, approval fields, drift rejection, and no-partial-write behavior are documented and tested."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.07",
"semantic_key": "immutable_close_correction_and_net_worth_formula",
"meaning": "Same-basis re-close requires a reason and creates a new run and rows; old runs remain; latest run is report authority; net worth is asset buckets minus representative loan and excludes missing valuations.",
"reviewed_ids": [
"REQ-003.07",
"REQ-003.07-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"C06",
"D03"
],
"planned_status": "strengthen_latest-run_and_formula_provenance; semantics_unchanged",
"authorization": "Preserve append-only runs, correction linkage, latest-run authority, and exact formula.",
"evidence_paths": [
"jarca_ledger_mcp/application/assets_service.py",
"docs/data-model.md",
"tests/test_issue_12_regressions.py::test_empty_conditional_close_is_closed_and_second_close_requires_correction",
"tests/test_issue_12_regressions.py::test_legacy_snapshot_requires_explicit_correction_before_new_close",
"tests/test_issue_12_review59_reports.py::test_asset_close_and_report_expose_provenance_aliases"
],
"verification": "Run correction fields, latest authority, and formula semantics are present in canonical docs and tests."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.08",
"semantic_key": "reconciliation_and_monthly_read_model_propagation",
"meaning": "Verified investment uses VALUATION_MANAGED without fake transactions; incomplete evidence reports unavailable/incomplete calculation; monthly and downstream reports propagate actual valuation date, age, carry, verification, run, approval, correction, warnings, and exclusions.",
"reviewed_ids": [
"REQ-003.08",
"REQ-003.08-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"C05",
"D04"
],
"planned_status": "strengthen_cross_read_model_alias_and_completeness_contract; semantics_unchanged",
"authorization": "Preserve read-only propagation, explicit incompleteness, no inferred balance, and monthly_source_snapshot_id as a read-model alias only.",
"evidence_paths": [
"jarca_ledger_mcp/application/reconciliation_service.py",
"jarca_ledger_mcp/application/monthly_settlement_service.py",
"jarca_ledger_mcp/adapters/streamlit/overview_gateway.py",
"docs/data-model.md",
"tests/test_issue_12_regressions.py::test_reconciliation_uses_corrected_investment_leaf_without_transaction_count_change",
"tests/test_issue_12_review59_reports.py::test_investment_without_verified_valuation_and_without_opening_balance_is_incomplete",
"tests/test_issue_12_review59_reports.py::test_timeseries_and_monthly_expose_canonical_asset_provenance",
"tests/test_issue_12_review59_reports.py::test_monthly_settlement_propagates_snapshot_provenance_id_and_actual_valuation_date"
],
"verification": "Named source owners and report propagation tests cover reconciliation and monthly semantics."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.09",
"semantic_key": "shared_application_adapters_and_auth_boundary",
"meaning": "MCP and Dashboard delegate to shared services, retain success/error envelopes, require Dashboard authentication before DB access, and keep render/read paths non-mutating.",
"reviewed_ids": [
"REQ-003.09",
"REQ-003.09-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"N01",
"D04"
],
"planned_status": "strengthen_adapter_and_auth-order assertions; semantics_unchanged",
"authorization": "Preserve shared policy ownership, thin adapters, auth-before-DB, and existing MCP authentication scope.",
"evidence_paths": [
"jarca_ledger_mcp/adapters/mcp/server.py",
"jarca_ledger_mcp/adapters/streamlit/accounts_gateway.py",
"jarca_ledger_mcp/adapters/streamlit/overview_gateway.py",
"dashboard/app.py",
"tests/test_contract_regression.py",
"tests/test_dashboard_app.py",
"tests/test_dashboard_menu_transition.py"
],
"verification": "The reviewed head retains named adapter paths and existing REQ-001/REQ-002 gates; requirement files are unchanged from baseline."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003.10",
"semantic_key": "synthetic_privacy_and_excluded_scope",
"meaning": "Docs, tests, fixtures, and evidence contain only synthetic/redacted data; credentials, tokens, cookies, real financial data, external prices, FX, automatic approval, inferred transactions, and redesign remain out of scope.",
"reviewed_ids": [
"REQ-003.10",
"REQ-003.10-AC01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"N02",
"D04"
],
"planned_status": "strengthen_recursive_redaction_and_static_inventory; semantics_unchanged",
"authorization": "Preserve privacy boundary and excluded scope; rework may add redaction checks but may not introduce real data or new financial inference.",
"evidence_paths": [
"docs/project-structure.md",
"docs/interface-contract.md",
"tests/test_requirement_docs.py",
"tests/test_issue_12_review59_reports.py::test_close_metadata_is_allowlisted_recursively",
"tests/test_issue_12_review59_reports.py::test_public_included_items_redact_source_note_and_memo_recursively"
],
"verification": "Canonical docs state synthetic-only and recursive redaction; focused tests validate metadata and evidence redaction."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003-INV01",
"semantic_key": "authority_and_safety_invariants",
"meaning": "Only verified leaves are authority; future/unverified/rejected/unknown evidence is not summed; preview/render do not write; close is transactional; current and historical authority remain separate.",
"reviewed_ids": [
"REQ-003-INV01"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"C05",
"D03",
"D04"
],
"planned_status": "strengthen explicit invariant checks; semantics_unchanged",
"authorization": "All listed invariants are preservation constraints, not permission to alter business meaning.",
"evidence_paths": [
"docs/requirements/req-003_account-valuation-history.md",
"docs/data-model.md",
"docs/runtime-flow.md",
"tests/test_issue_12_regressions.py",
"tests/test_issue_12_review59_reports.py"
],
"verification": "The requirement, data model, and runtime flow repeat the same authority and no-write invariants."
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"baseline_id": "REQ-003-INV02",
"semantic_key": "database_read_lifecycle_boundary",
"meaning": "Writer/bootstrap explicitly calls init_db; read_db uses SQLite mode=ro; missing DB returns db_not_initialized without mkdir/migrate/commit; initialized read failure returns db_error.",
"reviewed_ids": [
"REQ-003-INV02"
],
"reviewed_status": "present_and_approved",
"planned_ids": [
"N01"
],
"planned_status": "strengthen_no_create_and_error_classification; semantics_unchanged",
"authorization": "Preserve explicit initialization and stable read-only failure envelopes.",
"evidence_paths": [
"jarca_ledger_mcp/infrastructure/sqlite/connection.py",
"jarca_ledger_mcp/infrastructure/sqlite/migrations.py",
"docs/interface-contract.md",
"docs/runtime-flow.md",
"tests/test_issue_12_review59_readonly.py::test_read_queries_missing_nested_db_return_stable_error_without_creating_path",
"tests/test_issue_12_review59_readonly.py::test_read_schema_error_is_not_misreported_as_uninitialized"
],
"verification": "C13 database-boundary documentation and focused read-only tests exist at reviewed head."
},
{
"path": "docs/requirements/req-001_dashboard-administrator-authentication.md",
"baseline_id": "REQ-001.01-REQ-001.11",
"semantic_key": "dashboard_auth_gate_cookie_cleanup_logout_and_rotation",
"meaning": "REQ-001 authentication remains fail-closed before DB/page dispatch, with server-only credential binding, hardened persistent cookie, absolute TTL, invalid-cookie cleanup, explicit logout, rotation invalidation, and no DB-backed session.",
"reviewed_ids": [
"REQ-001.01",
"REQ-001.05",
"REQ-001.06",
"REQ-001.07",
"REQ-001.08",
"REQ-001.09",
"REQ-001.10"
],
"reviewed_status": "unchanged_from_baseline",
"planned_ids": [
"N01"
],
"planned_status": "must_remain_unchanged",
"authorization": "Issue #12 rework is additive and cannot modify authentication semantics or claim MCP authentication.",
"evidence_paths": [
"docs/requirements/req-001_dashboard-administrator-authentication.md",
"docs/interface-contract.md",
"dashboard/authentication.py",
"dashboard/auth_server.py",
"tests/test_dashboard_authentication.py",
"tests/test_dashboard_persistent_authentication.py",
"tests/test_dashboard_auth_server.py",
"tests/test_dashboard_authentication_app.py"
],
"verification": "Git diff confirms REQ-001 file has no changes between baseline and reviewed head."
},
{
"path": "docs/requirements/req-002_dashboard-menu-transition.md",
"baseline_id": "REQ-002.01-REQ-002.09",
"semantic_key": "dashboard_menu_identity_query_and_stable_shell",
"meaning": "The exact ten menu labels/order, selected state, page-only query update with sibling preservation, stable shell/background, local loading/error/retry, latest serialized selection, auth gate, finance pages, and clear_cache contract remain unchanged.",
"reviewed_ids": [
"REQ-002.01",
"REQ-002.02",
"REQ-002.03",
"REQ-002.04",
"REQ-002.05",
"REQ-002.06",
"REQ-002.07",
"REQ-002.08",
"REQ-002.09"
],
"reviewed_status": "unchanged_from_baseline",
"planned_ids": [
"N01"
],
"planned_status": "must_remain_unchanged",
"authorization": "Issue #12 rework may add valuation content behind existing pages but may not alter menu identity, query semantics, shell continuity, or browser evidence requirements.",
"evidence_paths": [
"docs/requirements/req-002_dashboard-menu-transition.md",
"docs/interface-contract.md",
"dashboard/app.py",
"dashboard/common.py",
"tests/test_dashboard_menu_transition.py",
"tests/test_dashboard_app.py",
"tests/test_dashboard_authentication_app.py",
"docs/evidence/issue-9-dashboard-menu-transition/"
],
"verification": "Git diff confirms REQ-002 file has no changes between baseline and reviewed head."
},
{
"path": "docs/interface-contract.md",
"baseline_id": "COMMON-IFACE-01",
"semantic_key": "success_error_envelope_and_public_service_signatures",
"meaning": "Existing success/error envelopes remain; valuation, snapshot, MCP, Dashboard gateway, and service signatures are additive and re-exported through the established service boundary.",
"reviewed_ids": [
"COMMON-IFACE-01"
],
"reviewed_status": "preserved_with_additive_REQ003_contract",
"planned_ids": [
"D04"
],
"planned_status": "strengthen_exact_output_and_replay_contract; semantics_unchanged",
"authorization": "No existing response key may be renamed or removed.",
"evidence_paths": [
"docs/interface-contract.md",
"jarca_ledger_mcp/service.py",
"jarca_ledger_mcp/adapters/mcp/server.py",
"tests/test_contract_regression.py"
],
"verification": "Reviewed contract explicitly lists signatures, envelopes, top-level aliases, nested provenance, and replay rules."
},
{
"path": "docs/data-model.md",
"baseline_id": "COMMON-DATA-01",
"semantic_key": "legacy_entities_and_storage_ownership",
"meaning": "Existing ledger entities and relationships remain; valuation and snapshot history are append-only; monthly alias is not a persistence field; application services own policy and SQLite owns storage.",
"reviewed_ids": [
"COMMON-DATA-01"
],
"reviewed_status": "preserved_with_additive_REQ003_model",
"planned_ids": [
"N02",
"D03"
],
"planned_status": "strengthen_physical_field_and_ownership_checks; semantics_unchanged",
"authorization": "No guessed migration, cascade deletion, fake transaction, or alternate policy boundary is authorized.",
"evidence_paths": [
"docs/data-model.md",
"jarca_ledger_mcp/infrastructure/sqlite/schema.py",
"jarca_ledger_mcp/application/assets_service.py",
"jarca_ledger_mcp/application/reconciliation_service.py"
],
"verification": "Data model names legacy preservation, append-only relationships, physical fields, and canonical owners."
},
{
"path": "docs/runtime-flow.md",
"baseline_id": "COMMON-RUNTIME-01",
"semantic_key": "auth_before_db_and_explicit_write_boundaries",
"meaning": "Dashboard auth precedes DB access; read/preview paths are read-only; first-use writer initialization is explicit; approval precedes atomic close; downstream reports are reads.",
"reviewed_ids": [
"COMMON-RUNTIME-01"
],
"reviewed_status": "preserved_with_additive_REQ003_flow",
"planned_ids": [
"N01",
"D03",
"D04"
],
"planned_status": "strengthen_transition_guards_and_drift_revalidation; semantics_unchanged",
"authorization": "Preserve ordering, transaction boundaries, stable missing-DB behavior, and no implicit writes.",
"evidence_paths": [
"docs/runtime-flow.md",
"dashboard/app.py",
"jarca_ledger_mcp/infrastructure/sqlite/connection.py",
"jarca_ledger_mcp/application/assets_service.py",
"tests/test_issue_12_review59_readonly.py"
],
"verification": "Runtime flow explicitly records auth-before-DB, read-only database lifecycle, preview drift revalidation, and atomic close."
},
{
"path": "docs/project-structure.md",
"baseline_id": "COMMON-STRUCTURE-01",
"semantic_key": "canonical_module_ownership_and_regression_map",
"meaning": "Valuation/snapshot policy remains in application services; adapters remain thin; Dashboard pages do not own SQL or policy; named tests and documentation validators are required evidence.",
"reviewed_ids": [
"COMMON-STRUCTURE-01"
],
"reviewed_status": "preserved_with_REQ003_ownership_map",
"planned_ids": [
"N01",
"C06"
],
"planned_status": "strengthen ownership and named-test traceability; semantics_unchanged",
"authorization": "Rework must preserve canonical owners and may not introduce duplicate business logic in adapters.",
"evidence_paths": [
"docs/project-structure.md",
"jarca_ledger_mcp/application/account_valuation_service.py",
"jarca_ledger_mcp/application/assets_service.py",
"jarca_ledger_mcp/application/monthly_settlement_service.py",
"jarca_ledger_mcp/infrastructure/sqlite/connection.py",
"tests/test_requirement_docs.py"
],
"verification": "Reviewed structure document contains explicit ownership, dependency, preservation, and named regression-test sections."
}
],
"missing_count": 0,
"ambiguous_count": 0,
"notes": [
"REQ-001 and REQ-002 requirement files are byte-for-byte unchanged from baseline; common docs were extended additively for REQ-003.",
"Source contract digest supplied by the caller is 6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818.",
"The checked-out repository does not define a canonical glossary mapping finding labels N01, C05, C06, N02, D03, and D04 to independent issue descriptions; planned mappings above are therefore preservation-oriented semantic associations inferred from the reviewed contract text.",
"No files were created or modified; no commit, push, fetch, or Gitea call was performed.",
"Main-agent resolution: Review #60 body is the authoritative glossary for N01/C05/C06/N02/D03/D04; each label and prose was read from admission-snapshot.json, so the delegate-reported label-mapping ambiguity is resolved without changing contract meaning."
],
"schema": "gitea-agent.contract-preservation-inventory/v1"
}
File diff suppressed because one or more lines are too long
@@ -1,181 +0,0 @@
{
"environment": [
"os",
"kernel",
"architecture",
"python",
"uv"
],
"command_env_allowlist": [
"PATH",
"HOME",
"TMPDIR",
"LANG",
"VIRTUAL_ENV",
"UV_CACHE_DIR"
],
"secret_env_names": [],
"forbidden_regexes": [],
"hash_paths": [
"dashboard/page_views/accounts.py",
"docs/data-model.md",
"docs/interface-contract.md",
"jarca_ledger_mcp/application/account_valuation_service.py",
"jarca_ledger_mcp/application/accounts_write_services.py",
"jarca_ledger_mcp/application/assets_service.py",
"jarca_ledger_mcp/application/imports_service.py",
"jarca_ledger_mcp/infrastructure/sqlite/schema.py",
"scripts/ci_fixture_db.py",
"tests/test_account_valuations.py",
"tests/test_issue_12_regressions.py",
"tests/test_issue_12_review59_account.py",
"tests/test_account_valuation_edge_contracts.py",
"tests/test_asset_snapshot_idempotency.py",
"tests/test_import_current_balance_authority.py",
"tests/test_valuation_contract_docs.py",
"jarca_ledger_mcp/adapters/mcp/server.py",
"tests/test_account_basis_public_consumers.py"
],
"commands": [
{
"id": "review60-regressions",
"purpose": "Run Review 60 and all independent-review named RED-to-GREEN regressions",
"argv": [
"uv",
"run",
"--frozen",
"--extra",
"dev",
"--extra",
"dashboard",
"pytest",
"-q",
"tests/test_account_valuation_edge_contracts.py",
"tests/test_import_current_balance_authority.py",
"tests/test_asset_snapshot_idempotency.py",
"tests/test_valuation_contract_docs.py",
"tests/test_account_basis_public_consumers.py",
"tests/test_issue_12_regressions.py"
],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": [
"review60-and-independent-regressions-green"
]
},
{
"id": "adversarial-compatibility",
"purpose": "Run correction, replay, current authority, read-only, provenance, contract and documentation compatibility coverage",
"argv": [
"uv",
"run",
"--frozen",
"--extra",
"dev",
"--extra",
"dashboard",
"pytest",
"-q",
"tests/test_account_valuation_edge_contracts.py",
"tests/test_import_current_balance_authority.py",
"tests/test_asset_snapshot_idempotency.py",
"tests/test_valuation_contract_docs.py",
"tests/test_issue_12_regressions.py",
"tests/test_account_valuations.py",
"tests/test_asset_snapshots.py",
"tests/test_issue_12_review59_account.py",
"tests/test_issue_12_review59_reports.py",
"tests/test_issue_12_review59_readonly.py",
"tests/test_contract_regression.py",
"tests/test_requirement_docs.py"
],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": [
"adversarial-compatibility-green"
]
},
{
"id": "full-suite",
"purpose": "Run the repository full test suite with declared optional dependencies",
"argv": [
"uv",
"run",
"--frozen",
"--extra",
"dev",
"--extra",
"dashboard",
"pytest",
"-q",
"-rs"
],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": [
"full-suite-green"
]
},
{
"id": "ruff",
"purpose": "Run repository lint gate",
"argv": [
"uv",
"run",
"--frozen",
"--extra",
"dev",
"--extra",
"dashboard",
"ruff",
"check",
"."
],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": [
"ruff-green"
]
},
{
"id": "compileall",
"purpose": "Compile Python sources, dashboard, tests and scripts",
"argv": [
"python3",
"-m",
"compileall",
"-q",
"jarca_ledger_mcp",
"dashboard",
"tests",
"scripts"
],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": [
"compileall-green"
]
},
{
"id": "commit-diff-check",
"purpose": "Validate committed rework diff whitespace",
"argv": [
"git",
"diff",
"--check",
"HEAD^",
"HEAD"
],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": [
"diff-check-green"
]
}
],
"limitations": [
"Four external PDF fixture tests are expected to skip when private sample files/passwords are unavailable.",
"No actual financial data or production credential is used; tests use synthetic fixtures.",
"Review 60 does not require a new real-browser observation; existing synthetic Streamlit/AppTest regressions remain in the full suite."
]
}
@@ -1,16 +0,0 @@
{
"specification_gate": {
"passed": true,
"findings": [],
"summary": "Exact-current-diff specification gate passed. Review60 semantics N01, C05, C06, N02, D03, D04; prior defects; documentation, adapters, dashboard consumers, fixtures, and named regressions were rechecked. Full pytest suite, Ruff, compileall, and git diff --check passed."
},
"code_security_gate": {
"passed": true,
"security_concerns": [],
"logic_errors": [],
"suggestions": [],
"summary": "Final current-diff code/security gate passed. Full pytest suite passed with four fixture-dependent skips and one existing Starlette deprecation warning; Ruff, compileall, diff-check, migration/idempotency/consumer tests, and added-line security scan passed."
},
"reviewed_parent_sha": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"delegation_id": "deleg_49abf596"
}
@@ -1,148 +0,0 @@
{
"schema": "gitea-agent.finding-matrix/v1",
"review_id": 60,
"review_author": "gitea-spec-agent",
"review_state": "REQUEST_CHANGES",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"findings": [
{
"finding": "60/N01-loan-balance-definition",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract": "Issue #12 확정 결정 8; REQ-003.04/05",
"classification": "valid_fix",
"implementation_paths": [
"jarca_ledger_mcp/application/account_valuation_service.py"
],
"test_paths": [
"tests/test_account_valuation_edge_contracts.py"
],
"verification": "named RED→GREEN test plus consolidated adversarial suite",
"result": "pending",
"reason": "Loan account balance alias bypasses mandatory representative liability definition."
},
{
"finding": "60/C05-current-import",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract": "Issue #12 현재값과 과거값; REQ-003.05",
"classification": "valid_fix",
"implementation_paths": [
"jarca_ledger_mcp/application/imports_service.py",
"jarca_ledger_mcp/application/accounts_write_services.py",
"jarca_ledger_mcp/infrastructure/sqlite/schema.py",
"jarca_ledger_mcp/infrastructure/sqlite/migrations.py"
],
"test_paths": [
"tests/test_import_current_balance_authority.py"
],
"verification": "named RED→GREEN sequential late-import test plus migration/full suite",
"result": "pending",
"reason": "Import compares actual evidence date against synchronization timestamp and loses current-cache basis authority."
},
{
"finding": "60/C06-idempotency",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract": "Issue #12 중복 및 정정; REQ-003.03",
"classification": "valid_fix",
"implementation_paths": [
"jarca_ledger_mcp/application/account_valuation_service.py"
],
"test_paths": [
"tests/test_account_valuation_edge_contracts.py"
],
"verification": "named RED→GREEN same-evidence correction replay and changed-payload conflict tests",
"result": "pending",
"reason": "A same source_ref/parent/effective payload correction retry without explicit key conflicts instead of reusing the child."
},
{
"finding": "60/N02-close-replay-run-binding",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract": "Issue #12 마감 불변성; REQ-003.03/07",
"classification": "valid_fix",
"implementation_paths": [
"jarca_ledger_mcp/application/assets_service.py"
],
"test_paths": [
"tests/test_asset_snapshot_idempotency.py"
],
"verification": "named RED→GREEN old-key replay-after-correction exact response binding test",
"result": "pending",
"reason": "Replay identifies the old run but loads latest-run rows and metadata."
},
{
"finding": "60/D03-data-model",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract": "Issue #12 데이터 모델 산출물; REQ-003.05",
"classification": "evidence_gap",
"implementation_paths": [
"docs/data-model.md"
],
"test_paths": [
"tests/test_valuation_contract_docs.py"
],
"verification": "named RED→GREEN canonical data-model contract assertion",
"result": "pending",
"reason": "Data model does not fix actual cache basis versus sync timestamp authority and no-rollback owner."
},
{
"finding": "60/D04-interface-contract",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract": "Issue #12 인터페이스 계약 산출물; REQ-003.04/09",
"classification": "evidence_gap",
"implementation_paths": [
"docs/interface-contract.md"
],
"test_paths": [
"tests/test_valuation_contract_docs.py"
],
"verification": "named RED→GREEN exact enum/conditional/output mapping assertion",
"result": "pending",
"reason": "Public interface delegates allowed values and loan definition/output mapping to source."
},
{
"finding": "independent-spec/C05-account-write-no-inference",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract": "Issue #12 current/history separation and no inferred basis; REQ-003.05",
"classification": "valid_fix",
"implementation_paths": [
"jarca_ledger_mcp/application/accounts_write_services.py"
],
"test_paths": [
"tests/test_import_current_balance_authority.py"
],
"verification": "named RED→GREEN explicit-basis and no-candidate account-write regression",
"result": "pending",
"reason": "Account writes inferred evidence basis and historical candidate from sync timestamp."
},
{
"finding": "independent-code/legacy-unknown-basis-reporting",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract": "Issue #12 unknown-basis non-retroactivity; REQ-003.05",
"classification": "valid_fix",
"implementation_paths": [
"jarca_ledger_mcp/application/assets_service.py"
],
"test_paths": [
"tests/test_import_current_balance_authority.py"
],
"verification": "named RED→GREEN legacy unknown-basis report regression",
"result": "pending",
"reason": "Live report exposed last_synced_at as valuation_date despite unknown evidence basis."
},
{
"finding": "independent-code/zero-row-run-replay",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"contract": "Issue #12 immutable close evidence; REQ-003.03/07",
"classification": "valid_fix",
"implementation_paths": [
"jarca_ledger_mcp/application/assets_service.py"
],
"test_paths": [
"tests/test_asset_snapshot_idempotency.py"
],
"verification": "named RED→GREEN empty-run replay after nonempty correction exact equality",
"result": "pending",
"reason": "Empty persisted applied_evidence triggered fallback to latest correction rows."
}
]
}
@@ -1,82 +0,0 @@
{
"schema": "gitea-agent.red-regression-evidence/v1",
"reviewed_head": "6a8a27a74ece9a9cce6f9772259677813d91ddf8",
"environment": {
"runner": "uv run --frozen --extra dev --extra dashboard pytest",
"fixture_policy": "synthetic only"
},
"results": [
{
"finding": "60/N01-loan-balance-definition",
"test": "tests/test_account_valuation_edge_contracts.py::test_loan_balance_requires_representative_liability_definition",
"exit_code": 1,
"failure": "missing_definition success was True; expected validation_error and no persisted valuation"
},
{
"finding": "60/C06-idempotency",
"test": "tests/test_account_valuation_edge_contracts.py::test_correction_replays_by_evidence_and_conflicts_on_changed_payload",
"exit_code": 1,
"failure": "identical correction replay success was False; expected reused=True with the original child"
},
{
"finding": "60/C05-current-import",
"test": "tests/test_import_current_balance_authority.py::test_late_import_current_balance_is_authoritative_by_evidence_basis_date",
"exit_code": 1,
"failure": "current last_balance was 700 after newer-basis late import; expected 900"
},
{
"finding": "60/N02-close-replay-run-binding",
"test": "tests/test_asset_snapshot_idempotency.py::test_exact_run_replay_after_correction_reconstructs_original_persisted_run",
"exit_code": 1,
"failure": "old-key replay returned correction-run row IDs, approval and provenance instead of exact original response"
},
{
"finding": "60/D03-data-model",
"test": "tests/test_valuation_contract_docs.py::test_data_model_documents_cache_authority_and_legacy_basis_migration",
"exit_code": 1,
"failure": "canonical last_balance current-cache contract marker was absent"
},
{
"finding": "60/D04-interface-contract",
"test": "tests/test_valuation_contract_docs.py::test_interface_contract_documents_public_valuation_enums_validation_and_mapping",
"exit_code": 1,
"failure": "interface contract still delegated allowed values to 'actual enums in source'"
},
{
"finding": "independent-spec/C05-account-write-no-inference",
"test": "tests/test_import_current_balance_authority.py::test_account_write_sync_timestamp_does_not_infer_balance_basis_or_candidate",
"exit_code": 1,
"failure": "account creation inferred last_balance_basis_date=2026-07-01 from last_synced_at; expected unknown basis and no candidate"
},
{
"finding": "independent-code/legacy-unknown-basis-reporting",
"test": "tests/test_import_current_balance_authority.py::test_unknown_legacy_balance_basis_is_not_reported_as_valuation_date",
"exit_code": 1,
"failure": "excluded valuation_date exposed last_synced_at timestamp; expected empty unknown basis"
},
{
"finding": "independent-code/zero-row-run-replay",
"test": "tests/test_asset_snapshot_idempotency.py::test_empty_run_replay_after_nonempty_correction_preserves_original_empty_payload",
"exit_code": 1,
"failure": "old empty run replay fell back to later run items and returned total_assets/net_worth=100 instead of exact original empty response"
},
{
"finding": "independent-code/historical-explicit-basis-no-rollback",
"test": "tests/test_issue_12_regressions.py::test_historical_account_update_preserves_current_and_adds_one_candidate",
"exit_code": 1,
"failure": "historical explicit update replaced the newer current cache instead of retaining current state while adding one historical candidate"
},
{
"finding": "independent-code/dashboard-basis-consumer",
"test": "tests/test_account_basis_public_consumers.py::test_dashboard_valuation_basis_never_uses_sync_timestamp",
"exit_code": 1,
"failure": "Dashboard page_accounts used last_synced_at for valuation selection and registration defaults"
},
{
"finding": "independent-code/mcp-basis-forwarding",
"test": "tests/test_account_basis_public_consumers.py::test_mcp_account_write_tools_forward_explicit_balance_basis",
"exit_code": 1,
"failure": "MCP account-write tools rejected/dropped last_balance_basis_date"
}
]
}
@@ -1,50 +0,0 @@
{
"schema": "gitea-agent.contract-preservation-inventory/v1",
"owner": "jsfamily",
"repository": "jarca-ledger-mcp",
"issue": 12,
"pull_request": 13,
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"review_id": 61,
"reviewed_parent_sha": "5e721b0ddd648f3e80f341a3c304d1cc3ca47ee8",
"reviewed_head": "1b9eda683c66d3c7129716b9899eddf61df3362d",
"preserved_semantics": [
{
"criterion": "N01-loan-balance-definition",
"required": "Every valuation attached to a loan account explicitly records principal_only or principal_interest_fees; no writer or account-type transition may infer or bypass it.",
"evidence": [
"tests/test_account_valuation_edge_contracts.py",
"tests/test_issue_12_review59_account.py"
]
},
{
"criterion": "C05-current-import",
"required": "Loan cache and import operations remain successful without creating definition-less history, and late evidence never rolls back newer current cache.",
"evidence": [
"test_dated_loan_create_and_historical_update_preserve_current_cache_without_valuations",
"test_loan_import_preserves_transactions_and_current_cache_without_valuation_candidate"
]
},
{
"criterion": "C13-read-only-and-no-partial-write",
"required": "Validation failure changes neither account state nor append-only valuation history.",
"evidence": [
"test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation"
]
},
{
"criterion": "compatibility",
"required": "A non-loan account with no valuation history may still be converted to loan; non-loan candidate insertion and replay are unchanged.",
"evidence": [
"test_bank_to_loan_conversion_without_valuations_remains_allowed",
"test_non_loan_candidate_still_succeeds_and_reuses_idempotently"
]
}
],
"excluded_scope": [
"No inferred or automatic liability-definition migration.",
"No schema change.",
"No changes to snapshot, reconciliation, dashboard, authentication, or menu behavior.",
"No merge, Issue close, or release operation."
]
}
@@ -1,208 +0,0 @@
{
"schema": "gitea-agent.evidence-manifest/v1",
"owner": "jsfamily",
"repository": "jarca-ledger-mcp",
"issue": 12,
"pull_request": 13,
"review": 61,
"reviewed_parent_sha": "5e721b0ddd648f3e80f341a3c304d1cc3ca47ee8",
"source_head_sha": "1b9eda683c66d3c7129716b9899eddf61df3362d",
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"environment": {
"platform": "Linux-6.18.34+rpt-rpi-v8-aarch64-with-glibc2.41",
"python": "3.13.5",
"uv": "uv 0.11.6 (aarch64-unknown-linux-musl)",
"fixture_policy": "synthetic repository fixtures only; no credentials or real financial data"
},
"commands": [
{
"id": "conversion-regression",
"argv": [
"uv",
"run",
"--frozen",
"--extra",
"dev",
"--extra",
"dashboard",
"pytest",
"-q",
"-rs",
"tests/test_account_valuation_edge_contracts.py::test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation",
"tests/test_account_valuation_edge_contracts.py::test_bank_to_loan_conversion_without_valuations_remains_allowed"
],
"purpose": "Verify conversion rejection is atomic and history-free conversion remains compatible",
"started_at_utc": "2026-10-07T03:59:49.130624Z",
"finished_at_utc": "2026-10-07T03:59:51.888407Z",
"duration_seconds": 2.757882494479418,
"exit_code": 0,
"expected_exit_code": 0,
"output": ".. [100%]\n"
},
{
"id": "review61-regressions",
"argv": [
"uv",
"run",
"--frozen",
"--extra",
"dev",
"--extra",
"dashboard",
"pytest",
"-q",
"-rs",
"tests/test_account_valuation_edge_contracts.py",
"tests/test_issue_12_review59_account.py",
"tests/test_valuation_contract_docs.py"
],
"purpose": "Run Review 61 and independent-review named regressions",
"started_at_utc": "2026-10-07T03:59:51.888570Z",
"finished_at_utc": "2026-10-07T03:59:58.351942Z",
"duration_seconds": 6.463426006026566,
"exit_code": 0,
"expected_exit_code": 0,
"output": "........................ [100%]\n"
},
{
"id": "full-suite",
"argv": [
"uv",
"run",
"--frozen",
"--extra",
"dev",
"--extra",
"dashboard",
"pytest",
"-q",
"-rs"
],
"purpose": "Run the complete repository test suite at the exact source head",
"started_at_utc": "2026-10-07T03:59:58.352023Z",
"finished_at_utc": "2026-10-07T04:01:03.222283Z",
"duration_seconds": 64.87031899858266,
"exit_code": 0,
"expected_exit_code": 0,
"output": "........................................................................ [ 25%]\n........................................................................ [ 51%]\n...............................................................ss....... [ 77%]\n.....................................................ss...... [100%]\n=============================== warnings summary ===============================\ntests/test_dashboard_auth_server.py:11\n /opt/data/gitea-agent/repositories/jsfamily/jarca-ledger-mcp/worktrees/issue-12/checkout/tests/test_dashboard_auth_server.py:11: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.\n from starlette.testclient import TestClient\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n=========================== short test summary info ============================\nSKIPPED [1] tests/test_kb_bank_pdf.py:26: sample KB Bank PDF/password is not available\nSKIPPED [1] tests/test_kb_bank_pdf.py:42: sample KB Bank PDF/password is not available\nSKIPPED [1] tests/test_toss_securities_pdf.py:20: sample Toss Securities PDF is not available\nSKIPPED [1] tests/test_toss_securities_pdf.py:32: sample Toss Securities PDF is not available\n"
},
{
"id": "ruff",
"argv": [
"uv",
"run",
"--frozen",
"--extra",
"dev",
"--extra",
"dashboard",
"ruff",
"check",
"."
],
"purpose": "Run Python lint checks",
"started_at_utc": "2026-10-07T04:01:03.222360Z",
"finished_at_utc": "2026-10-07T04:01:03.426930Z",
"duration_seconds": 0.20462407637387514,
"exit_code": 0,
"expected_exit_code": 0,
"output": "All checks passed!\n"
},
{
"id": "compileall",
"argv": [
"python3",
"-m",
"compileall",
"-q",
"jarca_ledger_mcp",
"dashboard",
"tests",
"scripts"
],
"purpose": "Compile production, dashboard, test, and script Python surfaces",
"started_at_utc": "2026-10-07T04:01:03.427003Z",
"finished_at_utc": "2026-10-07T04:01:03.684488Z",
"duration_seconds": 0.25754371751099825,
"exit_code": 0,
"expected_exit_code": 0,
"output": ""
},
{
"id": "diff-check",
"argv": [
"git",
"diff",
"--check",
"5e721b0ddd648f3e80f341a3c304d1cc3ca47ee8..1b9eda683c66d3c7129716b9899eddf61df3362d"
],
"purpose": "Check the complete rework diff for whitespace errors",
"started_at_utc": "2026-10-07T04:01:03.684567Z",
"finished_at_utc": "2026-10-07T04:01:03.730543Z",
"duration_seconds": 0.04603164829313755,
"exit_code": 0,
"expected_exit_code": 0,
"output": ""
}
],
"changed_files": [
{
"path": "docs/data-model.md",
"sha256": "f9932a04f3c74907da1c21d754a16e1401f64fd948945017d410de7d91494dca",
"git_blob": "70f26ce1522b8609a93471655c9506040a4078f1"
},
{
"path": "docs/interface-contract.md",
"sha256": "01a3f37e23ee17b2b928b44e3e07d4d6982faead1ab6f0298889225b0c5ce916",
"git_blob": "4304427a604949b8295b4f93cb6054357805fe92"
},
{
"path": "docs/requirements/req-003_account-valuation-history.md",
"sha256": "f5a3bf9f69d622451a1a08854062218382f03355a3464538a35b1e270ca5b70f",
"git_blob": "2a7be4d780999fce47402f15f0b05d0b92990715"
},
{
"path": "docs/verification/issue-12-review-61/red-evidence.json",
"sha256": "567c03d28e6de74b6adfc0f6141dd96e9e129bab9d4455761615a1655870b4bf",
"git_blob": "b5f4bc9286b4ea443671ab0744935a11c295b124"
},
{
"path": "jarca_ledger_mcp/application/account_valuation_service.py",
"sha256": "f7f5bac1e224401dfd02efb321b7e7bd87e02875b727a5366bcc223bf633fb57",
"git_blob": "0dfd7b7ce94a699b855e40b336afa7bf5cdc2595"
},
{
"path": "jarca_ledger_mcp/application/accounts_write_services.py",
"sha256": "41dd6579dc422549316da05347c6d5e33d89da1e1502d6065b5b78a0160776d3",
"git_blob": "48a1dd5acfe782c9cc35e8bd615dc71671dadf6f"
},
{
"path": "tests/test_account_valuation_edge_contracts.py",
"sha256": "fed17aec3518e2380d00b46252402ad69a71e7d265961a6264b03d790d579f42",
"git_blob": "0b08cb32af63084225e01642d333ca291c7445c4"
},
{
"path": "tests/test_issue_12_review59_account.py",
"sha256": "a8a7491c7b78841c2be0dbc66913c1e076bdd231281fd64818493a0f3a1de550",
"git_blob": "f5cee3e993f189e688f8e8ab3829b5c4ab34cf5a"
},
{
"path": "tests/test_valuation_contract_docs.py",
"sha256": "7443b8425d6b733e0e95d21b3c460a42dceeb2f99f84e1fb8fadcd88f6b77514",
"git_blob": "c473ee14a530496e11c28e33008b0d8a50d774cb"
}
],
"added_line_security_scan": {
"hardcoded_secret": [],
"shell_injection": [],
"dynamic_eval": [],
"pickle": [],
"interpolated_sql": []
},
"worktree_clean_after": true,
"limitations": [
"Four external private PDF fixture tests may be skipped when fixtures are unavailable.",
"Gitea status/readiness is not functional acceptance evidence."
],
"changed_files_scope": "Behavior-bearing source, tests, normative docs, and immutable RED evidence at source_head_sha; mutable verification metadata and generated GREEN/manifest artifacts are intentionally excluded from hash binding."
}
@@ -1,61 +0,0 @@
{
"schema": "gitea-agent.evidence-plan/v1",
"owner": "jsfamily",
"repository": "jarca-ledger-mcp",
"issue": 12,
"pull_request": 13,
"review_id": 61,
"reviewed_parent_sha": "5e721b0ddd648f3e80f341a3c304d1cc3ca47ee8",
"reviewed_head": "1b9eda683c66d3c7129716b9899eddf61df3362d",
"implementation_parent": "b8b8811a2a50371795a02e4fa65f097c9d9508ac",
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"manifest_generator": {
"path": "/opt/data/gitea-agent/.run/i12-r61/run_final_evidence.py",
"execution": "python3 /opt/data/gitea-agent/.run/i12-r61/run_final_evidence.py"
},
"commands": [
{
"id": "conversion-regression",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-q", "-rs", "tests/test_account_valuation_edge_contracts.py::test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation", "tests/test_account_valuation_edge_contracts.py::test_bank_to_loan_conversion_without_valuations_remains_allowed"]
},
{
"id": "review61-regressions",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-q", "-rs", "tests/test_account_valuation_edge_contracts.py", "tests/test_issue_12_review59_account.py", "tests/test_valuation_contract_docs.py"]
},
{
"id": "full-suite",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-q", "-rs"]
},
{
"id": "ruff",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "ruff", "check", "."]
},
{
"id": "compileall",
"argv": ["python3", "-m", "compileall", "-q", "jarca_ledger_mcp", "dashboard", "tests", "scripts"]
},
{
"id": "diff-check",
"argv": ["git", "diff", "--check", "5e721b0ddd648f3e80f341a3c304d1cc3ca47ee8..1b9eda683c66d3c7129716b9899eddf61df3362d"]
}
],
"bound_paths": [
"jarca_ledger_mcp/application/account_valuation_service.py",
"jarca_ledger_mcp/application/accounts_write_services.py",
"tests/test_account_valuation_edge_contracts.py",
"tests/test_issue_12_review59_account.py",
"tests/test_valuation_contract_docs.py",
"docs/data-model.md",
"docs/interface-contract.md",
"docs/requirements/req-003_account-valuation-history.md",
"docs/verification/issue-12-review-61/red-evidence.json",
"docs/verification/issue-12-review-61/finding-matrix.json",
"docs/verification/issue-12-review-61/contract-preservation-inventory.json",
"docs/verification/issue-12-review-61/evidence-plan.json"
],
"redaction": "Synthetic fixtures only; no credentials, real account identifiers, financial documents, or environment secrets.",
"limitations": [
"External private PDF fixtures may be skipped when unavailable.",
"Gitea commit status is readiness metadata, not functional acceptance evidence."
]
}
@@ -1,56 +0,0 @@
{
"schema": "gitea-agent.finding-matrix/v1",
"owner": "jsfamily",
"repository": "jarca-ledger-mcp",
"issue": 12,
"pull_request": 13,
"review_id": 61,
"reviewed_parent_sha": "5e721b0ddd648f3e80f341a3c304d1cc3ca47ee8",
"reviewed_head": "1b9eda683c66d3c7129716b9899eddf61df3362d",
"implementation_parent": "b8b8811a2a50371795a02e4fa65f097c9d9508ac",
"findings": [
{
"id": "N01-loan-balance-definition",
"classification": "implementation_defect",
"status": "resolved_and_verified_at_source_head",
"root_cause": "Internal candidate writers could persist loan balance evidence without an explicit representative liability definition, and unsupported loan purposes could bypass the definition rule.",
"tests": [
"test_direct_loan_candidate_requires_liability_definition_and_writes_nothing",
"test_import_loan_candidate_requires_liability_definition_and_writes_nothing",
"test_dated_loan_create_and_historical_update_preserve_current_cache_without_valuations",
"test_loan_cash_balance_and_total_value_are_rejected_without_writes",
"test_loan_import_preserves_transactions_and_current_cache_without_valuation_candidate"
],
"changed_paths": [
"jarca_ledger_mcp/application/account_valuation_service.py",
"tests/test_account_valuation_edge_contracts.py",
"tests/test_issue_12_review59_account.py",
"docs/data-model.md",
"docs/interface-contract.md",
"docs/requirements/req-003_account-valuation-history.md"
]
},
{
"id": "N01-account-type-conversion-bypass",
"classification": "implementation_defect",
"status": "resolved",
"root_cause": "update_ledger_account allowed a non-loan account with definition-less historical valuations to become a loan account without validating the persisted history.",
"named_regression": "test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation",
"positive_regression": "test_bank_to_loan_conversion_without_valuations_remains_allowed",
"red_evidence": "docs/verification/issue-12-review-61/red-evidence.json",
"minimal_fix": "Reject non-loan to loan conversion before mutation when any attached valuation lacks one of the two explicit valid representative liability definitions.",
"changed_paths": [
"jarca_ledger_mcp/application/accounts_write_services.py",
"tests/test_account_valuation_edge_contracts.py",
"docs/data-model.md",
"docs/interface-contract.md",
"docs/requirements/req-003_account-valuation-history.md"
],
"risks": [
"Do not infer or backfill a liability definition.",
"Do not reject conversion when no valuation history exists.",
"Do not partially mutate account or valuation history on rejection."
]
}
]
}
@@ -1,30 +0,0 @@
{
"schema": "gitea-agent.green-evidence/v1",
"finding": "N01-account-type-conversion-bypass",
"source_commit": "1b9eda683c66d3c7129716b9899eddf61df3362d",
"tests": [
"test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation",
"test_bank_to_loan_conversion_without_valuations_remains_allowed"
],
"argv": [
"uv",
"run",
"--frozen",
"--extra",
"dev",
"--extra",
"dashboard",
"pytest",
"-q",
"-rs",
"tests/test_account_valuation_edge_contracts.py::test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation",
"tests/test_account_valuation_edge_contracts.py::test_bank_to_loan_conversion_without_valuations_remains_allowed"
],
"started_at_utc": "2026-10-07T03:59:49.130624Z",
"finished_at_utc": "2026-10-07T03:59:51.888407Z",
"exit_code": 0,
"expected_exit_code": 0,
"result": "GREEN",
"output": ".. [100%]\n",
"fixture_policy": "synthetic only; no credentials or real financial data"
}
@@ -1,9 +0,0 @@
{
"gate": "independent-bank-to-loan-conversion",
"source_sha": "b8b8811a2a50371795a02e4fa65f097c9d9508ac",
"test": "tests/test_account_valuation_edge_contracts.py::test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation",
"command": "uv run --frozen --extra dev --extra dashboard pytest tests/test_account_valuation_edge_contracts.py::test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation -q",
"exit_code": 1,
"result": "RED",
"output": "F [100%]\n=================================== FAILURES ===================================\n_ test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation _\n\n assert rejected[\"success\"] is False\nE assert True is False\n=========================== short test summary info ============================\nFAILED tests/test_account_valuation_edge_contracts.py::test_bank_to_loan_conversion_rejects_existing_undefined_valuation_without_mutation"
}
@@ -1,36 +0,0 @@
{
"schema": "contract-preservation-inventory/v1",
"issue": 12,
"pull_request": 13,
"review_id": 63,
"source_revision": "3db086a7fd15e9152a57fb95ab7942c6dc3be2b1",
"contracts": [
{
"contract": "append-only immutable snapshot runs and corrections",
"preserved_by": ["test_f002_correction_replay_preserves_full_initial_response", "test_f006_initial_replay_survives_multiple_later_corrections", "tests/test_asset_snapshot_idempotency.py"]
},
{
"contract": "preview-to-close authority drift protection",
"preserved_by": ["test_f001_preview_fingerprint_uses_same_unredacted_authority_and_rejects_drift"]
},
{
"contract": "conditional zero-row closes remain visible historical authority",
"preserved_by": ["test_f003_zero_row_conditional_runs_are_timeseries_authority_with_aliases"]
},
{
"contract": "closed consumers use frozen valuation identity rather than live valuation state",
"preserved_by": ["test_f004_closed_report_restores_frozen_valuation_identity_without_legacy_invention"]
},
{
"contract": "NULL and zero remain distinct and balance basis dates require an actual balance",
"preserved_by": ["test_create_rejects_basis_only_without_mutation", "test_basis_only_create_cannot_block_later_dated_import"]
},
{
"contract": "existing Issue 12 behavior outside Review 63 remains intact",
"preserved_by": ["review63-full-suite", "review63-ruff", "review63-compileall", "review63-diff-check"]
}
],
"limitations": [
"Private KB Bank and Toss Securities PDF fixtures were unavailable; four fixture-dependent tests are reported as skips."
]
}
File diff suppressed because one or more lines are too long
@@ -1,62 +0,0 @@
{
"environment": ["os", "kernel", "architecture", "python", "uv"],
"command_env_allowlist": ["PATH", "HOME", "TMPDIR", "LANG", "VIRTUAL_ENV", "UV_CACHE_DIR"],
"secret_env_names": [],
"forbidden_regexes": [],
"hash_paths": [
"jarca_ledger_mcp/application/accounts_write_services.py",
"jarca_ledger_mcp/application/assets_service.py",
"tests/test_issue_12_review63_accounts.py",
"tests/test_issue_12_review63_assets.py",
"tests/test_asset_snapshot_idempotency.py",
"tests/test_issue_12_regressions.py",
"docs/interface-contract.md",
"docs/data-model.md",
"docs/requirements/req-003_account-valuation-history.md"
],
"commands": [
{
"id": "review63-regressions",
"purpose": "Verify all named Review 63 RED to GREEN regressions",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q", "tests/test_issue_12_review63_accounts.py", "tests/test_issue_12_review63_assets.py"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review63-findings-green"]
},
{
"id": "full-suite",
"purpose": "Run the canonical full repository suite at the functional source revision",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-ra"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review63-full-suite"]
},
{
"id": "ruff",
"purpose": "Run repository lint checks",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "ruff", "check", "."],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review63-ruff"]
},
{
"id": "compileall",
"purpose": "Compile package, dashboard, tests, and scripts",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "python", "-m", "compileall", "-q", "jarca_ledger_mcp", "dashboard", "tests", "scripts"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review63-compileall"]
},
{
"id": "diff-check",
"purpose": "Check the complete pull-request diff for whitespace errors",
"argv": ["git", "diff", "--check", "main...HEAD"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review63-diff-check"]
}
],
"limitations": [
"Four PDF fixture tests are skipped because private KB Bank and Toss Securities PDF fixtures are unavailable."
]
}
@@ -1,59 +0,0 @@
{
"schema": "review-finding-matrix/v1",
"issue": 12,
"pull_request": 13,
"review_id": 63,
"reviewed_head": "3b5101128d5a2cf038b8fb84facfd9d176928c26",
"source_revision": "3db086a7fd15e9152a57fb95ab7942c6dc3be2b1",
"findings": [
{
"id": "F-001",
"status": "resolved",
"production": "jarca_ledger_mcp/application/assets_service.py",
"regression": "test_f001_preview_fingerprint_uses_same_unredacted_authority_and_rejects_drift",
"claim": "preview and close hash the same private authority representation; real drift is rejected without a run write"
},
{
"id": "F-002",
"status": "resolved",
"production": "jarca_ledger_mcp/application/assets_service.py",
"regressions": [
"test_f002_close_replay_is_byte_for_byte_equal_for_mixed_account_manual_as_of",
"test_f002_correction_replay_preserves_full_initial_response"
],
"claim": "initial and correction replays reconstruct the original immutable response, warning set, provenance, item ordering, and IDs"
},
{
"id": "F-003",
"status": "resolved",
"production": "jarca_ledger_mcp/application/assets_service.py",
"regression": "test_f003_zero_row_conditional_runs_are_timeseries_authority_with_aliases",
"claim": "timeseries enumerates both legacy snapshot and run-only basis dates and exposes the latest zero-row conditional run"
},
{
"id": "F-004",
"status": "resolved",
"production": "jarca_ledger_mcp/application/assets_service.py",
"regression": "test_f004_closed_report_restores_frozen_valuation_identity_without_legacy_invention",
"claim": "closed reports restore persisted valuation identity and currency without consulting live authority or inventing legacy IDs"
},
{
"id": "F-005",
"status": "resolved",
"production": "jarca_ledger_mcp/application/accounts_write_services.py",
"regressions": [
"test_create_rejects_basis_only_without_mutation",
"test_basis_only_create_cannot_block_later_dated_import"
],
"claim": "basis-only creates fail before mutation while zero-with-basis and unknown pairs remain valid"
},
{
"id": "F-006",
"status": "resolved",
"production": "jarca_ledger_mcp/application/assets_service.py",
"regression": "test_f006_initial_replay_survives_multiple_later_corrections",
"claim": "an immutable run can be replayed through the complete later correction chain while unrelated live-authority drift still conflicts"
}
],
"green_evidence": "evidence-manifest.json"
}
@@ -1,74 +0,0 @@
{
"schema": "review-red-evidence/v1",
"issue": 12,
"pull_request": 13,
"review_id": 63,
"reviewed_head": "3b5101128d5a2cf038b8fb84facfd9d176928c26",
"review_state": "REQUEST_CHANGES",
"findings": [
{
"id": "F-001",
"test": "test_f001_preview_fingerprint_uses_same_unredacted_authority_and_rejects_drift",
"red_observation": "unchanged preview fingerprint was rejected with authority_drift"
},
{
"id": "F-002",
"tests": [
"test_f002_close_replay_is_byte_for_byte_equal_for_mixed_account_manual_as_of",
"test_f002_correction_replay_preserves_full_initial_response"
],
"red_observation": "replay responses differed in warnings/provenance/items or conflicted"
},
{
"id": "F-003",
"test": "test_f003_zero_row_conditional_runs_are_timeseries_authority_with_aliases",
"red_observation": "zero-row closed run produced no timeseries point"
},
{
"id": "F-004",
"test": "test_f004_closed_report_restores_frozen_valuation_identity_without_legacy_invention",
"red_observation": "closed report omitted persisted valuation_id and currency"
},
{
"id": "F-005",
"tests": [
"test_create_rejects_basis_only_without_mutation",
"test_basis_only_create_cannot_block_later_dated_import"
],
"red_observation": "basis-only account creation succeeded and could suppress a real dated import balance"
},
{
"id": "F-006",
"test": "test_f006_initial_replay_survives_multiple_later_corrections",
"red_observation": "replaying the initial key first conflicted after two corrections; after that path was enabled, unrelated live-authority drift after the correction chain was incorrectly accepted"
}
],
"red_runs": [
{
"argv": ["pytest", "-q", "tests/test_issue_12_review63_assets.py"],
"exit_code": 1,
"result": "5 failed before production changes",
"limitation": "isolated development environment used by the delegated TDD worker"
},
{
"argv": ["uv", "run", "pytest", "-q", "tests/test_issue_12_review63_accounts.py"],
"exit_code": 1,
"result": "2 failed before production changes"
},
{
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q", "tests/test_issue_12_review63_assets.py::test_f002_correction_replay_preserves_full_initial_response"],
"exit_code": 1,
"result": "correction replay omitted existing_snapshot_rows and differed from the original response"
},
{
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q", "tests/test_issue_12_review63_assets.py::test_f006_initial_replay_survives_multiple_later_corrections"],
"exit_code": 1,
"result": "initial replay after two later corrections returned idempotency_conflict"
},
{
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q", "tests/test_issue_12_review63_assets.py::test_f006_initial_replay_survives_multiple_later_corrections"],
"exit_code": 1,
"result": "unrelated live-authority drift after the correction chain returned success instead of idempotency_conflict"
}
]
}
@@ -1,72 +0,0 @@
{
"schema": "contract-preservation-inventory/v1",
"issue": 12,
"pull_request": 13,
"review_id": 64,
"reviewed_head": "c7a9cfa50d25ac68b6b722b415dc89bea8f2ae24",
"source_revision": "e41db959e4aa0093ab254ec4bb8aafd7dd57aafa",
"contracts": [
{
"contract": "current display cache and historical valuation authority remain separate and an evidence basis date requires a usable integer balance",
"status": "preserved",
"preserved_by": [
"test_empty_balance_with_basis_date_is_rejected_without_mutation_before_import",
"tests/test_import_current_balance_authority.py"
]
},
{
"contract": "actual zero remains valid while empty, whitespace, and non-integer balances fail without mutation",
"status": "preserved",
"preserved_by": [
"tests/test_issue_12_review63_accounts.py",
"tests/test_import_current_balance_authority.py"
]
},
{
"contract": "snapshot runs and corrections are append-only and idempotent replay returns the original immutable public response",
"status": "preserved",
"preserved_by": [
"test_replay_preserves_redacted_unverified_candidates_after_correction",
"test_replay_preserves_blocking_candidate_warning_shape",
"tests/test_issue_12_review63_assets.py"
]
},
{
"contract": "unverified candidate metadata remains redacted and candidate state hashes bind persisted evidence",
"status": "preserved",
"preserved_by": [
"test_replay_preserves_redacted_unverified_candidates_after_correction"
]
},
{
"contract": "malformed immutable evidence fails closed without report, replay, timeseries, or snapshot mutation ambiguity",
"status": "preserved",
"preserved_by": [
"test_malformed_persisted_evidence_fails_closed_without_snapshot_writes"
]
},
{
"contract": "canonical documentation describes actual account, currency, purpose, and basis-date selection together with existing physical index limitations",
"status": "preserved",
"preserved_by": [
"test_data_model_documents_valuation_selection_contract_and_index_mapping"
]
},
{
"contract": "all remaining Issue 12 acceptance behavior and prior Review 63 regressions remain intact",
"status": "preserved",
"preserved_by": [
"review64-focused-regressions",
"review64-full-suite",
"review64-ruff",
"review64-compileall",
"review64-diff-check"
]
}
],
"missing": 0,
"ambiguous": 0,
"limitations": [
"Four fixture-dependent PDF tests remain skipped because private KB Bank and Toss Securities fixtures are unavailable."
]
}
File diff suppressed because one or more lines are too long
@@ -1,64 +0,0 @@
{
"environment": ["os", "kernel", "architecture", "python", "uv"],
"command_env_allowlist": ["PATH", "HOME", "TMPDIR", "LANG", "VIRTUAL_ENV", "UV_CACHE_DIR"],
"secret_env_names": ["GITEA_TOKEN"],
"forbidden_regexes": [],
"hash_paths": [
"jarca_ledger_mcp/application/accounts_write_services.py",
"jarca_ledger_mcp/application/assets_service.py",
"tests/test_import_current_balance_authority.py",
"tests/test_asset_snapshot_idempotency.py",
"tests/test_valuation_contract_docs.py",
"tests/test_issue_12_review63_accounts.py",
"tests/test_issue_12_review63_assets.py",
"tests/test_issue_12_regressions.py",
"docs/data-model.md",
"docs/interface-contract.md",
"docs/requirements/req-003_account-valuation-history.md"
],
"commands": [
{
"id": "review64-focused-regressions",
"purpose": "Run Review 64 and preserved Review 63 regression coverage",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q", "tests/test_import_current_balance_authority.py", "tests/test_asset_snapshot_idempotency.py", "tests/test_valuation_contract_docs.py", "tests/test_issue_12_review63_accounts.py", "tests/test_issue_12_review63_assets.py", "tests/test_account_valuation_edge_contracts.py", "tests/test_issue_12_regressions.py"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review64-findings-green"]
},
{
"id": "review64-full-suite",
"purpose": "Run the canonical full repository suite at the functional source revision",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review64-full-suite"]
},
{
"id": "review64-ruff",
"purpose": "Run repository lint checks",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "ruff", "check", "."],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review64-ruff"]
},
{
"id": "review64-compileall",
"purpose": "Compile package, dashboard, tests, and scripts",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "python", "-m", "compileall", "-q", "jarca_ledger_mcp", "dashboard", "tests", "scripts"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review64-compileall"]
},
{
"id": "review64-diff-check",
"purpose": "Check the complete pull-request diff for whitespace errors",
"argv": ["git", "diff", "--check", "main...HEAD"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review64-diff-check"]
}
],
"limitations": [
"Four PDF fixture tests are skipped because private KB Bank and Toss Securities PDF fixtures are unavailable."
]
}
@@ -1,47 +0,0 @@
{
"schema": "review-finding-matrix/v1",
"issue": 12,
"pull_request": 13,
"review_id": 64,
"reviewed_head": "c7a9cfa50d25ac68b6b722b415dc89bea8f2ae24",
"source_revision": "e41db959e4aa0093ab254ec4bb8aafd7dd57aafa",
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"findings": [
{
"id": "C05-current-import",
"classification": "valid_fix",
"status": "resolved",
"production": "jarca_ledger_mcp/application/accounts_write_services.py",
"regression": "test_empty_balance_with_basis_date_is_rejected_without_mutation_before_import",
"claim": "an empty balance cannot acquire a basis date or block a later valid dated import; zero and valid integer balances remain distinct"
},
{
"id": "N02-close-replay-run-binding",
"classification": "valid_fix",
"status": "resolved",
"production": "jarca_ledger_mcp/application/assets_service.py",
"regressions": [
"test_replay_preserves_redacted_unverified_candidates_after_correction",
"test_replay_preserves_blocking_candidate_warning_shape"
],
"claim": "immutable close replay preserves redacted candidate metadata and exact blocking or nonblocking warning shape without additional writes"
},
{
"id": "D03-data-model",
"classification": "evidence_gap",
"status": "resolved",
"production": "docs/data-model.md",
"regression": "test_data_model_documents_valuation_selection_contract_and_index_mapping",
"claim": "the data model maps actual selection predicates to existing indexes and states their limitations without inventing new DDL"
},
{
"id": "IR-001-malformed-persisted-evidence",
"classification": "valid_fix",
"status": "resolved",
"production": "jarca_ledger_mcp/application/assets_service.py",
"regression": "test_malformed_persisted_evidence_fails_closed_without_snapshot_writes",
"claim": "malformed persisted evidence returns a controlled data_integrity_error from report, replay, and timeseries paths without snapshot writes"
}
],
"green_evidence": "evidence-manifest.json"
}
@@ -1,35 +0,0 @@
{
"schema": "review-red-evidence/v1",
"issue": 12,
"pull_request": 13,
"review_id": 64,
"reviewed_head": "c7a9cfa50d25ac68b6b722b415dc89bea8f2ae24",
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"reproductions": [
{
"id": "C05-current-import",
"command": "uv run pytest -q tests/test_import_current_balance_authority.py::test_empty_balance_with_basis_date_is_rejected_without_mutation_before_import",
"exit_code": 1,
"observed": "update_ledger_account accepted an empty balance paired with a basis date, persisted unusable cache authority, and could block a later valid dated import"
},
{
"id": "N02-close-replay-run-binding",
"command": "uv run python synthetic verified-plus-unverified close and replay probe",
"exit_code": 1,
"observed": "replay omitted included_items.unverified_candidates and the nonblocking warning marker, so replay differed from the first immutable response"
},
{
"id": "D03-data-model",
"command": "python -c tests.test_valuation_contract_docs.test_data_model_documents_valuation_selection_contract_and_index_mapping",
"exit_code": 1,
"observed": "the canonical data model did not document select_account_valuation predicates or existing index coverage and limitations"
},
{
"id": "IR-001-malformed-persisted-evidence",
"command": "uv run pytest -o addopts= -q tests/test_asset_snapshot_idempotency.py::test_malformed_persisted_evidence_fails_closed_without_snapshot_writes",
"exit_code": 1,
"observed": "malformed persisted excluded_evidence raised JSONDecodeError instead of a controlled fail-closed response"
}
],
"sensitive_data": "synthetic fixtures only; no credentials or real financial data"
}
@@ -1,58 +0,0 @@
{
"schema": "contract-preservation-inventory/v1",
"issue": 12,
"pull_request": 13,
"review_id": 65,
"reviewed_head": "fc2d2a6faeac3c64c0ed604f2aa1ad7c37c65570",
"source_revision": "8caf1f8379171a128a2c12971a2eadc9a324309a",
"contracts": [
{
"contract": "current cache balance is an exact integer or unknown NULL; zero remains a valid known balance",
"status": "preserved",
"preserved_by": [
"test_create_rejects_whitespace_or_non_integer_dated_balance_without_mutation",
"test_zero_dated_balance_and_valid_import_recovery"
]
},
{
"contract": "invalid dated account creation fails with validation_error before account or valuation mutation",
"status": "preserved",
"preserved_by": [
"test_create_rejects_whitespace_or_non_integer_dated_balance_without_mutation"
]
},
{
"contract": "a valid dated import can establish current cache and append a valuation after invalid create attempts are rejected",
"status": "preserved",
"preserved_by": [
"test_zero_dated_balance_and_valid_import_recovery",
"test_late_import_current_balance_is_authoritative_by_evidence_basis_date"
]
},
{
"contract": "unknown basis remains distinct from last_synced_at and historical imports cannot roll back a newer usable cache",
"status": "preserved",
"preserved_by": [
"test_account_write_sync_timestamp_does_not_infer_balance_basis_or_candidate",
"test_unknown_legacy_balance_basis_is_not_reported_as_valuation_date",
"review65-focused-regressions"
]
},
{
"contract": "all prior Issue 12 and Review 64 public envelopes, valuation selection, snapshot replay, read-only, migration, privacy, authentication, and menu semantics remain intact",
"status": "preserved",
"preserved_by": [
"review65-focused-regressions",
"review65-full-suite",
"review65-ruff",
"review65-compileall",
"review65-diff-check"
]
}
],
"missing": 0,
"ambiguous": 0,
"limitations": [
"Four fixture-dependent PDF tests remain skipped because private KB Bank and Toss Securities fixtures are unavailable."
]
}
File diff suppressed because one or more lines are too long
@@ -1,62 +0,0 @@
{
"environment": ["os", "kernel", "architecture", "python", "uv"],
"command_env_allowlist": ["PATH", "HOME", "TMPDIR", "LANG", "VIRTUAL_ENV", "UV_CACHE_DIR"],
"secret_env_names": ["GITEA_TOKEN"],
"forbidden_regexes": [],
"hash_paths": [
"jarca_ledger_mcp/application/accounts_write_services.py",
"tests/test_import_current_balance_authority.py",
"tests/test_account_basis_public_consumers.py",
"tests/test_account_valuation_edge_contracts.py",
"tests/test_account_valuations.py",
"tests/test_issue_12_regressions.py",
"tests/test_issue_12_review63_accounts.py",
"docs/data-model.md",
"docs/requirements/req-003_account-valuation-history.md"
],
"commands": [
{
"id": "review65-focused-regressions",
"purpose": "Run Review 65 and preserved account authority regression coverage",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q", "tests/test_import_current_balance_authority.py", "tests/test_account_basis_public_consumers.py", "tests/test_account_valuation_edge_contracts.py", "tests/test_account_valuations.py", "tests/test_issue_12_regressions.py", "tests/test_issue_12_review63_accounts.py"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review65-findings-green"]
},
{
"id": "review65-full-suite",
"purpose": "Run the canonical full repository suite at the functional source revision",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review65-full-suite"]
},
{
"id": "review65-ruff",
"purpose": "Run repository lint checks",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "ruff", "check", "."],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review65-ruff"]
},
{
"id": "review65-compileall",
"purpose": "Compile package, dashboard, tests, and scripts",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "python", "-m", "compileall", "-q", "jarca_ledger_mcp", "dashboard", "tests", "scripts"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review65-compileall"]
},
{
"id": "review65-diff-check",
"purpose": "Check the complete pull-request diff for whitespace errors",
"argv": ["git", "diff", "--check", "main...HEAD"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review65-diff-check"]
}
],
"limitations": [
"Four PDF fixture tests are skipped because private KB Bank and Toss Securities PDF fixtures are unavailable."
]
}
@@ -1,31 +0,0 @@
{
"schema": "review-finding-matrix/v1",
"issue": 12,
"pull_request": 13,
"review_id": 65,
"reviewed_head": "fc2d2a6faeac3c64c0ed604f2aa1ad7c37c65570",
"source_revision": "8caf1f8379171a128a2c12971a2eadc9a324309a",
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"findings": [
{
"id": "C05-current-import-create",
"classification": "valid_fix",
"status": "resolved",
"production": "jarca_ledger_mcp/application/accounts_write_services.py",
"regressions": [
"test_create_rejects_whitespace_or_non_integer_dated_balance_without_mutation",
"test_zero_dated_balance_and_valid_import_recovery"
],
"claim": "account creation rejects unusable dated balances before mutation while preserving zero and later valid dated import authority"
},
{
"id": "IR-001-strict-integer-create",
"classification": "valid_fix",
"status": "resolved",
"production": "jarca_ledger_mcp/application/accounts_write_services.py",
"regression": "test_create_rejects_whitespace_or_non_integer_dated_balance_without_mutation",
"claim": "boolean and floating-point balances are rejected instead of coercing or truncating; canonical signed integer text remains compatible"
}
],
"green_evidence": "evidence-manifest.json"
}
@@ -1,23 +0,0 @@
{
"schema": "review-red-evidence/v1",
"issue": 12,
"pull_request": 13,
"review_id": 65,
"reviewed_head": "fc2d2a6faeac3c64c0ed604f2aa1ad7c37c65570",
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"reproductions": [
{
"id": "C05-current-import-create",
"command": "uv run pytest -q tests/test_import_current_balance_authority.py::test_create_rejects_whitespace_or_non_integer_dated_balance_without_mutation tests/test_import_current_balance_authority.py::test_zero_dated_balance_and_valid_import_recovery",
"exit_code": 1,
"observed": "invalid whitespace or non-integer dated balance creation returned success, persisted unusable cache authority, omitted its valuation candidate, and exposed ValueError to a public liquidity consumer"
},
{
"id": "IR-001-strict-integer-create",
"command": "uv run pytest -o addopts= -q tests/test_import_current_balance_authority.py::test_create_rejects_whitespace_or_non_integer_dated_balance_without_mutation",
"exit_code": 1,
"observed": "boolean and floating-point balances were accepted through int coercion and persisted values different from caller input"
}
],
"sensitive_data": "synthetic fixtures only; no credentials or real financial data"
}
@@ -1,59 +0,0 @@
{
"schema": "contract-preservation-inventory/v1",
"issue": 12,
"pull_request": 13,
"review_id": 66,
"reviewed_head": "3bf9b77c6cc167a4e2a8706271bdf8e848d2c4fe",
"source_revision": "31d1c11dcf397ddf6a23e98d6d9062f96bab0194",
"contracts": [
{
"contract": "last_balance is an exact integer or NULL-compatible unknown value; boolean and floating-point coercion are invalid while zero and signed integer text remain valid",
"status": "preserved",
"preserved_by": [
"test_update_rejects_bool_or_float_dated_balance_without_mutation",
"test_update_rejects_empty_undated_balance_without_mutation",
"test_update_preserves_zero_and_signed_integer_balance_semantics"
]
},
{
"contract": "invalid account updates return validation_error before changing account cache or valuation history",
"status": "preserved",
"preserved_by": [
"test_update_rejects_bool_or_float_dated_balance_without_mutation",
"test_update_rejects_empty_undated_balance_without_mutation"
]
},
{
"contract": "a historical dated candidate is append-only and cannot roll back a newer current cache",
"status": "preserved",
"preserved_by": [
"test_historical_account_update_preserves_current_and_adds_one_candidate",
"test_late_import_current_balance_is_authoritative_by_evidence_basis_date"
]
},
{
"contract": "unknown basis remains distinct from last_synced_at and an undated valid balance write creates no dated candidate",
"status": "preserved",
"preserved_by": [
"test_account_write_sync_timestamp_does_not_infer_balance_basis_or_candidate",
"test_unknown_legacy_balance_basis_is_not_reported_as_valuation_date"
]
},
{
"contract": "Review 65 create validation, actual zero, valid import recovery, public consumer, valuation selection, snapshot replay, read-only, migration, privacy, authentication, and menu semantics remain intact",
"status": "preserved",
"preserved_by": [
"review66-focused-regressions",
"review66-full-suite",
"review66-ruff",
"review66-compileall",
"review66-diff-check"
]
}
],
"missing": 0,
"ambiguous": 0,
"limitations": [
"Four fixture-dependent PDF tests remain skipped because private KB Bank and Toss Securities fixtures are unavailable."
]
}
File diff suppressed because one or more lines are too long
@@ -1,64 +0,0 @@
{
"environment": ["os", "kernel", "architecture", "python", "uv"],
"command_env_allowlist": ["PATH", "HOME", "TMPDIR", "LANG", "VIRTUAL_ENV", "UV_CACHE_DIR"],
"secret_env_names": ["GITEA_TOKEN"],
"forbidden_regexes": [],
"hash_paths": [
"jarca_ledger_mcp/application/accounts_write_services.py",
"jarca_ledger_mcp/application/account_valuation_service.py",
"tests/test_import_current_balance_authority.py",
"tests/test_account_basis_public_consumers.py",
"tests/test_issue_12_review63_accounts.py",
"tests/test_account_valuation_edge_contracts.py",
"tests/test_account_valuations.py",
"tests/test_issue_12_regressions.py",
"docs/data-model.md",
"docs/interface-contract.md",
"docs/requirements/req-003_account-valuation-history.md"
],
"commands": [
{
"id": "review66-focused-regressions",
"purpose": "Run Review 66 and preserved account authority regressions",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q", "tests/test_import_current_balance_authority.py", "tests/test_account_basis_public_consumers.py", "tests/test_issue_12_review63_accounts.py", "tests/test_account_valuation_edge_contracts.py", "tests/test_account_valuations.py", "tests/test_issue_12_regressions.py"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review66-findings-green"]
},
{
"id": "review66-full-suite",
"purpose": "Run the canonical full repository suite at the functional source revision",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "pytest", "-o", "addopts=", "-q"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review66-full-suite"]
},
{
"id": "review66-ruff",
"purpose": "Run repository lint checks",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "ruff", "check", "."],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review66-ruff"]
},
{
"id": "review66-compileall",
"purpose": "Compile package, dashboard, tests, and scripts",
"argv": ["uv", "run", "--frozen", "--extra", "dev", "--extra", "dashboard", "python", "-m", "compileall", "-q", "jarca_ledger_mcp", "dashboard", "tests", "scripts"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review66-compileall"]
},
{
"id": "review66-diff-check",
"purpose": "Check the complete pull-request diff for whitespace errors",
"argv": ["git", "diff", "--check", "main...HEAD"],
"expected_exit_code": 0,
"max_output_bytes": 65536,
"claims": ["review66-diff-check"]
}
],
"limitations": [
"Four PDF fixture tests are skipped because private KB Bank and Toss Securities PDF fixtures are unavailable."
]
}
@@ -1,24 +0,0 @@
{
"schema": "review-finding-matrix/v1",
"issue": 12,
"pull_request": 13,
"review_id": 66,
"reviewed_head": "3bf9b77c6cc167a4e2a8706271bdf8e848d2c4fe",
"source_revision": "31d1c11dcf397ddf6a23e98d6d9062f96bab0194",
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"findings": [
{
"id": "C05-account-update-balance-authority",
"classification": "valid_fix",
"status": "resolved",
"production": "jarca_ledger_mcp/application/accounts_write_services.py",
"regressions": [
"test_update_rejects_bool_or_float_dated_balance_without_mutation",
"test_update_rejects_empty_undated_balance_without_mutation",
"test_update_preserves_zero_and_signed_integer_balance_semantics"
],
"claim": "account updates reject boolean, floating-point, empty, whitespace, and non-integer balance values before account or valuation mutation while preserving zero, signed integer text, dated candidates, historical cache authority, and unknown-basis semantics"
}
],
"green_evidence": "evidence-manifest.json"
}
@@ -1,23 +0,0 @@
{
"schema": "review-red-evidence/v1",
"issue": 12,
"pull_request": 13,
"review_id": 66,
"reviewed_head": "3bf9b77c6cc167a4e2a8706271bdf8e848d2c4fe",
"contract_sha256": "6d86b591b022fcc722611bfec6d24e54a64216ab6f7c4098f357e25420218818",
"reproductions": [
{
"id": "C05-account-update-balance-authority",
"command": "uv run --frozen --extra dev --extra dashboard pytest -o addopts= -q tests/test_import_current_balance_authority.py::test_update_rejects_bool_or_float_dated_balance_without_mutation tests/test_import_current_balance_authority.py::test_update_rejects_empty_undated_balance_without_mutation",
"exit_code": 1,
"observed": "2 failed: dated bool and float values returned success after coercion and appended candidates; an undated empty string returned success and replaced the integer cache"
},
{
"id": "C05-exact-head-synthetic-probe",
"command": "uv run --frozen --extra dev --extra dashboard python <synthetic temporary-DB update probe>",
"exit_code": 0,
"observed": "True, False, 1.2, and -1.2 were persisted as 1, 0, 1, and -1 with dated candidates; an undated empty string was persisted and cleared the basis; all invalid requests mutated state"
}
],
"sensitive_data": "synthetic temporary databases only; no credentials or real financial data"
}
+32 -3
View File
@@ -328,6 +328,32 @@ def _snapshot_rows_for_latest_run(conn: sqlite3.Connection, basis_date: str) ->
rows = conn.execute("SELECT rowid AS _rowid, * FROM asset_snapshots WHERE basis_date=? ORDER BY created_at ASC, name ASC", (basis_date,)).fetchall()
run = conn.execute("SELECT * FROM asset_snapshot_runs WHERE basis_date=? ORDER BY rowid DESC LIMIT 1", (basis_date,)).fetchone()
if not run:
legacy_run_rows = [row for row in rows if row["closed_at"] and row["snapshot_run_id"]]
if legacy_run_rows:
# Schema 11 persisted run identity only on the snapshot rows. Keep
# its rowid-based latest-run rule rather than falling back to live
# authority merely because schema 12 has no matching run record.
latest = max(
legacy_run_rows,
key=lambda row: (
int(row["_rowid"]),
row["closed_at"] or row["created_at"] or "",
row["snapshot_run_id"],
),
)
latest_run_id = latest["snapshot_run_id"]
latest_rows = [row for row in legacy_run_rows if row["snapshot_run_id"] == latest_run_id]
return latest_rows, {
"snapshot_run_id": latest_run_id,
"legacy_closed": True,
"is_correction": bool(latest["is_correction"]),
"correction_of_run_id": latest["correction_of_run_id"] or "",
"correction_reason": latest["correction_reason"] or "",
"close_status": "legacy_read_only",
"selection_policy": latest["selection_policy"] or "exact",
"closed_at": latest["closed_at"] or latest["created_at"] or "",
"total_snapshot_rows_for_basis_date": len(rows),
}
return rows, {"snapshot_run_id": "", "is_correction": False, "correction_of_run_id": "", "correction_reason": "", "total_snapshot_rows_for_basis_date": len(rows)} if rows else {}
return _snapshot_rows_for_run(conn, run["id"])
@@ -403,7 +429,7 @@ def _closed_run_metadata(metadata: dict[str, Any]) -> dict[str, Any]:
"snapshot_run_id": metadata.get("snapshot_run_id", ""),
"close_status": metadata.get("close_status", ""),
"selection_policy": metadata.get("selection_policy", ""),
"approval": {k: metadata.get(k, "") for k in ("approved_by", "approved_at", "approval_reason", "approval_reference")},
"approval": {} if metadata.get("legacy_closed") else {k: metadata.get(k, "") for k in ("approved_by", "approved_at", "approval_reason", "approval_reference")},
"correction": {k: metadata.get(k, "") for k in ("is_correction", "correction_of_run_id", "correction_reason")},
}
@@ -949,16 +975,19 @@ def get_asset_snapshot_report(basis_date: str = "", db_path: str | None = None,
if close_metadata.get("evidence_integrity_error"):
return fail("data_integrity_error", "snapshot evidence is malformed", snapshot_run_id=close_metadata.get("snapshot_run_id", ""))
has_close_run = bool(close_metadata.get("snapshot_run_id"))
legacy_closed = bool(close_metadata.get("legacy_closed"))
snapshots = _snapshot_items(conn, basis) if rows else []
legacy_snapshots = snapshots if rows and not has_close_run else []
if has_close_run:
if has_close_run or legacy_closed:
run_public = _closed_run_metadata(close_metadata)
run_public["warnings"] = _closed_warnings(close_metadata, basis, rows)
if legacy_closed:
run_public["approval"] = {}
summary = _summarize_valuation_items(snapshots, run_public["excluded_items"])
evidence_provenance = _evidence_provenance(close_metadata.get("applied_evidence", []), basis)
return _ok_with_provenance_aliases(
basis_date=basis,
source="asset_snapshots",
source="legacy_asset_snapshots" if legacy_closed else "asset_snapshots",
is_closed_snapshot=True,
snapshot_run_id=close_metadata.get("snapshot_run_id", ""),
snapshot_count=len(snapshots),
+146
View File
@@ -0,0 +1,146 @@
from __future__ import annotations
import hashlib
import sqlite3
from pathlib import Path
from jarca_ledger_mcp import service
from jarca_ledger_mcp.db import init_db
BASIS = "2026-06-30"
def _legacy_closed_db(tmp_path):
path = str(tmp_path / "legacy.sqlite3")
init_db(path)
asset = service.create_manual_asset("legacy cash", 100, "2026-06-01", "cash", db_path=path)["manual_asset"]
with sqlite3.connect(path) as conn:
# These are schema-11 close rows after the additive schema-12 migration:
# run identity is absent, but row close/correction fields remain frozen.
conn.execute(
"""INSERT INTO asset_snapshots
(id, basis_date, source_type, source_id, name, amount, liquidity_bucket,
valuation_source, snapshot_run_id, correction_of_run_id, correction_reason,
is_correction, closed_at, created_at)
VALUES (?, ?, 'manual_asset', ?, ?, 100, 'cash', 'manual_assets', 'legacy-run-correction',
'legacy-initial', 'legacy correction', 1, '2026-06-30T12:00:00+09:00',
'2026-06-30T12:00:00+09:00')""",
("legacy-row", BASIS, asset["id"], asset["name"]),
)
conn.execute("DROP TABLE asset_snapshot_runs")
conn.execute("UPDATE schema_meta SET value='11' WHERE key='schema_version'")
init_db(path)
with sqlite3.connect(path) as conn:
assert conn.execute("SELECT COUNT(*) FROM asset_snapshot_runs").fetchone()[0] == 0
return path, asset["id"]
def test_schema11_closed_rows_remain_frozen_after_live_value_and_date_change(tmp_path):
path, asset_id = _legacy_closed_db(tmp_path)
service.update_manual_asset(asset_id, valuation_amount=900, valuation_date="2026-06-30", db_path=path)
before = hashlib.sha256(Path(path).read_bytes()).hexdigest()
report = service.get_asset_snapshot_report(BASIS, db_path=path)
after = hashlib.sha256(Path(path).read_bytes()).hexdigest()
assert report["success"] is True
assert report["source"] == "legacy_asset_snapshots"
assert report["is_closed_snapshot"] is True
assert report["close_status"] == "legacy_read_only"
assert report["net_worth"] == 100
assert report["included_items"][0]["amount"] == 100
assert report["correction"] == {
"is_correction": True,
"correction_of_run_id": "legacy-initial",
"correction_reason": "legacy correction",
}
assert report["snapshot_run_id"] == "legacy-run-correction"
assert report["approval"] == {}
assert "valuation_id" not in report["included_items"][0]
assert after == before
def test_schema11_closed_rows_are_frozen_in_timeseries(tmp_path):
path, asset_id = _legacy_closed_db(tmp_path)
service.update_manual_asset(asset_id, valuation_amount=900, valuation_date="2026-07-01", db_path=path)
point = service.get_net_worth_timeseries(BASIS, BASIS, db_path=path)["points"][0]
assert point["net_worth"] == 100
assert point["item_count"] == 1
assert point["close_status"] == "legacy_read_only"
assert point["snapshot_run_id"] == "legacy-run-correction"
assert point["approval"] == {}
assert point["correction"] == {
"is_correction": True,
"correction_of_run_id": "legacy-initial",
"correction_reason": "legacy correction",
}
def test_schema11_closed_rows_are_frozen_in_monthly_settlement(tmp_path):
path, asset_id = _legacy_closed_db(tmp_path)
service.update_manual_asset(asset_id, valuation_amount=900, valuation_date="2026-07-01", db_path=path)
monthly = service.get_monthly_settlement_summary("2026-06", db_path=path)
assets = monthly["reports"]["assets"]
coverage = monthly["source_coverage"]["assets"]
assert assets["net_worth"] == 100
assert assets["is_closed_snapshot"] is True
assert coverage["net_worth"] == 100
assert coverage["is_closed_snapshot"] is True
assert coverage["monthly_source_snapshot_id"] == "legacy-run-correction"
assert assets["approval"] == {}
assert coverage["approval"] == {}
assert coverage["correction"] == assets["correction"]
def test_schema11_latest_legacy_correction_run_is_selected_as_a_complete_group(tmp_path):
path = str(tmp_path / "legacy-corrections.sqlite3")
init_db(path)
with sqlite3.connect(path) as conn:
rows = [
("old-a", "legacy-run-1", "", "", 0, "2026-06-30T10:00:00+09:00", 40),
("old-b", "legacy-run-1", "", "", 0, "2026-06-30T10:00:00+09:00", 60),
("new-a", "legacy-run-2", "legacy-run-1", "correct frozen close", 1, "2026-07-01T10:00:00+09:00", 70),
("new-b", "legacy-run-2", "legacy-run-1", "correct frozen close", 1, "2026-07-01T10:00:00+09:00", 80),
]
for row_id, run_id, prior, reason, correction, closed_at, amount in rows:
conn.execute(
"""INSERT INTO asset_snapshots
(id, basis_date, source_type, source_id, name, amount, currency,
liquidity_bucket, valuation_source, snapshot_run_id,
correction_of_run_id, correction_reason, is_correction,
closed_at, created_at)
VALUES (?, ?, 'manual_asset', ?, ?, ?, 'KRW', 'cash',
'manual_assets', ?, ?, ?, ?, ?, ?)""",
(row_id, BASIS, row_id, row_id, amount, run_id, prior, reason,
correction, closed_at, closed_at),
)
conn.execute("DROP TABLE asset_snapshot_runs")
conn.execute("UPDATE schema_meta SET value='11' WHERE key='schema_version'")
init_db(path)
with sqlite3.connect(path) as conn:
assert conn.execute("SELECT COUNT(*) FROM asset_snapshot_runs").fetchone()[0] == 0
before = hashlib.sha256(Path(path).read_bytes()).hexdigest()
report = service.get_asset_snapshot_report(BASIS, db_path=path)
after = hashlib.sha256(Path(path).read_bytes()).hexdigest()
assert report["success"] is True
assert report["source"] == "legacy_asset_snapshots"
assert report["snapshot_run_id"] == "legacy-run-2"
assert report["snapshot_count"] == 2
assert [item["id"] for item in report["included_items"]] == ["new-a", "new-b"]
assert report["net_worth"] == 150
assert report["correction"] == {
"is_correction": True,
"correction_of_run_id": "legacy-run-1",
"correction_reason": "correct frozen close",
}
assert report["approval"] == {}
assert all("valuation_id" not in item for item in report["included_items"])
assert after == before