Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
63e7d15
v2.0.0: cleanup release — architecture, telemetry, tenancy, gates
dmichael-fastly Jun 13, 2026
2df55a8
frontend: collapsible sidebar with ⌘B shortcut and tooltip labels
dmichael-fastly Jun 14, 2026
7bb664c
boot: scope FOS-pulled scoring matrix to per-tenant path
dmichael-fastly Jun 14, 2026
97d9166
frontend: move sidebar collapse toggle into the app header
dmichael-fastly Jun 14, 2026
962d84b
scoring: tenant-scoped matrix in deploy_wasm + post-enable publish
dmichael-fastly Jun 14, 2026
cdfbbe4
frontend: server-render share-login flow and admin shell
dmichael-fastly Jun 14, 2026
ccb8aea
frontend: defer heavy chunks and hydrate filter URL pre-render
dmichael-fastly Jun 14, 2026
4fd19e5
docs: cleanup audit + remediation plan for refactor/cleanup
dmichael-fastly Jun 14, 2026
f27aeac
backend: short rebind lock timeout for pool checkouts + surface rebin…
dmichael-fastly Jun 14, 2026
27b2ef9
cron: drop dead try/pass residue from sync jobs
dmichael-fastly Jun 14, 2026
2e2a70e
deps: share source-resolution body via _resolve_source_or_400
dmichael-fastly Jun 14, 2026
60d55fb
repositories: hoist _base imports and fold origin TS into TIME_SERIES
dmichael-fastly Jun 14, 2026
f1df91b
docs: mark PR-1 done in cleanup audit + flag adjacent latent bug
dmichael-fastly Jun 14, 2026
a2b0eb1
utils: adopt iso_z_now / iso_z across stale strftime sites
dmichael-fastly Jun 14, 2026
8b2a8df
tunnel: import iso_z_now directly instead of via share_db re-export
dmichael-fastly Jun 14, 2026
6900c40
utils: delete retry.py — never adopted
dmichael-fastly Jun 14, 2026
7a04d01
utils: delete cdn.py — only its test imported it
dmichael-fastly Jun 14, 2026
1598356
core: inline query_monitor_enabled, delete settings.py + pydantic-set…
dmichael-fastly Jun 14, 2026
47e6efa
docs: mark PR-2 + PR-4 done; note pre-existing concurrency test flake
dmichael-fastly Jun 14, 2026
5d77e67
origin: collapse 6 TEMP_* templates into the live ones (b1/b4)
dmichael-fastly Jun 14, 2026
0c60713
docs: mark PR-3 partial done; record scope landed vs deferred
dmichael-fastly Jun 14, 2026
200b1ad
repositories+routers: share section timings via SectionTimer
dmichael-fastly Jun 14, 2026
c274d21
rollups: lift describe_columns / discover_closed_hours / parse_hour_t…
dmichael-fastly Jun 14, 2026
a9ec6a1
metadata: delete legacy usage_log + migration_003 + metadata.db migra…
dmichael-fastly Jun 14, 2026
57d03d8
docker: cache uv/npm/next via BuildKit cache mounts
dmichael-fastly Jun 14, 2026
d7aa43b
sidebar: persist collapsed state in cookie so SSR paints correct width
dmichael-fastly Jun 14, 2026
10fe196
share: drop tunnel use_tunnel / tunnel_url fields and scrypt passcode…
dmichael-fastly Jun 14, 2026
43858f5
file structure: move lake.py into core/iceberg/, sync_admin_state int…
dmichael-fastly Jun 14, 2026
6908b26
sidebar: extract cookie name to non-client module so SSR can read it
dmichael-fastly Jun 14, 2026
abd7909
core: behavior-change cleanups — promote shared bits, drop fictional …
dmichael-fastly Jun 14, 2026
49a9891
choropleth: drop rafThrottle wrapper that Turbopack mangled
dmichael-fastly Jun 14, 2026
3585bd2
docs: checkpoint Index + execution log for PRs 5/6/7/8/13/14
dmichael-fastly Jun 14, 2026
d3c15df
PR-10 partial: invalidate_service / _purge_surrogate_key / refresh_vi…
dmichael-fastly Jun 14, 2026
c26585c
PR-11 + PR-12 partial: origin ResponseCache + bulk small cleanups
dmichael-fastly Jun 14, 2026
b39638a
filter-value-cell: shared menu replaces hover-only dashboard link
dmichael-fastly Jun 14, 2026
fc16a0d
dashboard: bundle endpoint always fetches top_bots regardless of fiel…
dmichael-fastly Jun 14, 2026
a678e64
sessions: enrich rows with ASN labels; origin: fix oip filter column
dmichael-fastly Jun 14, 2026
aabbc55
sessions: enrich rollup-path rows with ASN labels too
dmichael-fastly Jun 14, 2026
a0361db
core: extract ThreadLocalPool for shared SQLite pool shape
dmichael-fastly Jun 14, 2026
7c63234
filter-cell: cmd/ctrl-click skips the menu and filters this page dire…
dmichael-fastly Jun 14, 2026
51b4203
core: ThreadLocalPool — providers for _initialized / _local
dmichael-fastly Jun 14, 2026
2a801c0
core: migrate usage_log_db to ThreadLocalPool
dmichael-fastly Jun 14, 2026
86dad41
filter-cell: catch cmd-click on both mousedown and click
dmichael-fastly Jun 14, 2026
049648c
telemetry_proxy: fix concurrent-first-caller race in start_proxy_server
dmichael-fastly Jun 14, 2026
39cc04f
reconciliation: route usage_log stats + cleanup through usage_log_db
dmichael-fastly Jun 14, 2026
99b4182
tests: drop stale TLS_FINGERPRINTS pin in favor of FINGERPRINT_TOP_N
dmichael-fastly Jun 14, 2026
935d484
filter-cell: queue cmd-click menu-close so it wins the race vs base-ui
dmichael-fastly Jun 14, 2026
8c8e28e
filter-cell: swallow base-ui's open callback after cmd-click
dmichael-fastly Jun 14, 2026
57a7c85
core: migrate metadata.base to ThreadLocalPool
dmichael-fastly Jun 14, 2026
86c2fa2
core: migrate share_db.connection to ThreadLocalPool
dmichael-fastly Jun 14, 2026
8036f97
origin: fold TEMP_SUMMARY_* into SUMMARY_GROUPING_SETS + cursor.descr…
dmichael-fastly Jun 15, 2026
f571b31
rollups: extract collect_hourly_bundle_paths + run_pending_migrations
dmichael-fastly Jun 15, 2026
85df030
PR-10: finalize_cron_duration / load_service_config / start_or_resume…
dmichael-fastly Jun 15, 2026
b431ef4
perf(share-banner): honor SSR bootstrap seed by re-reading cache at e…
dmichael-fastly Jun 15, 2026
96aecd0
perf(dashboard): kill lazy-then-full /bundle+/aggregates double fetch
dmichael-fastly Jun 15, 2026
bc8b201
perf(security): stop duplicate /api/security/aggregates mount fetch
dmichael-fastly Jun 15, 2026
11825d8
PR-12: 8 deferred audit findings — small extractions across config / …
dmichael-fastly Jun 15, 2026
4e3f307
perf(sessions): hoist 7-day range guard above get_schema_cols
dmichael-fastly Jun 15, 2026
e622d37
perf(sessions): client 7-day guard to skip /api/sessions POST on too-…
dmichael-fastly Jun 15, 2026
55f45f2
perf(admin/queries): adaptive poll cadence 300 ms (active) / 1.5 s (i…
dmichael-fastly Jun 15, 2026
92f95e0
perf(admin/queries): bound in-memory cron history 2000 → 400
dmichael-fastly Jun 15, 2026
6ad7115
perf(admin): drop health-snapshot poll 1 s → 5 s and memoize compacti…
dmichael-fastly Jun 15, 2026
1b2ba01
perf(admin/log-accounting): TTL-cache Fastly Stats fetch inside compu…
dmichael-fastly Jun 15, 2026
d32faee
perf(admin/usage-log): drop COUNT(*) OVER () and read total from rollup
dmichael-fastly Jun 15, 2026
bf53ab1
perf(admin/log-accounting): add Cache-Control max-age=30 to short-cir…
dmichael-fastly Jun 15, 2026
6af6f1c
perf(admin/usage-log): round now to minute bucket to stop cache-key c…
dmichael-fastly Jun 15, 2026
a966d77
perf(alerts): bump /logging-settings staleTime 30s→5min + /api/alerts…
dmichael-fastly Jun 15, 2026
257724e
perf(charts): pass fields filter to /api/dashboard/aggregates
dmichael-fastly Jun 15, 2026
4b9b46c
perf(logs): throttle cron polls (delta 5s→15s, history 5s→30s)
dmichael-fastly Jun 15, 2026
62689cb
perf(network): replace PERCENTILE_CONT with APPROX_QUANTILE on RTT pe…
dmichael-fastly Jun 15, 2026
7d4f919
perf(usage): skip update_iceberg_view on /api/usage/prefill edge_rati…
dmichael-fastly Jun 15, 2026
a7046be
perf(query): add staleTime 5min to /api/schema and /api/presets useQuery
dmichael-fastly Jun 15, 2026
4af991f
perf(security): Cache-Control 30s on /api/security/aggregates
dmichael-fastly Jun 15, 2026
c556b3d
perf(network): short-TTL response cache on /api/network-health
dmichael-fastly Jun 15, 2026
2e29ac3
perf(admin): cache cron_runs terminal-state lookup to silence 422 SEL…
dmichael-fastly Jun 15, 2026
079da13
perf(performance): drop unused latency_ts query and waterfall percent…
dmichael-fastly Jun 15, 2026
22524e7
perf(logs): TTL-cache /api/cron-schedule for 5s
dmichael-fastly Jun 15, 2026
1efc9e7
perf(network): drop unused dt/resp_state from network temp table proj…
dmichael-fastly Jun 15, 2026
7b9f272
perf(sessions): use idBySid map in SessionsTable Flag column
dmichael-fastly Jun 15, 2026
333015c
perf(network): memoize ASN leaderboard tbody render
dmichael-fastly Jun 15, 2026
f511a73
perf(origin): add _section_timings instrumentation to /api/origin/agg…
dmichael-fastly Jun 15, 2026
4e92115
core-13: route fetch_service_name through shared fastly() client
dmichael-fastly Jun 15, 2026
c735b44
docs: CHANGELOG entry for core-13 fetch_service_name route through fa…
dmichael-fastly Jun 15, 2026
58651e4
chore: gitignore per-tenant scoring matrices
dmichael-fastly Jun 15, 2026
74a5ee4
Revert "perf(dashboard): kill lazy-then-full /bundle+/aggregates doub…
dmichael-fastly Jun 15, 2026
609e8c2
perf(sessions): use _cached_listdir in collect_hourly_bundle_paths
dmichael-fastly Jun 15, 2026
1d0bce4
perf(query): keep section_timings in /api/query response for analyst …
dmichael-fastly Jun 15, 2026
5e4780d
perf(logs): drop count(*) precount on /api/cron-runs when since_id is…
dmichael-fastly Jun 15, 2026
292ce08
Revert "perf(sessions): use _cached_listdir in collect_hourly_bundle_…
dmichael-fastly Jun 15, 2026
6df7804
perf(dashboard): kill lazy-then-full /bundle+/aggregates double fetch…
dmichael-fastly Jun 15, 2026
08881a3
perf(usage): drop vestigial DuckDB connection from /api/usage/log-act…
dmichael-fastly Jun 15, 2026
abbb54e
perf(query): default maxRows 10000 → 100
dmichael-fastly Jun 15, 2026
b3a12d1
u6: extract _run_falco_lint subprocess helper
dmichael-fastly Jun 15, 2026
e23acf1
docs: CHANGELOG entry for u6 _run_falco_lint extraction
dmichael-fastly Jun 15, 2026
46418c0
fix(debug-panel): bail out of useEffect setState loop on no-diff updates
dmichael-fastly Jun 15, 2026
865dbda
perf(origin): use precomputed lat_us in _origin_timeseries_from_temp
dmichael-fastly Jun 15, 2026
020a4cd
perf(query): skip df.to_json triple-serialize via arrow.to_pylist
dmichael-fastly Jun 15, 2026
64fc3d1
perf(admin): trim /api/services response to fields the admin UI renders
dmichael-fastly Jun 15, 2026
6e6a5f9
perf(dashboard): seed schema_types into execute_top_n_rollups instead…
dmichael-fastly Jun 15, 2026
0497fe9
chore: ignore .agents directory and track skills-lock.json
dmichael-fastly Jun 15, 2026
fdb7487
chore: remove local browser-use skill in favor of global installation
dmichael-fastly Jun 15, 2026
c25f25d
perf(alerts): skip /api/alerts + /logging-settings for analysts
dmichael-fastly Jun 15, 2026
8296f92
perf(admin/queries): derive SummaryStrip from snapshot to drop second…
dmichael-fastly Jun 15, 2026
9ec3de0
fix(network): handle dict-shaped filters in _response_cache_key
dmichael-fastly Jun 15, 2026
7e22f2b
fix(cron): return len(entries) as total when COUNT(*) precount is ski…
dmichael-fastly Jun 15, 2026
228302c
perf(admin/queries): drop include_completed=true on the Live-only tab
dmichael-fastly Jun 15, 2026
c436f6f
test: align suite with recent perf commits — drop stale pins + isolat…
dmichael-fastly Jun 15, 2026
2eac154
perf(admin/usage-log): switch reader to open_readonly() to decouple f…
dmichael-fastly Jun 15, 2026
864a42b
perf(query): gate Structured-mode first-paint auto-run on user intent
dmichael-fastly Jun 15, 2026
bd6bd51
Reapply "perf(sessions): use _cached_listdir in collect_hourly_bundle…
dmichael-fastly Jun 15, 2026
cf6265d
perf(admin): fold /api/debug/state into bootstrap
dmichael-fastly Jun 15, 2026
a80735b
perf(admin/usage-log): hoist service_id out of per-row into response …
dmichael-fastly Jun 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .check_router_core_floor
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
117
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@
# backend runs on a different host than the frontend.
# NEXT_PUBLIC_API_URL=http://127.0.0.1:8000

# ── Observability ──────────────────────────────────────────────────────────────
# OpenTelemetry exporter. Default 'none' — no spans/metrics leave the process.
# Set 'console' to dump spans and 60s metric snapshots to stdout (loud; useful
# locally when chasing a perf regression). Don't set 'console' in prod — it
# pollutes log aggregation with ~1 MB/min of JSON.
# OTEL_EXPORTER=console

# ── Docker only ────────────────────────────────────────────────────────────────
# Set automatically by docker-compose; not needed for local dev.
# API_PROXY_URL=http://backend:8000
62 changes: 55 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@ jobs:
- name: Format check (ruff)
run: uv run ruff format --check .

- name: Type check (mypy)
run: uv run mypy backend/
- name: Type check (mypy, filtered through mypy-baseline)
# Pre-existing errors accepted via mypy-baseline.txt; the filter
# exits non-zero only on NET-NEW errors. Refresh the baseline after
# a burndown PR with
# uv run mypy backend/ 2>&1 | uv run mypy-baseline sync
# and commit mypy-baseline.txt. Burndown plan +
# bucket scoping live in
# pending-docs/session_2026-06-10_otel_dump_and_log_extents.md.
run: uv run mypy backend/ 2>&1 | uv run mypy-baseline filter

- name: Install falco
run: |
Expand Down Expand Up @@ -85,19 +92,50 @@ jobs:
env:
FALCO_REQUIRED: "1"
TERRAFORM_VALIDATE: "1"
# Gate ratcheted as milestones land:
# Gate ratcheted as milestones land (convention: current actual − 2pp):
# end Milestone A: 44% (baseline 46%, -2pp buffer)
# end Milestone E: 47% (current 49% — keeps the 2pp buffer)
# post-Milestone E coverage backfill: 55% (current 59% — 4pp buffer)
# confidence-batch (insights+admin+services+dashboard+origin+
# hypothesis+regression+E2E smoke): 78% (current 83% — 5pp buffer)
# post live-query-monitor (2026-06-11): 80% (current 82%)
# post backend coverage waves (reconciliation/compaction/session_scoring
# /data_migrations/tunnel-state/dashboard-router/views/sqlite_profiler):
# 82% (current 83% — 1pp buffer; tight while v2.0 target 85% lands).
# v2.0 final wave (2026-06-12): per-module tests for the post-split
# rollups/ + admin/ packages (rollups/sessions 85, rollups/time_series
# 84, rollups/day_bundles 76, rollups/recompute 96, admin/compaction
# 100, admin/health 100): 85% (current 85% — the v2.0 target hit).
#
# `-n auto` parallelizes via pytest-xdist (TESTING_PLAN_3 item 21).
# Verified safe: per-service SQLite (`{id}.metadata.db`) + per-test
# tmp_path give file isolation; autouse `_reset_module_caches` resets
# the 8 module-level caches between tests; moto fixtures are per-test.
# Local run: 2268 passed in 58s under `-n auto` vs ~3min serial.
run: uv run pytest -n auto --cov=backend --cov-report=term --cov-fail-under=78
run: uv run pytest -n auto --cov=backend --cov-report=term --cov-fail-under=85

- name: Security-regression count gate
# v2.0 cleanup Phase 0.8: asserts the
# @pytest.mark.security_regression count never drops below the
# baseline floor (24 — derived from audit-findings/ verified
# fixes). A refactor cannot silently delete coverage of a
# verified fix without surfacing the change.
run: bash scripts/check_security_regression_count.sh

- name: Emit perf samples (CI-scale synthetic load)
# Produces tests/perf/latest.json from a 100K-row in-memory
# DuckDB dataset (~2 s wall). The gate below compares to
# tests/perf/baseline.json and fails on >regression_pct_threshold%
# over baseline (50 % default; tuned for GH Actions runner
# variance at CI scale).
run: uv run python scripts/emit_perf_latest.py

- name: Perf gate (load-harness baseline)
# Compares the just-emitted latest.json against baseline.json.
# Production targets (≤2800 / ≤1900 ms) are documented in
# baseline.json's production_targets_comment for traceability
# but enforced by the manual loadtest probe, not this CI gate.
run: bash scripts/perf_gate.sh

frontend:
name: Frontend (Node)
Expand Down Expand Up @@ -140,7 +178,17 @@ jobs:
run: npx tsc --noEmit

- name: Tests (vitest with coverage)
# Gate ratcheted as milestones land:
# Gate ratcheted as milestones land (convention: current actual − 2pp):
# end Milestone A: 40% (baseline 42.7%, -2pp buffer)
# end Milestone E: 44% (current 46.55% — keeps the 2pp buffer)
run: npx vitest run --coverage --coverage.thresholds.lines=44
# end Milestone E: 44% (current 46.55%)
# post live-query-monitor (2026-06-11): 53% (current 55.19%)
# post lib/toast + lib/api/custom-fields + lib/workers/parseJson tests
# (2026-06-12): 55% (current 57.12%)
# post ProvisionWizard/wizard-config-helpers tests
# (2026-06-12): 56% (current 58.42%)
# post ProvisionWizard/wizard-api tests
# (2026-06-12): 57% (current 59.8%)
# post ProvisionWizard/wizard-deploy tests
# (2026-06-12): 58% (current 61.66%) — final v2.0 target hit
# per cleanup_plan §10.14.
run: npx vitest run --coverage --coverage.thresholds.lines=58
53 changes: 53 additions & 0 deletions .github/workflows/cidr-refresh.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Refresh Fastly CIDRs

# Weekly refresh of the Fastly edge CIDR list in the repo-root Caddyfile.
# The @from_fastly_v4 matcher gates X-Forwarded-For rewriting on Fastly's
# published v4 ranges; a stale list silently classifies traffic from new
# POPs as direct (untrusted) until somebody refreshes it and reloads
# Caddy. The script is well-tested (scripts/refresh_fastly_cidrs.py);
# this workflow just runs it on a cadence and opens a PR if the file
# changed. Off-minute schedule on purpose so the runner pool isn't
# hammered at :00 alongside everybody else's hourly jobs.

on:
schedule:
- cron: '13 9 * * 1' # Mondays at 09:13 UTC
workflow_dispatch: {}

permissions:
contents: write
pull-requests: write

jobs:
refresh:
name: Fetch + open PR on diff
runs-on: forge-amd64-medium
steps:
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
python-version: "3.13"

- name: Refresh Caddyfile
# No-op if the published list already matches what's in the
# Caddyfile (script prints "No changes …" and exits 0). Writes
# the updated matcher block otherwise; peter-evans/create-pull-
# request below only opens a PR when the working tree is dirty.
run: uv run python scripts/refresh_fastly_cidrs.py

- name: Open PR if Caddyfile changed
uses: peter-evans/create-pull-request@v7
with:
commit-message: 'chore: refresh Fastly edge CIDR list in Caddyfile'
branch: chore/refresh-fastly-cidrs
delete-branch: true
title: 'chore: refresh Fastly edge CIDR list'
body: |
Automated update from `scripts/refresh_fastly_cidrs.py`, triggered by the weekly `cidr-refresh.yml` workflow.

The `@from_fastly_v4` matcher in [Caddyfile](../blob/main/Caddyfile) gates the `X-Forwarded-For` rewrite on Fastly-published edge ranges. A stale list silently classifies traffic from new POPs as direct (untrusted) until Caddy reloads.

After merge: run `~/restart.sh caddy` (or equivalent) on the VM to pick up the new ranges.
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ tests/fixtures/scoring/
# scripts/scoring/train.py against a fresh trace extract.
compute/scorer/matrix.json

# Per-tenant matrices pulled from FOS on every backend startup
# (see backend/main.py:_ensure_scoring_matrix). matrix.default.json
# stays tracked — that's the in-repo fallback the scoring endpoint
# uses when neither the shared matrix.json nor a tenant matrix exists.
compute/scorer/matrix_*.json

# Rust build artifacts.
compute/scorer/target/
compute/scorer/bin/
Expand All @@ -76,6 +82,11 @@ compute/scorer/pkg/
# split_per_page.py) live here for now; treat the whole tree as throwaway.
/scratch/

# Performance-audit campaign artifacts: HAR captures, per-sample telemetry,
# aggregated p50/p95/p99 summaries, per-page reports + improvement plans.
# Throwaway — regenerable by re-running scratch/perf_audit.mjs.
/performance-report/

# Local-only VS Code config (file-watcher / Pylance excludes for the
# regenerating .next + cache trees). Personal to each contributor's editor
# setup — not promoted to the repo by default.
Expand Down
47 changes: 39 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
repos:
# Pinned ruff version must stay reasonably close to the version in
# pyproject.toml (currently ruff>=0.11) — drift triggers pre-existing
# rule changes (UP038, E731 strictness) that the project's actual ruff
# has already retired. Bump together when bumping either side.
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.0
rev: v0.15.15
hooks:
- id: ruff
args: [--fix]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.15.0
# mypy runs via the project's own uv env (matches what CI runs) and is
# piped through mypy-baseline so pre-existing errors stay accepted and
# only NET-NEW errors fail the commit. The baseline lives in
# mypy-baseline.txt at the repo root; refresh it after a burndown PR with
# uv run mypy backend/ 2>&1 | uv run mypy-baseline sync
# and commit the updated file. Burndown plan in
# pending-docs/session_2026-06-10_otel_dump_and_log_extents.md.
- repo: local
hooks:
- id: mypy
additional_dependencies:
- types-boto3
- types-pytz
- fastapi
- pydantic
name: mypy (full backend/, filtered through mypy-baseline)
language: system
# Always check the whole backend/ tree, not just changed files —
# per-file mypy only visits a partial import graph, which makes
# mypy-baseline report unrelated baseline entries as "fixed" and
# exit non-zero. Cost: ~10s per commit; benefit: matches CI exactly.
entry: bash -c 'uv run mypy backend/ 2>&1 | uv run mypy-baseline filter'
files: '^backend/.*\.py$'
pass_filenames: false

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
# openapi-typescript emits openapi.json without a trailing newline;
# end-of-file-fixer adds one, then the next regen-openapi run
# strips it. Excluding the generated artifact breaks the cycle.
exclude: '^frontend/openapi\.json$'
- id: check-yaml
- id: check-json
- id: check-merge-conflict
Expand Down Expand Up @@ -60,3 +78,16 @@ repos:
language: system
pass_filenames: false
entry: bash -c 'cd frontend && npx tsc --noEmit'

# v2.0 cleanup (Phase 0.12): pre-push gate that the
# @pytest.mark.security_regression count hasn't dropped below
# the Phase 0 floor (24). Catches a refactor that silently
# removes coverage of a verified security fix before push,
# not in CI. `stages: [pre-push]` keeps it off the per-commit
# hot path (the gate takes ~2s to collect 3k+ tests).
- id: security-regression-count
name: Assert security_regression test count >= floor
stages: [pre-push]
language: system
pass_filenames: false
entry: bash scripts/check_security_regression_count.sh
Loading