Files
mousehold/docs/handover.md
2026-06-28 13:48:15 +02:00

1501 lines
33 KiB
Markdown

# 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.