# Component valuations and period-evidence hardening

Use this when one managed investment scope contains multiple additive account components, source totals are controls, and activation depends on explicit cashflow or no-flow evidence.

## 1. Control totals are not additive valuations

For a source that reports securities, settlement cash, and a confirmed total:

- Materialize one canonical valuation per additive account component.
- Reconcile `sum(components) == source_total` within the existing tolerance.
- Retain the source total as provenance/control evidence; never add it as a third valuation.
- Reject a historical combined value assigned to one component account when the selected scope requires both accounts.
- Keep old confirmed rows append-only. Corrections use a new version and supersession lineage after Confirm.

### Same-day source selection

Select a coherent component set, never independent highest-ranked rows that can mix sources.

A source set is eligible only when:

1. every required component account occurs exactly once;
2. every component is complete and has valid canonical FX;
3. every component belongs to the same source **and source-reference/run/batch identity**;
4. the source set is deterministic under source priority, capture time, version, and stable ID.

Do not group by a generic source name alone: two partial rows from different runs can otherwise form a fictitious complete pair. Preserve `source_reference` in the in-memory grouping key even when the persisted `source` string is identical. Group synthetic read-only projections by source snapshot/batch ID.

For competing complete observations of the same priority on one business day, prefer the later capture/statement time before comparing internal version counters; version numbers from different lineage families are not globally comparable. A partial high-priority daily set must not displace a complete lower-priority official set.

Append-only observation writers need two identities:

- the logical account/day chain for supersession;
- the immutable observation identity, normally `source + source_reference/run`.

An exact retry of an existing observation is a no-op even when that row has since been superseded. Reusing the same observation identity with a different payload must fail closed rather than create another version.

After retaining source variants for component-set selection, restore the pre-existing anti-double-counting rule for every other account and instrument: exactly one canonical row per scope/account/calendar-day. Add regressions for two source variants on a non-component account, partial components from different runs, same-day complete statements with different capture times, retry-after-supersession, and conflicting observation replay.

### Example diagnostic decomposition

When a daily value differs from an official statement, compare components, not only totals. One verified case decomposed a CHF 1,149.99 total difference into:

- +CHF 1,980.60 securities market/FX difference;
- -CHF 830.61 stale cash difference.

This distinguished different market-close prices/FX plus stale combined cash from a simple cash double count.

## 2. Performance cashflow classification is a derived layer

Preserve raw source classifications. Derive performance semantics deterministically:

- external deposit/withdrawal: only scope-crossing capital;
- transfer wholly inside the selected scope: neutral;
- scope-crossing transfer: external, with direction from the selected leg;
- dividend/interest: internal income;
- fees/taxes: internal expense;
- buy/sell/FX trade: portfolio trade;
- split/corporate action/opening position: neutral;
- unknown class or unclear transfer boundary: fail closed.

A reclassification preview fingerprints every relevant input row, not `COUNT(*)`/`MAX(timestamp)`. Zero activities are not positive readiness evidence; return a distinct missing-evidence reason unless a separately audited no-flow contract covers the period.

Every success, empty, missing-scope, and blocked early-return shape must validate against the declared API response model. Include required zero-valued counters such as `activity_count=0`; test early-return payloads with the actual schema model so an HTTP 500 cannot hide behind service-level dict tests.

## 3. Period evidence must not duplicate or shrink

### Economic repetition protection

Confirmation IDs make request replay idempotent, but they do not prevent a second confirmation ID from duplicating the same money movement.

Derive a stable economic identity from:

- account/scope;
- date;
- direction;
- original amount and currency;
- confirmed canonical amount;
- FX provenance;
- evidence reference when supplied.

Use that identity for transaction uniqueness independently of the confirmation ID. On re-preview, classify existing identical rows as unchanged and show `new`, `existing`, and `valuation_snapshots=0`. Confirm may use `INSERT OR IGNORE` only when the deterministic economic key is database-unique and the audit still records the attempted period evidence.

