# Handover note: household finance ledger platform ## 1. Goal of the project / conversation Build a household finance platform that combines transaction import, manual entry, document/email capture, project budgeting, promised-payment tracking, recurring-expense tracking, categorization, budgeting, debt/savings planning, and explainable settlement between household members. The platform should handle a real-world household setup with: ```text - shared household account(s) - personal bank accounts - extra fourth bank account - personal credit card - PayPal accounts - Klarna Card / Pay Later style obligations - recurring bills and memberships - project-based budgets, e.g. holidays, renovation, moving ``` The user wants this to become useful as: ```text - an API/backend - a web frontend - a mobile app - local-first or privacy-preserving connectors where possible ``` A key privacy goal is that **bank connections should ideally happen locally**, so the central server does not need to store plaintext or unencrypted banking credentials, tokens, or sensitive banking connection material. The conceptual core is not “transaction import.” The core is a **household financial ledger** that distinguishes: ```text cash movement purchase / consumption event liability promised future payment recurring commitment refund reimbursement settlement between household members project budget item source evidence ``` ## 2. Current state No code, files, repository, database schema, or artifacts have been created yet in this conversation. The project is at the **architecture and product-definition stage**. The following concepts have been agreed as important: ```text - manual entry must be first-class - imports/connectors are useful but incomplete - duplicate detection is central - IMAP ingestion is useful for PayPal, Klarna, invoices, receipts, subscriptions - receipt/photo scanning is useful but should be reviewed by the user - AI/categorization/OCR should run locally - projects should be first-class, not just tags - project budgets must track budgeted, committed, paid, reconciled, settled, and still-due amounts separately - settlement must be explainable - private and shared visibility must be handled carefully ``` Known external access situation from earlier discussion: ```text German bank accounts: FinTS/HBCI and/or PSD2 aggregators are plausible. GLS/comdirect: FinTS likely relevant. comdirect: also has its own API, but it is not bank-independent. Klarna Bank Account / Guthaben: likely accessible through PSD2/AIS route. Klarna Card as credit/BNPL card: no known public consumer transaction API. Treat as weak/incomplete data source. Fallbacks: IMAP, statements/PDFs, GDPR export, manual entry, bank-side repayment reconciliation. PayPal: possible routes include PayPal API, CSV export, and IMAP/email parsing. Third-party/multi-user API access may require partner status. ``` ## 3. Important decisions already made ### Product decisions The platform should be modeled as an **evidence-to-ledger system**, not as direct transaction import. Recommended flow: ```text raw source observation -> extraction/parsing -> candidate event -> duplicate/link/reconciliation engine -> accepted household ledger event -> categorization -> project assignment -> split/settlement logic -> budget and cash-flow views ``` Every imported or manually entered item should first become a **raw observation** or **candidate**, not an immediately final expense. ### Accounting decisions Do not collapse these concepts: ```text expense payment cash settlement promised future payment liability recurring commitment refund reimbursement transfer project budget item ``` A bank debit from PayPal 30 days after a purchase is usually **not a new expense**. It may settle a previously recorded PayPal/Klarna obligation. A credit card bill payment is usually **not a new expense**. It reduces card liability. A reimbursement between household members is usually **not income**. It settles an interpersonal balance. ### Privacy/security decisions Bank connections should ideally happen locally. The server should avoid storing: ```text - plaintext bank login credentials - unencrypted bank tokens - TAN/SCA secrets - unnecessary raw banking payloads outside encrypted storage ``` Potential model: ```text local connector agent -> connects to FinTS/PSD2/file imports locally -> normalizes/encrypts data -> sends sanitized observations/events to backend ``` The backend can still store encrypted raw observations if needed, but the design should support local-only sensitive connectors. ### Import decisions Manual entry, CSV import, IMAP, PDF import, receipt photo OCR, and API connectors should all feed the same raw observation pipeline. CSV and manual import are not fallback-only features; they are primary ingestion methods. ### Project budgeting decisions Projects are first-class entities. A project must support: ```text budget lines planned items actual events partial payments future due payments recurring project-related payments participants split rules settlement evidence open actions scenario estimates ``` Projects should work for: ```text holidays renovations moving large purchases weddings/events debt repayment savings goals child/pet/medical/education expenses ``` ## 4. Files, code, or artifacts involved None yet. There is no repository, package structure, codebase, database migration, OpenAPI spec, UI design file, or generated artifact from this conversation. The new coding agent should start by creating the initial repository structure. ## 5. Relevant architecture / data model / APIs / commands ### Recommended high-level architecture Suggested initial architecture: ```text monorepo/ apps/ web/ # web frontend mobile/ # app later; can be deferred api/ # backend API local-agent/ # local bank/import connector agent packages/ domain/ # shared domain types, validation schemas importers/ # CSV, IMAP, PDF/OCR parsers matching/ # deduplication and reconciliation logic categorization/ # local rules/classifier interfaces ui/ # shared UI components, optional infra/ docker/ migrations/ ``` No specific stack has been chosen yet. For fast development, a reasonable starting assumption would be: ```text Language: TypeScript for API/web/shared domain. Backend: Node.js with Fastify, NestJS, or similar. Frontend: React / Next.js. Database: PostgreSQL for server mode. SQLite for local-agent/local-first mode or test fixtures. Mobile: Defer initially; later React Native, Expo, Flutter, or native app. Local agent: TypeScript or Python. Python may be better for FinTS, OCR, PDF parsing, IMAP prototypes. TypeScript may be better for shared validation and app packaging. AI/OCR: local only. No cloud AI dependency. ``` These are assumptions, not confirmed choices. ### Core domain entities Use explicit domain entities. Do not store everything as one flat transaction table. #### `User` ```text id display_name email household_id created_at ``` #### `Household` ```text id name default_currency created_at ``` #### `Account` Represents checking accounts, shared accounts, PayPal, credit cards, Klarna, cash wallets, or virtual liability accounts. ```text id household_id owner_user_id nullable name account_type # checking, shared_checking, paypal, credit_card, klarna, cash, liability, savings institution_name currency visibility_mode # private, shared_summary, shared_full, settlement_only is_active created_at ``` #### `RawObservation` Every imported file row, email, PDF, receipt photo, API transaction, notification, or manual draft. ```text id household_id source_type # bank_csv, fints, paypal_csv, paypal_email, klarna_email, receipt_photo, pdf_invoice, manual, api source_account_id nullable owner_user_id nullable observed_at nullable received_at raw_hash raw_text nullable raw_file_path nullable raw_payload_json nullable parsed_json nullable parser_name nullable parser_version nullable confidence status # new, parsed, candidate_created, ignored, duplicate_source, error created_at ``` #### `FinancialEvent` The accepted or candidate economic event. ```text id household_id event_type # expense, income, transfer, liability_payment, promised_payment, refund, reimbursement, fee, interest, adjustment status # candidate, confirmed, pending, booked, settled, cancelled, ignored event_date booking_date nullable due_date nullable amount currency merchant nullable counterparty nullable description category_id nullable project_id nullable created_by_user_id nullable created_at updated_at ``` Important: `FinancialEvent` is not necessarily a bank transaction. #### `EventObservationLink` Links evidence to event. ```text id event_id observation_id relation # evidence_for, duplicate_source, imported_from, parser_source, statement_source confidence created_at ``` #### `EventRelation` Links events to each other. ```text id from_event_id to_event_id relation_type # duplicate_of, same_economic_event_as, settles, funds, reimburses, refunds, reverses, replaces_pending_authorization, part_of_statement confidence created_by # system, user created_at ``` #### `SplitLine` Represents economic responsibility. ```text id event_id user_id share_amount share_percent nullable reason # equal_split, custom, private, income_ratio, project_default, manual created_at ``` #### `Category` Use hierarchical categories. ```text id household_id parent_id nullable name path # e.g. "holiday > restaurant" is_active ``` #### `Tag` Use tags for flexible labels. Do not use tags as project replacement. ```text id household_id name ``` #### `Project` First-class project/budget object. ```text id household_id name # e.g. "Stockholm 2026" description start_date nullable end_date nullable default_split_rule_id nullable status # idea, planning, active, completed, cancelled, archived currency created_at updated_at ``` #### `ProjectParticipant` ```text id project_id user_id role default_share_percent nullable ``` #### `ProjectBudgetLine` Budget category within project. ```text id project_id category_id nullable name # Transport, Accommodation, Food, Souvenirs, Buffer budgeted_amount committed_amount_cached nullable paid_amount_cached nullable sort_order notes ``` #### `ProjectPlannedItem` Planned or committed project item. ```text id project_id budget_line_id nullable description estimated_amount committed_amount nullable expected_date nullable due_date nullable expected_payer_account_id nullable split_rule_id nullable planning_status # idea, estimated, committed, cancelled payment_status # unpaid, partially_paid, paid, refunded, partially_refunded reconciliation_status # no_evidence, email_seen, receipt_seen, bank_matched, statement_matched, manually_confirmed settlement_status # not_shared, unsettled, partially_settled, settled, ignored notes created_at updated_at ``` #### `PaymentLine` Partial payments for project items or events. ```text id planned_item_id nullable event_id nullable amount currency date paid_by_user_id nullable payment_account_id nullable source_observation_id nullable status # expected, scheduled, paid, reconciled, failed, cancelled created_at ``` #### `RecurringCommitment` For insurance, memberships, subscriptions, rent, direct debits, standing orders. ```text id household_id provider description amount_expected amount_min nullable amount_max nullable currency interval # monthly, yearly, quarterly, weekly, custom next_due_date payer_account_id nullable split_rule_id nullable category_id nullable project_id nullable notice_period nullable renewal_date nullable status # active, paused, cancelled, unknown matching_rule_json nullable created_at updated_at ``` #### `PromisedPayment` May be separate from `FinancialEvent`, or implemented as event type `promised_payment`. Prefer separate table only if extra lifecycle detail is needed. ```text id household_id related_event_id nullable provider # Klarna, PayPal, credit_card, insurance, manual amount currency created_date due_date expected_funding_account_id nullable responsible_user_id nullable status # expected, scheduled, debited, reconciled, failed, cancelled source_observation_id nullable created_at updated_at ``` #### `ImportConnection` For connectors and local agent configuration metadata. Do not store plaintext secrets. ```text id household_id owner_user_id connection_type # fints, psd2_aggregator, imap, paypal_api, csv_folder, local_folder display_name status # active, needs_auth, error, disabled last_sync_at nullable last_error nullable secret_storage_ref nullable runs_locally boolean created_at updated_at ``` ### Key services/classes/components discussed #### Backend/domain services ```text ObservationIngestionService accepts raw inputs and stores RawObservation ParserRegistry selects parser by source_type, sender, file type, MIME type, CSV header, etc. CandidateEventService turns parsed observations into candidate FinancialEvents DeduplicationService exact hash/ID-based duplicate detection SemanticMatchingService fuzzy matching across observations/events ReconciliationService links PayPal/Klarna/bank/card lifecycle events LedgerService confirms events, updates split lines, creates relations SettlementService calculates who owes whom globally or per project BudgetService calculates category and project budget states ProjectBudgetService calculates budgeted/committed/paid/due/settled/reconciled values RecurringCommitmentService tracks recurring expenses and expected future payments PromisedPaymentService tracks PayPal/Klarna/credit card/direct debit future cash movements CategorizationService rules first, local ML later, local LLM suggestions only if available PrivacyVisibilityService filters events by account visibility and user permissions AuditLogService records merges, edits, category changes, settlement changes ``` #### Importer/connectors ```text ManualEntryImporter CsvImporter BankCsvImporter PayPalCsvImporter ImapImporter KlarnaEmailParser PayPalEmailParser PdfInvoiceImporter ReceiptPhotoImporter FintsConnector Psd2AggregatorConnector LocalAgentSyncClient ``` #### Matching functions ```text computeRawObservationHash(input): string computeNormalizedTransactionFingerprint(parsed): string findExactDuplicates(observation): DuplicateCandidate[] findSemanticMatches(candidateEvent): MatchCandidate[] scoreEventMatch(a, b): number linkEvents(from, to, relationType, confidence): EventRelation markDuplicate(source, duplicateOf): void reconcilePromisedPaymentWithBankDebit(promisedPayment, bankEvent): ReconciliationResult reconcilePayPalPurchaseWithFundingDebit(paypalEvent, bankEvent): ReconciliationResult reconcileCreditCardPurchaseWithStatementPayment(cardEvent, bankEvent): ReconciliationResult ``` #### Project functions ```text createProject(input): Project createProjectFromTemplate(templateName, participants): Project addProjectBudgetLine(projectId, input): ProjectBudgetLine addProjectPlannedItem(projectId, input): ProjectPlannedItem linkEventToProject(eventId, projectId, budgetLineId?): void addPaymentLine(input): PaymentLine calculateProjectBudget(projectId): ProjectBudgetSummary calculateProjectSettlement(projectId): SettlementSummary calculateProjectCashForecast(projectId): CashForecast calculateProjectOpenActions(projectId): ActionItem[] ``` #### UI components ```text DashboardPage ImportInboxPage RawObservationReviewQueue CandidateEventReviewCard DuplicateMatchReviewCard HouseholdLedgerPage Transaction/EventDetailPage ProjectListPage ProjectDetailPage ProjectBudgetTable ProjectTimeline ProjectSettlementPanel PromisedPaymentsCalendar RecurringCommitmentsPage SettlementOverview ManualEntryForm ReceiptUploadForm ImapConnectionSettings LocalAgentStatusPanel AccountVisibilitySettings ``` ### Duplicate/linking relationships Use relationship types instead of one boolean duplicate flag: ```text duplicate_of same_economic_event_as settles funds reimburses refunds reverses part_of_statement replaces_pending_authorization ``` ### Matching strategy Use layered matching: ```text Layer A: exact technical deduplication - source transaction ID - message ID - file hash - attachment hash - raw payload hash - CSV import fingerprint Layer B: semantic matching - amount - currency - date window - merchant/counterparty similarity - order ID - invoice number - PayPal transaction ID - Klarna reference - IBAN - card last four digits - receipt total - due date - email sender domain Layer C: confidence thresholds >= 0.98: auto-link if safe 0.75 - 0.98: review queue < 0.75: keep separate ``` Conservative rule: a bad merge is worse than a missed merge. ### Source authority hierarchy Use source confidence. ```text High authority: booked bank/card/PayPal transaction official statement imported CSV from provider Medium authority: invoice PDF Klarna/PayPal email merchant receipt recurring contract register Low authority: push notification OCR line item manual draft browser capture ``` ### Project budget calculations Each project should calculate: ```text budgeted_total committed_total paid_total reconciled_total settled_total due_later_total remaining_uncommitted_budget current_settlement_balance forecast_settlement_balance open_actions ``` Example target output: ```text Project: Stockholm 2026 Budgeted total: 2,350 € Committed so far: 1,455 € Already paid: 775 € Still due: 680 € Remaining uncommitted: 895 € Current settlement: B owes A 187.50 € Forecast settlement: B will owe A 527.50 € if A pays all remaining committed items. Open items: hotel balance due 2026-08-12 accommodation receipt missing 3 restaurant receipts uncategorized local transport not yet budgeted ``` ### Local AI/OCR assumptions Everything AI-related should run locally. Suggested local stack: ```text OCR: Tesseract and/or PaddleOCR PDF extraction: text extraction first OCR only for scanned PDFs Invoice extraction: template/rule-based parser first invoice2data-style approach Categorization: deterministic rules first local classifier second local embeddings for similarity third local LLM only as assistant/suggester Local LLM: optional use only for structured suggestions must not directly mutate confirmed ledger state ``` Categorization order: ```text 1. deterministic user rules 2. previous merchant/account/category memory 3. local supervised classifier 4. local embeddings/similarity 5. local LLM suggestion 6. user review ``` ## 6. Bugs, open questions, and TODOs ### Bugs No code exists yet, so there are no known software bugs. ### Unresolved compile/runtime issues None yet. No build system, package manager, database, migrations, Docker setup, test runner, or runtime has been created. ### Open product questions These should be resolved soon, but do not block initial domain modeling: ```text - Exact tech stack: TypeScript/Node vs Python backend vs mixed. - Whether the local connector agent is mandatory in v1 or introduced after local imports. - Whether mobile app starts as PWA or native app. - Whether backend is single-household self-hosted first or multi-tenant from day one. - Whether data is encrypted field-by-field, database-level, or local-only for sensitive payloads. - Whether raw observations are stored centrally, locally, or optionally both. - Whether FinTS connector is Python local-agent only or backend-capable. - Whether IMAP credentials are stored in local agent or backend encrypted vault. - Which user authentication system to use. - Whether project budgeting should support multiple currencies in v1. ``` ### Important technical TODOs ```text - Choose initial stack. - Create repository scaffold. - Define domain schema and migrations. - Implement RawObservation ingestion first. - Implement manual entry first. - Implement candidate event review. - Implement exact deduplication. - Implement event relations. - Implement split lines and settlement calculation. - Implement project model and project budget summary. - Add CSV import. - Add IMAP import. - Add PDF/receipt import after core ledger works. - Add local categorization rules. - Add local-agent architecture before real bank connectors. ``` ## 7. Constraints, preferences, naming conventions, style rules ### Constraints ```text - AI must be local-only. - Categorization must be local-only. - OCR should be local-only. - Bank connection should ideally happen locally. - Server should not store plaintext banking credentials. - Klarna Card cannot be assumed to have API access. - CSV/manual/PDF/email imports must be treated as first-class sources. - Duplicate detection and reconciliation are central. - Privacy modes are required for multi-user support. ``` ### Product preferences ```text - Explainability over opaque automation. - Conservative deduplication. - User review for uncertain matches. - Raw evidence retained. - Manual corrections should improve future suggestions. - Project budgets should be explicit and useful for planning. - Settlement calculations should show reasoning. ``` ### Naming conventions Suggested names: ```text RawObservation: untrusted/raw source item. FinancialEvent: economic event or candidate event in ledger. EventRelation: semantic relationship between events. EventObservationLink: evidence relationship between observation and event. PromisedPayment: known or inferred future cash movement. RecurringCommitment: subscription/contract/standing expected payment. Project: first-class budgeting/planning object. ProjectBudgetLine: high-level budget bucket. ProjectPlannedItem: planned/committed item inside project. PaymentLine: partial payment attached to item/event. SplitLine: economic responsibility allocation. SettlementSummary: who owes whom and why. ``` Do not name everything `Transaction`. Reserve `Transaction` only for provider/bank-specific imported rows if needed, or avoid the term entirely in domain code. Better terms: ```text BankTransactionObservation PayPalTransactionObservation FinancialEvent LedgerEntry PaymentLine ``` ### UI style preference No detailed UI style was decided. Functional preference: ```text - review queues - explainable cards - budget tables - project timelines - settlement panels - confidence indicators - evidence links ``` ## 8. Exact next steps ### Step 1: Create the initial repo Create a minimal monorepo. Recommended initial structure: ```text household-finance/ apps/ api/ web/ packages/ domain/ matching/ db/ migrations/ docs/ handover.md ``` Defer mobile and local-agent folders until the first backend/web skeleton exists, unless the chosen stack strongly favors creating them immediately. ### Step 2: Add the handover note Save this handover as: ```text docs/handover.md ``` ### Step 3: Define the domain package Create shared schemas/types for: ```text Household User Account RawObservation FinancialEvent EventObservationLink EventRelation SplitLine Category Tag Project ProjectParticipant ProjectBudgetLine ProjectPlannedItem PaymentLine RecurringCommitment PromisedPayment ImportConnection ``` Use validation schemas if using TypeScript, e.g. Zod. Use Pydantic if using Python. ### Step 4: Define database schema Create initial migrations for: ```text households users accounts raw_observations financial_events event_observation_links event_relations split_lines categories tags event_tags projects project_participants project_budget_lines project_planned_items payment_lines recurring_commitments promised_payments import_connections audit_log ``` ### Step 5: Implement minimal API Minimum endpoints: ```text POST /households GET /households/:id POST /accounts GET /accounts POST /observations/manual GET /observations/inbox POST /events GET /events GET /events/:id PATCH /events/:id POST /events/:id/confirm POST /events/:id/relations POST /events/:id/splits GET /settlement/summary POST /projects GET /projects GET /projects/:id POST /projects/:id/budget-lines POST /projects/:id/planned-items GET /projects/:id/summary GET /projects/:id/settlement ``` ### Step 6: Implement manual entry flow Manual entry should create: ```text RawObservation(source_type = "manual") -> Candidate FinancialEvent -> optional SplitLine -> optional Project assignment ``` Manual entry fields: ```text amount currency date merchant/counterparty description paid_by account/user event_type category project tags split rule due date if promised payment attachment optional ``` ### Step 7: Implement exact deduplication Start with: ```text raw_hash source_type + provider_transaction_id message_id for email file hash normalized amount/date/merchant fingerprint ``` Create reviewable duplicate candidates. Do not auto-delete. ### Step 8: Implement settlement calculation Start simple: ```text confirmed shared expenses only single currency equal split or explicit SplitLine amounts exclude pending/promised by default optionally include promised payments later ``` Settlement output must include explanation: ```text payer totals share totals net owed events included events excluded pending/promised handling ``` ### Step 9: Implement project budgeting v1 Project v1 should support: ```text project creation participants budget lines planned items manual payment lines linking existing events to project summary calculation project settlement calculation ``` Do not wait for bank imports before building projects. ### Step 10: Add importers After manual/project/settlement core works: ```text CSV importer v1 IMAP importer v1 PayPal email parser Klarna email parser PDF invoice importer receipt photo OCR importer ``` ### Step 11: Add local agent design Define but do not overbuild: ```text local-agent registers with backend local-agent stores connector secrets locally local-agent runs imports/connectors locally local-agent submits RawObservations or normalized candidate events backend never receives connector passwords ``` Possible local-agent API: ```text POST /local-agent/register POST /local-agent/sync/observations GET /local-agent/config POST /local-agent/heartbeat ``` ### Step 12: Add local categorization First version: ```text merchant/counterparty rules IBAN rules amount + recurrence rules project context rules manual correction memory ``` Only later add: ```text local classifier local embeddings local LLM structured suggestions ``` ## 9. Anything that should not be repeated or reopened unless necessary Do not reopen the question of whether Klarna Card has a clean public transaction API unless new evidence is needed. Current working assumption: ```text Klarna Card API access is unavailable or unreliable. Use IMAP, statements/PDFs, manual entry, GDPR export, and bank repayment reconciliation. ``` Do not design the platform as a plain transaction viewer. Do not treat PayPal/Klarna bank debits as ordinary expenses by default. They may settle prior obligations. Do not treat credit card bill payments as ordinary expenses by default. Do not let local AI or categorization directly mutate confirmed ledger entries without review or rules. Do not assume imported rows are final truth. Store them as observations/evidence. Do not overload categories with project names. Use: ```text category: holiday > restaurant project: Stockholm 2026 tags: optional flexible labels ``` Do not assume all users should see all transactions. Privacy modes are part of the design. Do not delay manual entry until connectors are ready. Manual entry is core. Do not delay project budgeting until imports are ready. Project budgeting is core. ## 10. Assumptions about runtime/build/deployment environment These are starting assumptions for development, not confirmed requirements. ```text Development mode: local machine development first. Deployment mode: self-hosted or private household deployment first. multi-tenant SaaS later only if regulatory/compliance issues are handled. Backend: API server with relational database. Database: PostgreSQL preferred for backend. SQLite acceptable for early prototype/local-agent/testing. Frontend: web app first. Mobile: defer; possibly PWA first. Local connector agent: separate process/app running on user-controlled machine. stores secrets locally. talks to backend through authenticated API. Bank connector: not implemented in first coding pass. architecture should allow FinTS/PSD2 connectors later. AI/OCR: local process only. no cloud OCR/LLM/categorization dependency. ``` ## 11. Minimum viable development target The first useful prototype should not connect to banks yet. MVP target: ```text 1. Create household and users. 2. Create accounts. 3. Manually enter expense/payment/promised payment. 4. Assign category, project, tags. 5. Attach raw evidence placeholder. 6. Split expense between users. 7. Detect exact duplicates among manual/CSV-like observations. 8. Show ledger. 9. Show settlement summary. 10. Create project with budget lines and planned items. 11. Show project budget summary: budgeted / committed / paid / due later / settled. ``` This validates the domain model before spending time on connectors. ## 12. Suggested first implementation milestone Milestone name: ```text M0: Local household ledger skeleton ``` Acceptance criteria: ```text - Repository builds. - Database migrations run. - API starts. - Web app starts. - User can create household, users, accounts. - User can create manual RawObservation. - System creates candidate FinancialEvent. - User can confirm event. - User can split event between users. - Settlement summary shows who owes whom. - User can create project. - User can add project budget lines and planned items. - User can link event to project. - Project summary calculates budgeted, committed, paid, due later. - Basic duplicate hash detection exists. - Tests cover settlement and project summary calculations. ``` ## 13. Suggested test cases ### Settlement test ```text A pays 120 € groceries, common 50/50. Expected: A share = 60 € B share = 60 € B owes A = 60 € ``` ### Shared account test ```text Shared account pays 100 € common expense. If shared account is neutral household wallet: no direct A/B reimbursement generated. ``` ### Reimbursement test ```text B transfers A 60 € after A paid 120 € common. Expected: reimbursement settles previous imbalance. transfer is not income. ``` ### PayPal promised payment test ```text PayPal email creates 84 € promised payment due in 30 days. Later bank debit from PayPal for 84 €. Expected: bank debit settles promised payment. no duplicate expense created. ``` ### Credit card test ```text Card purchase 50 € restaurant. Later bank payment 50 € to card issuer. Expected: purchase is expense. card payment is liability payment, not second expense. ``` ### Project partial payment test ```text Hotel planned item: budgeted 800 € committed 760 € deposit paid 200 € due later 560 € Expected: committed = 760 € paid = 200 € due_later = 560 € ``` ### Project settlement test ```text Project has 420 € transport paid by A and 200 € hotel deposit paid by B. Split 50/50. Expected: A paid 420 € B paid 200 € total = 620 € each share = 310 € B owes A = 110 € ``` ### Duplicate observation test ```text Same manual input imported twice with same raw hash. Expected: second observation flagged duplicate_source. no second confirmed event unless user overrides. ``` ## 14. Final note for the next coding agent Start with the domain model and ledger semantics. Connectors, OCR, IMAP, and local AI are important, but they should plug into the same `RawObservation -> CandidateEvent -> Ledger` pipeline later. The first code should prove that the platform can correctly answer: ```text What happened? Who paid? Who benefited? Is it private or shared? Is it already paid? Is it still due? Is it reconciled with evidence? Is it settled between household members? Which project/budget does it belong to? What should happen next? ``` No blocking clarification is required before scaffolding a prototype. The main implementation choice still open is the tech stack; absent other instructions, a TypeScript monorepo with API, web app, shared domain package, PostgreSQL migrations, and a future local-agent package is the most straightforward starting point.