Skip to content

audit: Share column renders 120% on the live weekly statement — contribution % can exceed 100% #31

Description

@tkowalczyk

Evidence (live on production 2026-06-12)

https://ogsfrompoly.com/statements/2026-05-31-weekly Top-wallets table renders wallet_1f8a → Share: 120%.

Mechanics: formatContributionPercent(bucketed, statement.hypothetical_pnl_usd) in src/pages/[collection]/[...slug].astro:192-196 divides one wallet's bucketed PnL (174.95 → bucketed 175) by the headline total (146.41). Since the headline total includes hundreds of other alerts (many negative), a single wallet can exceed it → 120%. Verified by executing the real frontmatter through the actual functions.

Secondary papercuts in the same column:

  • Numerator is bucketed but denominator is not (175/146.41, not 174.95/146.41) — the % leaks a different rounding than the Bucket column implies.
  • A negative total with a positive wallet would render a negative share.
  • 0% rows (e.g. wallet PnL −1.60 → bucket 0) read as "contributed nothing" while the bucket column shows <$25 — fine, but worth a conscious decision.

Decision needed (hitl)

What should "Share" mean when contributions aren't a partition of the total? Options:

  1. Cap display at >100% and add a footnote ("share of headline PnL; can exceed 100% because other alerts net negative") — most honest, keeps signal.
  2. Change denominator to Σ|top-wallet PnL| (a real partition) and relabel "Share of top-wallet PnL".
  3. Drop the column for v1.

TDD plan (failing tests first; applies once option chosen)

src/lib/statement-format/contribution-percent.test.ts already exists — add cases FIRST:

  • wallet 175 / total 146.41 → expected display per chosen option (currently asserts nothing above 100%)
  • negative total, positive wallet
  • consistency: percent computed from the same value the Bucket column displays
  • if option 1: footnote copy lives next to the existing "rows do not sum to headline PnL" caption

Acceptance criteria

AC Test
No unexplained >100% (or out-of-range) share on any rendered statement unit tests on formatContributionPercent + content-lint pass over real statement data
Caption copy matches the chosen semantics review

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghitlHuman-in-the-loop: needs design, copy, or decision

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions