Fix loan account conversion invariant
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"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_head": "5e721b0ddd648f3e80f341a3c304d1cc3ca47ee8",
|
||||
"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."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"schema": "gitea-agent.evidence-plan/v1",
|
||||
"owner": "jsfamily",
|
||||
"repository": "jarca-ledger-mcp",
|
||||
"issue": 12,
|
||||
"pull_request": 13,
|
||||
"review_id": 61,
|
||||
"reviewed_head": "5e721b0ddd648f3e80f341a3c304d1cc3ca47ee8",
|
||||
"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..<SOURCE_SHA>"]
|
||||
}
|
||||
],
|
||||
"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."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"schema": "gitea-agent.finding-matrix/v1",
|
||||
"owner": "jsfamily",
|
||||
"repository": "jarca-ledger-mcp",
|
||||
"issue": 12,
|
||||
"pull_request": 13,
|
||||
"review_id": 61,
|
||||
"reviewed_head": "5e721b0ddd648f3e80f341a3c304d1cc3ca47ee8",
|
||||
"implementation_parent": "b8b8811a2a50371795a02e4fa65f097c9d9508ac",
|
||||
"findings": [
|
||||
{
|
||||
"id": "N01-loan-balance-definition",
|
||||
"classification": "implementation_defect",
|
||||
"status": "resolved_at_implementation_parent",
|
||||
"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."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
Reference in New Issue
Block a user