Work / Chitragupt / Wiki / Decisions
2026-07-12-portfolio-position-dedup
Decisioncanonicalverified 2026-07-12
DECISION.2026-07-12.PORTFOLIO-POSITION-DEDUPDecision
Portfolio position ids are stable across re-uploads of the same
holding statement — the id is ${holdingId}, not
${holdingId}__${docId}. Re-uploading the same MF CAS or NPS
statement overwrites the prior position rather than adding a second
one.
Why
The recompute audit found that every re-upload of an MF CAS produced
a new portfolio_positions/{holdingId__docId} row, causing the
engine to double-count NAV — a user who uploaded the same CAS twice
saw their portfolio value inflated by 2x. Contributing factors:
- Positions carry no natural key beyond
holdingId + docId. - The
latest-upload-winspromise in the mapper comment was aspirational. - Firestore's write-if-not-exists semantics don't apply — every
document confirm ran
set().
Impact
portfolio-position-mapper.ts:120— position id changed.- Test updated: two-doc dedup case now expects equal position ids.
- Engine: no change; the engine sums by
holding_id, which was always correct — only the underlying position table was wrong. - V2 will introduce per-lot position ids (
${holdingId}__${lotId}) when the FIFO lot ledger lands; that changes this id scheme again.
Status
Active. Landed as part of the Phase 4 recompute-correctness sweep.
Sources
- Phase 4 recompute-correctness commit (Phase 4 sweep).
apps/functions/src/portfolio/portfolio-position-mapper.ts.- Related entity: pillar-portfolio.
Every project of mine is written down like this.
Read the résumé