monitoring: "Your comp vs the market" panel on Job Hunter dashboard

Add a barchart (panel 10) ranking every company's London p50 total comp
(COALESCE total/base) with the user's current comp shown in line, so it's a
direct "how do I compare" view. The user's figure is NOT hardcoded in the
dashboard JSON — it's a labeled comp_point in the DB (company_slug
'self-current', source 'self', "Me (Meta IC5)"), keeping the sensitive number
out of git. It's below the £500k alert bar (no Slack ping) and ranks too low
to appear in analyze leaders. Runbook documents the panel + how to update the
baseline.

[ci skip] — dashboard ConfigMap applied locally (targeted).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-06-02 21:23:01 +00:00
parent 74313149dd
commit deb0dd4778
2 changed files with 103 additions and 0 deletions

View file

@ -255,6 +255,37 @@ WHERE c.slug = 'janestreet' AND s.snapshot_date IN ('2026-06-02', '2026-08-30')
GROUP BY c.display_name, s.snapshot_date;
```
### "Your comp vs the market" dashboard panel + your baseline
The Job Hunter Grafana dashboard (`grafana.viktorbarzin.me` → Job Hunter) has a
bar chart **"Your comp vs the market — London p50 total comp"** ranking every
company's London median TC with your current comp shown in line. Your figure is
deliberately **not hardcoded in the committed dashboard JSON** — it lives in the
DB as a labeled comp_point (`company_slug='self-current'`, `source='self'`,
display "Me (Meta IC5)"). It sits below the £500k alert bar (never pings Slack)
and ranks too low to surface in `analyze` leaders — it only appears on this
chart and as its own `comp-table` row.
Update it when your comp changes (the only place the number lives):
```bash
kubectl -n job-hunter exec deploy/job-hunter -- python -c "
import asyncio; from decimal import Decimal; from datetime import date
from job_hunter.db import create_engine_from_env, make_session_factory
from job_hunter.sources.comp.base import CompPoint
from job_hunter.storage_comp import upsert_comp_point
async def m():
e=create_engine_from_env(); sf=make_session_factory(e)
async with sf() as s:
await upsert_comp_point(s, CompPoint(source='self', external_id='self-current',
company_slug='self-current', company_display_name='Me (Meta IC5)',
level_slug='senior', location_bucket='london',
total_value=Decimal('267000'), currency='GBP', effective_date=date.today()))
await s.commit()
await e.dispose()
asyncio.run(m())"
```
### Interpreting the numbers — caveats
- **Sample size**: `analyze` flags companies with `n < 3` as `low_confidence`. A single self-reported datapoint is anecdote, not a band — chase the p50 only where n is healthy.

View file

@ -332,6 +332,78 @@
],
"title": "Base-salary trend (p50) — top 5 companies",
"type": "timeseries"
},
{
"id": 10,
"type": "barchart",
"title": "Your comp vs the market \u2014 London p50 total comp (GBP)",
"description": "Per-company London median total comp (COALESCE total/base) ranked vs your current TC. 'Me (Meta IC5)' is a labeled data point in the DB (source='self'), not a hardcoded dashboard value.",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "job-hunter-pg"
},
"gridPos": {
"h": 14,
"w": 24,
"x": 0,
"y": 48
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisPlacement": "auto",
"fillOpacity": 85,
"gradientMode": "none",
"lineWidth": 1,
"axisCenteredZero": false
},
"mappings": [],
"unit": "currencyGBP",
"thresholds": {
"mode": "absolute",
"steps": []
}
},
"overrides": []
},
"options": {
"orientation": "horizontal",
"xField": "company",
"colorByField": "who",
"showValue": "auto",
"stacking": "none",
"barRadius": 0,
"barWidth": 0.85,
"groupWidth": 0.9,
"fullHighlight": false,
"legend": {
"showLegend": true,
"displayMode": "list",
"placement": "bottom",
"calcs": []
},
"tooltip": {
"mode": "single",
"sort": "none"
},
"xTickLabelRotation": 0,
"xTickLabelSpacing": 0
},
"targets": [
{
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "job-hunter-pg"
},
"format": "table",
"rawQuery": true,
"refId": "A",
"rawSql": "SELECT c.display_name AS company, CASE WHEN bool_or(cp.source = 'self') THEN 'You' ELSE 'Market' END AS who, percentile_cont(0.5) WITHIN GROUP (ORDER BY COALESCE(cp.total_gbp, cp.base_gbp)) AS \"p50_gbp\" FROM job_hunter.comp_points cp JOIN job_hunter.companies c ON c.id = cp.company_id WHERE cp.location_bucket = 'london' AND COALESCE(cp.total_gbp, cp.base_gbp) IS NOT NULL GROUP BY c.display_name ORDER BY \"p50_gbp\" DESC"
}
]
}
],
"refresh": "",