diff --git a/stacks/broker-sync/main.tf b/stacks/broker-sync/main.tf index b3c71905..7c99a916 100644 --- a/stacks/broker-sync/main.tf +++ b/stacks/broker-sync/main.tf @@ -105,7 +105,7 @@ resource "kubernetes_cron_job_v1" "version_probe" { metadata {} spec { backoff_limit = 1 - ttl_seconds_after_finished = 300 + ttl_seconds_after_finished = 86400 template { metadata { labels = { app = "broker-sync", component = "version-probe" } @@ -246,7 +246,12 @@ resource "kubernetes_cron_job_v1" "imap" { concurrency_policy = "Forbid" successful_jobs_history_limit = 3 failed_jobs_history_limit = 5 - suspend = true # enable in Phase 2 + # Unsuspended 2026-04-19 for RSU vest ground-truth ingestion — the parser + # now detects Schwab Release Confirmations and scaffolds VestEvents; the + # postgres sink that persists them into payslip_ingest.rsu_vest_events is + # pending a real-email fixture and cross-service DB grant (see + # follow-up beads task filed under the RSU tax spike fix epic). + suspend = false job_template { metadata {} spec { diff --git a/stacks/monitoring/modules/monitoring/dashboards/uk-payslip.json b/stacks/monitoring/modules/monitoring/dashboards/uk-payslip.json index 76a513eb..eb20a480 100644 --- a/stacks/monitoring/modules/monitoring/dashboards/uk-payslip.json +++ b/stacks/monitoring/modules/monitoring/dashboards/uk-payslip.json @@ -2158,6 +2158,151 @@ "editorMode": "code" } ] + }, + { + "id": 15, + "title": "RSU vest reconciliation \u2014 payslip vs Schwab", + "description": "Per-vest-month join between payslip.rsu_vest (what HMRC reporting shows) and Schwab's vest-confirmation email data (what actually happened at the broker). Empty rows until the broker-sync IMAP ingest runs and VestEvents are persisted.", + "type": "table", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "payslips-pg" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 169 + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "right", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "unit": "currencyGBP", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "vest_month" + }, + "properties": [ + { + "id": "custom.width", + "value": 140 + }, + { + "id": "custom.align", + "value": "left" + }, + { + "id": "unit", + "value": "none" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "ticker" + }, + "properties": [ + { + "id": "custom.width", + "value": 80 + }, + { + "id": "unit", + "value": "none" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "shares_vested" + }, + "properties": [ + { + "id": "unit", + "value": "none" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "tax_delta_pct" + }, + "properties": [ + { + "id": "unit", + "value": "percent" + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-background", + "mode": "basic" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 2 + }, + { + "color": "red", + "value": 5 + } + ] + } + } + ] + } + ] + }, + "options": { + "cellHeight": "sm", + "footer": { + "show": false + } + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "payslips-pg" + }, + "rawQuery": true, + "editorMode": "code", + "format": "table", + "rawSql": "WITH vest_by_month AS (SELECT DATE_TRUNC('month', vest_date)::date AS vest_month, ticker, SUM(shares_vested) AS shares_vested, SUM(gross_value_gbp) AS broker_gross_gbp, SUM(tax_withheld_gbp) AS broker_tax_gbp FROM payslip_ingest.rsu_vest_events GROUP BY 1, 2), payslip_by_month AS (SELECT DATE_TRUNC('month', pay_date)::date AS vest_month, SUM(rsu_vest) AS payslip_rsu_gbp, SUM(income_tax - COALESCE(cash_income_tax, income_tax)) AS payslip_rsu_tax_gbp FROM payslip_ingest.payslip WHERE rsu_vest > 0 GROUP BY 1) SELECT COALESCE(v.vest_month, p.vest_month) AS vest_month, v.ticker, v.shares_vested, v.broker_gross_gbp, p.payslip_rsu_gbp, (p.payslip_rsu_gbp - v.broker_gross_gbp) AS gross_delta_gbp, v.broker_tax_gbp, p.payslip_rsu_tax_gbp, (p.payslip_rsu_tax_gbp - v.broker_tax_gbp) AS tax_delta_gbp, CASE WHEN v.broker_tax_gbp IS NULL OR v.broker_tax_gbp = 0 THEN NULL ELSE ABS(p.payslip_rsu_tax_gbp - v.broker_tax_gbp) * 100.0 / v.broker_tax_gbp END AS tax_delta_pct FROM vest_by_month v FULL OUTER JOIN payslip_by_month p ON p.vest_month = v.vest_month ORDER BY vest_month DESC" + } + ] } ], "refresh": "5m",