Attribution doc gaps
Stale or misleading claims we found in the previous attribution guide and in the code's own comments — so future edits don't reintroduce them.
This page exists for whoever edits the attribution docs next. Each item below is a place where a docstring, an old guide, or an intuitive reading of one file disagrees with what the code actually does — verified by opening the current file, not by trusting the comment.
The leaderboard has a fourth bucket the old guide never mentions
A previous version of this documentation described the leaderboard as three buckets: sources,
organic, pending. core/source_leaderboard/materialise.py returns four top-level rows —
sources, organic, pending, and checking — with matching checking_net_cents and
checking_subs counters in reconciliation/coverage. checking holds a brand-new paying fan
the engine's event-driven source check hasn't settled yet. A doc (or an integration) built from
only three buckets will silently drop this money out of whatever total it reconstructs.
attribution_kind's own docstring says "write-only" — it isn't
core/attribution/kind.py's module docstring states: "WRITE-ONLY — nothing in the
leaderboard/stats/dashboard reads this yet." That was true once; it no longer is. At least ten
call sites read OfConversion.attribution_kind today, including the leaderboard package this
docstring itself claims is clear of it:
core/conversions_report.py— filters rows byattribution_kind == "link_attributed"to build the per-fan link-conversion view.core/country_revenue.py'scompute_campaign_revenue— defaults itskindsfilter to(KIND_LINK_ATTRIBUTED,)and always queriesattribution_kind.in_(kinds).core/free_pool.pyandcore/of/campaigns.pyboth callcompute_campaign_revenuewith an explicitkinds=(KIND_CID_ATTRIBUTED, KIND_LINK_ATTRIBUTED, KIND_PENDING)to decide whether a code has ever earned money before resurfacing/hiding it.core/source_leaderboard/resolve.py— its own inline comment says as much:attribution_kindis read here, not derived — feeding the L4 campaign stage of the priority ladder this very docstring's package is supposedly untouched by.core/source_leaderboard/fan_card.pyandcore/source_leaderboard/collect_people.py— both filter/select on it directly.core/fan/country.pyandcore/fan/classify.py— both query it to resolve a fan's country and classification.core/offer/attribution.py— filters onattribution_kind == "link_attributed"when deciding which promo-offer entries to promote.
A doc (or a future change) written from the docstring alone would badly underestimate the blast
radius of touching attribution_kind — it isn't dead weight, and the package the docstring
names as unaffected is itself one of the readers.
reconciliation.balanced is a tautology, not proof
The old guide told readers to "trust the balanced flag." Read literally, that overstates what
it checks:
balanced is guaranteed by construction
_lb_residuals dumps any leftover, unbucketed money into organic specifically so that
sources + organic + pending + checking == total_net_cents always holds. The code's own
comment in materialise.py calls this "a tautology, not proof" — balanced: true cannot, by
itself, catch a fan who was double-counted into two buckets.
The check that actually catches that class of bug is reconciliation.fans_disjoint
(fans_counted == fans_distinct — every fan's id appears in exactly one bucket's fan set). It's
computed right next to balanced in the same response but wasn't documented at all in the old
guide. If you're writing an integrity check against this endpoint, assert fans_disjoint, not
just balanced.
resolver.py's docstring undersells what actually decides a fan's source
core/source/resolver.py calls itself "the canonical fan→source resolver... ONE source of
truth" and documents a 4-stage ladder (cid → link → pending → organic). Read on its own, that
describes a smaller system than what actually runs: the leaderboard's _lb_attribute adds
campaign, D3b-code, and LLM-guess stages between "mapped link" and "pending," and resolver.py's
own inline comment admits KIND_LLM is deliberately kept out of its ranking. Documenting "how a
fan's source is decided" from resolver.py alone — as opposed to reading _lb_attribute too —
undercounts the real ladder by three stages. See
Source attribution for
the full, code-verified ladder.
What's next
- Source attribution — the corrected mental model these four items feed into.
- Read revenue by traffic source
A fan's card shows another page's data
Why the same fan_id can render one page's money and DMs under another page's name — and the fail-closed header that stops it.
Permission gating surprises
A missing button or a "no access" tile usually means a specific, narrow permission is missing — not that something broke.