Documentation
Project Status

Known Gaps

Places where the dashboard lies, oversimplifies, or is operationally fragile. Read this before you defend any number to a client.

Updated regularly
This page captures known data-quality and operational risk. Update it when you discover a new issue. The next analyst should not have to rediscover what we already know is broken.

Attribution accuracy

Lead source attribution is only as good as the contact source field in GHL. If account managers don't standardise intake (UTM-tagged web forms, consistent labels for offline calls), the breakdown looks tidy but is wrong. Treat the breakdown as directional, not authoritative, until we audit per-client tagging hygiene.

We override GHL attribution with platform truth where the platform is more reliable (Google Ads conversions, Meta Ads leads) — but the override only kicks in when the platform integration is connected. Clients without Meta Ads connected have all paid social attribution flowing through the imperfect GHL bucketer.

Adding the adwords scope to OAuth in April 2026 invalidated every existing Google integration for ads purposes. Old tokens silently lack the scope and the Ads route returns 403. We have not yet forced a re-auth across the board, so most clients return empty Google Ads data.

Mitigation: see integrations.scope in Supabase and check whether adwords is present. If not, the agency token must reconnect.

GHL API key deprecation

GHL is moving from API keys to OAuth 2.0. We rely on a single agency-level API key today. When the deprecation date is firm, we will need a per-location OAuth install flow. The dashboard will break if we don't migrate before the cutover.

DataForSEO staleness

Keyword rankings and backlinks come from a weekly n8n snapshot. If n8n misses a run, the data is stale and the snapshot date in the card header is the only signal. There is no "refresh" button.

Meta long-lived token expiry

Meta tokens are 60 days. We refresh on read, but if a token has fully expired, the route returns no data and the client must reconnect via the Integrations panel. There is no proactive notification — the dashboard goes quiet on Meta data and someone has to notice.

Microsoft Clarity

The Clarity card shows on the Website Performance tab but is auto-hidden because every value is hard-coded null. Until we wire Clarity, treat the absence as silence — clients who ask "where are my Clarity metrics?" should be told it is on the roadmap.

RFMS sheet column layout is rigid

The Historical Revenue section assumes a specific column order in the client's Google Sheet. If a client renames a column or re-orders, the chart breaks silently — no error, just zeros. We should validate the header row at fetch time and surface an error.

PDF rendering fragility

@react-pdf/renderer is unforgiving. A small style change can push a section onto the next page and orphan a header. Always download and review every page after a PDF change. There is no automated visual diff.

RLS coverage

Most tables have RLS enabled, but new tables sometimes ship without policies because the dev workflow is "ALTER TABLE … ADD COLUMN" not "create new table". Verify RLS on any new table you add. Default-deny is a footgun — a client with no policy will see nothing, which we will discover only when they complain.

No automated tests

We have no unit tests, no integration tests, no E2E tests. Every change is verified manually against Denver. This works while the team is small and the failure mode is "data looks weird" rather than "app crashes" — but it is a known risk, not a feature.

Single-client verification bias

Almost everything is verified against Denver Carpet & Flooring. Denver is well-instrumented but it is one client with one industry pattern (home services). Bugs that only manifest with multi-location clients, unusual GHL pipeline shapes, or non-USD currency, will not be caught until those clients onboard.