1
Repo-docs-handover
zemion edited this page 2026-07-06 14:38:53 +02:00

Mirrored from /mnt/DATA/git/mousehold/docs/handover.md. Origin: repository. Active tasks and changing state belong in Gitea issues; this wiki page is durable project context.


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:

- 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:

- 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:

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:

- 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:

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:

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:

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:

- plaintext bank login credentials
- unencrypted bank tokens
- TAN/SCA secrets
- unnecessary raw banking payloads outside encrypted storage

Potential model:

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:

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:

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

Suggested initial architecture:

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:

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

id
display_name
email
household_id
created_at

Household

id
name
default_currency
created_at

Account

Represents checking accounts, shared accounts, PayPal, credit cards, Klarna, cash wallets, or virtual liability accounts.

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.

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.

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.

Links evidence to event.

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.

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.

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.

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.

id
household_id
name

Project

First-class project/budget object.

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

id
project_id
user_id
role
default_share_percent nullable

ProjectBudgetLine

Budget category within project.

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.

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.

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.

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.

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.

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

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

ManualEntryImporter
CsvImporter
BankCsvImporter
PayPalCsvImporter
ImapImporter
KlarnaEmailParser
PayPalEmailParser
PdfInvoiceImporter
ReceiptPhotoImporter
FintsConnector
Psd2AggregatorConnector
LocalAgentSyncClient

Matching functions

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

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

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:

duplicate_of
same_economic_event_as
settles
funds
reimburses
refunds
reverses
part_of_statement
replaces_pending_authorization

Matching strategy

Use layered matching:

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.

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:

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:

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:

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:

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:

- 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

- 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

- 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

- 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:

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:

BankTransactionObservation
PayPalTransactionObservation
FinancialEvent
LedgerEntry
PaymentLine

UI style preference

No detailed UI style was decided. Functional preference:

- 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:

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:

docs/handover.md

Step 3: Define the domain package

Create shared schemas/types for:

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:

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:

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:

RawObservation(source_type = "manual")
  -> Candidate FinancialEvent
    -> optional SplitLine
      -> optional Project assignment

Manual entry fields:

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:

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:

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:

payer totals
share totals
net owed
events included
events excluded
pending/promised handling

Step 9: Implement project budgeting v1

Project v1 should support:

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:

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:

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:

POST /local-agent/register
POST /local-agent/sync/observations
GET  /local-agent/config
POST /local-agent/heartbeat

Step 12: Add local categorization

First version:

merchant/counterparty rules
IBAN rules
amount + recurrence rules
project context rules
manual correction memory

Only later add:

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:

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:

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.

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:

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:

M0: Local household ledger skeleton

Acceptance criteria:

- 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

A pays 120 € groceries, common 50/50.
Expected:
  A share = 60 €
  B share = 60 €
  B owes A = 60 €

Shared account test

Shared account pays 100 € common expense.
If shared account is neutral household wallet:
  no direct A/B reimbursement generated.

Reimbursement test

B transfers A 60 € after A paid 120 € common.
Expected:
  reimbursement settles previous imbalance.
  transfer is not income.

PayPal promised payment test

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

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

Hotel planned item:
  budgeted 800 €
  committed 760 €
  deposit paid 200 €
  due later 560 €
Expected:
  committed = 760 €
  paid = 200 €
  due_later = 560 €

Project settlement test

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

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:

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.