DASHBOARD WIRING VALIDATION β Red Flags & Alignment Check
Date: 2025-12-19Reviewer: AI Assistant
Status: π΄ CRITICAL RED FLAGS IDENTIFIED β Cutover blocked until resolved
Executive Summary
The validation documentDASHBOARD_WIRING_VALIDATION.md contains accurate assumptions about architecture and widget inventory, but identifies a CRITICAL BLOCKER: The API layer (api/bigquery/queries.py) has hardcoded dataset names with no mechanism to switch from payroll_analytics to payroll_analytics_shadow.
Impact: The documentβs Step 2 cutover plan (βSwitch API dataset pointer via env varβ) cannot be executed because the code doesnβt support dataset switching.
β ALIGNMENT VERIFIED
1. Widget Inventory (100% Match)
Document Assumption: Core dashboard widgets are theCardId set from dashboard/src/types/dashboard.ts
Verification: β
CONFIRMED
- File:
dashboard/src/types/dashboard.ts(lines 6-23) - All 12 widgets listed in document match exactly:
- KPI:
kpi_totalBusinesses,kpi_totalEmployees,kpi_grossPayout,kpi_totalChargebacks - Commissions:
commissions_agentCommissions,commissions_ownerNetCommission - Charts:
charts_businessHealthDistribution,charts_employeeGrowthLossAnalysis - Growth:
growth_splitGrowthLossTable,growth_newLostBusinesses - Rankings:
rankings_top10Businesses,rankings_top10Agents
- KPI:
2. API Routes Exist (100% Match)
Document Assumption: Dashboard reads via FastAPI routes, not direct BQ Verification: β CONFIRMED- All routes exist in
api/routes/ceo_metrics.pyandapi/routes/dashboard.py:/api/v1/ceo-metricsβ (line 49,api/routes/ceo_metrics.py)/api/v1/top-businessesβ (line 35,api/routes/dashboard.py)/api/v1/top-agentsβ (line 81,api/routes/dashboard.py)/api/v1/growth-lossβ (line 135,api/routes/dashboard.py)/api/v1/growth-loss-detailsβ (line 213,api/routes/dashboard.py)/api/v1/new-lost-businessesβ (line 260,api/routes/dashboard.py)/api/v1/business-healthβ (line 330,api/routes/dashboard.py)
3. Routes Call api/bigquery/queries.py Functions (100% Match)
Document Assumption: Routes call functions from api/bigquery/queries.py
Verification: β
CONFIRMED
ceo_metrics.pyimports:get_ceo_snapshot_from_view,get_ceo_metrics_range(lines 17-20)dashboard.pyimports:get_top_businesses_from_view,get_top_agents_from_view,get_growth_loss_summary,get_growth_loss_details,get_business_health_distribution,get_new_lost_businesses(lines 11-18)
4. Multi-Tenant Isolation (100% Match)
Document Assumption: JWT βtenant_id dependency enforces isolation
Verification: β
CONFIRMED
- All routes use
tenant_id: str = Depends(get_tenant_id)(e.g.,ceo_metrics.pyline 54,dashboard.pyline 40) get_tenant_idextractstenant_idfrom JWT token (api/dependencies.py)
5. Shadow Dataset Publishing (100% Match)
Document Assumption: Repo B publishes topayroll_analytics_shadow
Verification: β
CONFIRMED
- Script:
scripts/publish_repo_b_to_shadow.py(line 207:dataset_id = "payroll_analytics_shadow") - Publishes to
payroll_analytics_shadow.ceo_metricstable (line 208) - Creates
payroll_analytics_shadow.ceo_snapshotview (line 305)
π΄ CRITICAL RED FLAGS
RED FLAG #1: Hardcoded Dataset Names in API Layer (BLOCKER)
Severity: π΄ CRITICAL β Blocks cutover Issue:api/bigquery/queries.py hardcodes payroll-bi-gauntlet.payroll_analytics.* in all SQL queries. There is no environment variable or configuration mechanism to switch to payroll_analytics_shadow.
Evidence:
payroll-bi-gauntlet.payroll_analytics.* in api/bigquery/queries.py
Document Assumption vs Reality:
- Document says: βPreferred architecture: ONE config value controls dataset selection (e.g. env var
BQ_DATASET_ANALYTICS)β - Reality: No such env var exists. Project ID is configurable (
GCP_PROJECT_ID), but dataset name is hardcoded.
- Cutover Step 2 cannot be executed β Thereβs no env var to flip
- Dashboard will continue reading legacy dataset even after shadow data is published
- Parity validation will pass (shadow data exists), but production dashboard wonβt use it
- Add
BQ_DATASET_ANALYTICSenv var (defaults topayroll_analytics) - Refactor all queries in
api/bigquery/queries.pyto use configurable dataset name - Update Cloud Run deployment to set
BQ_DATASET_ANALYTICS=payroll_analytics_shadowfor cutover
RED FLAG #2: Table vs View Confusion
Severity: π‘ MEDIUM β Needs clarification Issue: Document mentionsceo_metrics table, but some queries use ceo_snapshot view. Need to clarify which endpoints use which.
Evidence:
- Document Task A1 queries
payroll_analytics_shadow.ceo_metrics(table) api/bigquery/queries.pyline 361 queriespayroll_analytics.ceo_snapshot(view)- Shadow publisher creates BOTH:
- Table:
payroll_analytics_shadow.ceo_metrics(frompublish_repo_b_to_shadow.pyline 208) - View:
payroll_analytics_shadow.ceo_snapshot(frompublish_repo_b_to_shadow.pyline 305)
- Table:
- Which endpoints should read from
ceo_metricstable vsceo_snapshotview? - Document Task B4 mapping table shows
ceo_metricsfor all KPI widgets β is this correct?
RED FLAG #3: Missing Dataset Configuration Pattern
Severity: π‘ MEDIUM β Inconsistency with Repo B Issue: Repo B hasBQ_SHADOW_MODE env var pattern (repo_b/upload_to_bq.py line 62), but API layer doesnβt follow same pattern.
Evidence:
- Option A: Use same
BQ_SHADOW_MODEpattern (adds_shadowsuffix) - Option B: Use explicit
BQ_DATASET_ANALYTICSenv var (more flexible)
RED FLAG #4: Document Task B3 Assumption Not Validated
Severity: π‘ MEDIUM β Validation incomplete Issue: Document Task B3 says βinspectapi/bigquery/queries.py for hard-coded dataset stringsβ β this check hasnβt been run yet (document is a template).
Required Action: Complete Task B3 mapping table by:
- Grepping
api/bigquery/queries.pyfor all dataset references - Mapping each query function to its dataset/table
- Confirming all CEO/lifecycle endpoints read from shadow dataset (after RED FLAG #1 is fixed)
π’ MINOR ALIGNMENT ISSUES
Issue #1: Document SQL Uses Placeholder PROJECT
Severity: π’ MINOR β Documentation clarity
Issue: Document Task A1-A4 SQL uses PROJECT placeholder, but actual project ID is payroll-bi-gauntlet.
Recommendation: Update document SQL examples to use actual project ID or @project_id parameter.
Issue #2: Document Mentions ceo_snapshots Plural
Severity: π’ MINOR β Typo
Issue: Document line mentions βceo_metrics and/or ceo_snapshotsβ β should be singular ceo_snapshot (itβs a view, not a table).
Recommendation: Fix typo in document.
π REQUIRED ACTIONS BEFORE CUTOVER
Priority 1 (BLOCKER)
- Fix RED FLAG #1: Implement dataset configuration in
api/bigquery/queries.py- Add
BQ_DATASET_ANALYTICSenv var (default:payroll_analytics) - Refactor all queries to use configurable dataset name
- Test with both
payroll_analyticsandpayroll_analytics_shadow
- Add
Priority 2 (HIGH)
-
Complete Task B3: Generate actual mapping table
- Run grep to find all dataset references
- Map each widget β endpoint β query function β dataset/table
- Verify 100% of dashboard endpoints will read from shadow after cutover
-
Clarify RED FLAG #2: Document which endpoints use
ceo_metricstable vsceo_snapshotview- Update Task B4 mapping table with correct table/view names
Priority 3 (MEDIUM)
-
Fix RED FLAG #3: Align dataset configuration pattern with Repo B (optional)
- Decide on single pattern (
BQ_SHADOW_MODEvsBQ_DATASET_ANALYTICS) - Update both codebases to use same pattern
- Decide on single pattern (
- Fix MINOR ISSUES: Update document SQL examples and fix typos
π― GO/NO-GO RECOMMENDATION
Current Status: π΄ NO-GO Blockers:- RED FLAG #1: No dataset switching mechanism in API layer
- β
Implement
BQ_DATASET_ANALYTICSenv var support - β Complete Task B3 mapping table validation
- β Test cutover with shadow dataset (smoke test endpoints)
- β Verify parity gate (Task C) passes for July 2025
π NOTES
Dataset Configuration Implementation Pattern
Recommended approach (based on existingPROJECT_ID pattern):
BQ_DATASET_ANALYTICS) for flexibility and clarity.
β VALIDATION CHECKLIST
- Widget inventory matches document
- API routes exist and are correctly wired
- Routes call
api/bigquery/queries.pyfunctions - Multi-tenant isolation implemented
- Shadow dataset publishing confirmed
- Dataset configuration mechanism exists β BLOCKER
- Task B3 mapping table completed
- Table vs view usage clarified
- Parity gate validation passed
Next Steps: Fix RED FLAG #1, then re-run validation checklist.