Allow equal same-day amounts as separate economic events only with distinct authoritative evidence identities; otherwise fail closed rather than guess.

### Non-shrinking coverage

A single current coverage row cannot silently represent disjoint periods.

- Exact or enclosed repeat: preserve the wider existing range.
- Overlapping or adjacent complete periods: derive the union and audit both requested period and effective union.
- Disjoint periods with a gap: reject until the gap is evidenced or the schema can represent period segments.
- A cashflow/no-flow confirmation writes no valuation.

Fingerprint full existing transactions, coverage, scope mappings, and proposed entries. Recheck after obtaining the write lock.

## 4. A Preview must be visibly reviewable

Before exposing Confirm, render:

- selected filename for CSV input;
- normalized date, direction, amount, currency, canonical amount, and evidence reference for every row;
- requested period and effective coverage period;
- new versus already-existing rows;
- explicit `0 valuations` statement.

A row count alone is not a meaningful financial Preview. Any date, mode, attestation, CSV, or manual-row edit invalidates the applied preview immediately and hides Confirm. Keep draft input separate from the deep-cloned applied request used by Confirm.

Treat file reads and preview requests as asynchronous state transitions, not simple event handlers:

- maintain independent generation counters for file reads and preview requests;
- every input mutation invalidates both generations and clears the applied preview;
- while a file read is pending, expose a visible loading state and disable **and handler-guard** Preview/Confirm;
- commit a file result only when its generation is still current, then invalidate any older preview request again;
- discard late preview responses whose generation no longer matches;
- ensure cancellation/reset also clears busy flags so stale requests cannot leave the form permanently disabled.

Test both races deterministically with deferred promises: a late file read after switching to manual rows, and a late preview response after editing the underlying draft.

The main dashboard should show one compact setup card. Keep source details and technical diagnostics collapsed by default; test the expanded workflow separately.

## 5. Review and release closure

Independent review probes should include:

- missing one required component role;
- complete official pair versus partial higher-priority daily pair;
- multiple same-day source snapshots;
- two source variants for an unrelated account;
- same economic cashflow under two confirmation IDs;
- narrower, adjacent, and gapped coverage periods;
- zero-activity reclassification;
- CSV row visibility and manual-edit invalidation;
- 1440/820/390 responsive expanded-form UAT;
- normal render network log proving no provider calls.

Every post-review fix needs a focused regression. Then rerun the entire backend suite, frontend suite, typecheck, production build, lint, compile, schema/migration gate, Git safety, and browser UAT on the final frozen tree. Earlier green gates do not survive later review fixes.

If execution stops before those final gates, report a checkpoint only: no feature commit, PR, merge, deployment, or `sentinel_equal=true` claim.

### Dual-sentinel production closure

For a schema-frozen, read-only activation release, use two comparisons rather than one ambiguous post-smoke check:

1. **Whole-release sentinel:** before deployment, capture the production DB byte hash and mtime, schema/integrity/FK state, every user-table row count, protected snapshot/coverage/audit counts, scheduler state/gate, deployment cleanliness, and old deployed SHA. After all service and browser reads, require every field to match except the explicitly expected deployment SHA transition.
2. **Final-render sentinel:** immediately before the last production browser reload, capture the same state again; after the reload require exact equality including the deployed SHA. This isolates GET/render side effects from the intentional code deployment.

Probe loopback health, DB-backed setup/coverage, private frontend, proxied health, and browser navigation as separate consent-sensitive operations so one blocked private-network probe does not erase evidence from another. The final browser pass must show the changed setup section, no console/page errors, and no unauthorized Confirm interaction. A readiness result such as `review_inputs` or `0/N ready` is valid no-activation evidence, not a smoke failure. Only report `sentinel_equal=true` after both comparisons pass and services, timer state, gate, logs, and clean exact merge SHA are rechecked.
