feat: add Spotlight dev overlay opt-in and fallow audit config#11580
feat: add Spotlight dev overlay opt-in and fallow audit config#11580DrJKL wants to merge 5 commits into
Conversation
Enable Sentry Spotlight (https://spotlightjs.com) as an opt-in local dev overlay by adding a SPOTLIGHT=true env flag that routes events to a local sidecar via spotlightBrowserIntegration. Production wiring is unchanged. Add a new warn-only 'CI: Fallow Audit' GitHub Actions workflow plus a .fallowrc.json to surface dead code, duplication, complexity, and boundary-violation signals from fallow as PR annotations without blocking merges. Rule levels are set to 'warn' so the team can evaluate signal quality before considering any promotion to a gate. Context: follow-up on Syntax.fm #998 tooling review in #frontend-here-be-dragons.
- main.ts: add browserTracingIntegration + consoleLoggingIntegration +
enableLogs so Spotlight actually shows the traces/logs CONTRIBUTING.md
advertises, not just errors.
- ci-fallow-audit.yaml: switch from branches-ignore (which filters the
PR base, not source) to an if-guard on github.head_ref so wip/draft/
temp branches really skip the audit.
- .fallowrc.json: drop **/build/** from ignorePatterns since
build/**/*.{ts,mts,js} is an entry pattern for the Vite plugin sources
and the two were cancelling out.
The GitHub App used for this session lacks the workflows permission, so it cannot push files under .github/workflows/. The YAML is reproduced verbatim in the PR description so a maintainer can add it in a follow-up commit. All other pieces of this PR (Spotlight wiring, .fallowrc.json, docs) are complete.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds an opt‑in Sentry “Spotlight” local dev overlay: build-time flag, runtime/global declarations, test shim, docs and env guide, analyzer config, and Sentry initialization changes to route events to a local Spotlight sidecar when enabled in non-cloud development. Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant App as Frontend App (Sentry SDK)
participant Sidecar as Spotlight Sidecar (local)
participant UI as Spotlight Local UI
Dev->>Sidecar: start spotlight sidecar (listens on local DSN)
Dev->>App: set SPOTLIGHT=true in dev env
Note right of App: build-time __SPOTLIGHT_ENABLED__ = true
App->>App: Sentry.init reads __SPOTLIGHT_ENABLED__
App->>Sidecar: send captured events/traces (local DSN)
Sidecar->>UI: store/forward events
Dev->>UI: open Spotlight UI to inspect events
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 7✅ Passed checks (7 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Review rate limit: 4/5 reviews remaining, refill in 12 minutes. Comment |
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 1441 passed, 0 failed · 2 flaky📊 Browser Reports
|
📦 Bundle: 5.27 MB gzip 🔴 +37 kBDetailsSummary
Category Glance App Entry Points — 22.9 kB (baseline 22.5 kB) • 🔴 +361 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.24 MB (baseline 1.24 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 77.7 kB (baseline 77.7 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 2 unchanged Panels & Settings — 488 kB (baseline 488 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 11 unchanged User & Accounts — 17.5 kB (baseline 17.5 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 5 added / 5 removed / 2 unchanged Editors & Dialogs — 112 kB (baseline 112 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 4 added / 4 removed UI Components — 61 kB (baseline 61 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 3.04 MB (baseline 3.04 MB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 4 unchanged Utilities & Hooks — 364 kB (baseline 364 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 13 added / 13 removed / 18 unchanged Vendor & Third-Party — 10 MB (baseline 9.87 MB) • 🔴 +149 kBExternal libraries and shared vendor chunks
Status: 1 added / 1 removed / 15 unchanged Other — 8.84 MB (baseline 8.84 MB) • ⚪ 0 BBundles that do not match a named category
Status: 57 added / 57 removed / 78 unchanged ⚡ Performance Report
No regressions detected. All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-05-01T09:00:12.653Z",
"gitSha": "ec074a05d0aafb31ebb5000152ec4e3ba7af64f6",
"branch": "glary/syntax-998-spotlight-fallow",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2045.6100000000106,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.9869999999999965,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 330.2440000000001,
"heapDeltaBytes": 23119916,
"heapUsedBytes": 72427200,
"domNodes": 16,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 17.419999999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2009.6360000000004,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.516,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 304.417,
"heapDeltaBytes": 23383872,
"heapUsedBytes": 72018516,
"domNodes": 20,
"jsHeapTotalBytes": 15466496,
"scriptDurationMs": 15.231,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 1989.736999999991,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.104000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 291.13100000000003,
"heapDeltaBytes": 22715132,
"heapUsedBytes": 71020316,
"domNodes": 18,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 13.813,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1691.4560000000165,
"styleRecalcs": 72,
"styleRecalcDurationMs": 29.241000000000003,
"layouts": 12,
"layoutDurationMs": 3.1029999999999998,
"taskDurationMs": 618.535,
"heapDeltaBytes": 18211356,
"heapUsedBytes": 66132088,
"domNodes": 55,
"jsHeapTotalBytes": 15990784,
"scriptDurationMs": 99.358,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2201.5140000000883,
"styleRecalcs": 69,
"styleRecalcDurationMs": 32.564,
"layouts": 18,
"layoutDurationMs": 3.498,
"taskDurationMs": 611.899,
"heapDeltaBytes": 8072140,
"heapUsedBytes": 56476812,
"domNodes": -211,
"jsHeapTotalBytes": 16379904,
"scriptDurationMs": 89.17999999999999,
"eventListeners": -122,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1695.920000000001,
"styleRecalcs": 71,
"styleRecalcDurationMs": 27.583000000000002,
"layouts": 12,
"layoutDurationMs": 2.9949999999999997,
"taskDurationMs": 640.924,
"heapDeltaBytes": -3660300,
"heapUsedBytes": 44753360,
"domNodes": -262,
"jsHeapTotalBytes": 15855616,
"scriptDurationMs": 97.36800000000001,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1718.498000000011,
"styleRecalcs": 32,
"styleRecalcDurationMs": 13.558,
"layouts": 6,
"layoutDurationMs": 0.5660000000000001,
"taskDurationMs": 244.58,
"heapDeltaBytes": 542348,
"heapUsedBytes": 49530268,
"domNodes": 79,
"jsHeapTotalBytes": 15466496,
"scriptDurationMs": 20.569000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1721.053999999981,
"styleRecalcs": 33,
"styleRecalcDurationMs": 13.526000000000002,
"layouts": 6,
"layoutDurationMs": 0.544,
"taskDurationMs": 231.741,
"heapDeltaBytes": -71944,
"heapUsedBytes": 48252256,
"domNodes": 79,
"jsHeapTotalBytes": 14680064,
"scriptDurationMs": 15.632,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.669999999999998,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1717.3510000000078,
"styleRecalcs": 32,
"styleRecalcDurationMs": 16.247999999999998,
"layouts": 6,
"layoutDurationMs": 0.5450000000000002,
"taskDurationMs": 293.49,
"heapDeltaBytes": 209264,
"heapUsedBytes": 48024240,
"domNodes": 79,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 27.749999999999996,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "dom-widget-clipping",
"durationMs": 476.22200000000703,
"styleRecalcs": 12,
"styleRecalcDurationMs": 6.694999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 281.09000000000003,
"heapDeltaBytes": 9035420,
"heapUsedBytes": 57292364,
"domNodes": 19,
"jsHeapTotalBytes": 15466496,
"scriptDurationMs": 46.345000000000006,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "dom-widget-clipping",
"durationMs": 422.4490000000287,
"styleRecalcs": 13,
"styleRecalcDurationMs": 7.377,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 266.748,
"heapDeltaBytes": 9312232,
"heapUsedBytes": 58469056,
"domNodes": 21,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 47.67900000000001,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "dom-widget-clipping",
"durationMs": 471.4410000000271,
"styleRecalcs": 13,
"styleRecalcDurationMs": 8.196,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 284.106,
"heapDeltaBytes": 9082052,
"heapUsedBytes": 56832908,
"domNodes": 20,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 51.225,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2014.5570000000248,
"styleRecalcs": 11,
"styleRecalcDurationMs": 6.816000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 420.76199999999994,
"heapDeltaBytes": 5293384,
"heapUsedBytes": 62316000,
"domNodes": -256,
"jsHeapTotalBytes": -757760,
"scriptDurationMs": 67.885,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-idle",
"durationMs": 1998.6450000000104,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.198999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 419.527,
"heapDeltaBytes": 1746432,
"heapUsedBytes": 60427852,
"domNodes": -261,
"jsHeapTotalBytes": 5009408,
"scriptDurationMs": 65.71499999999999,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 1999.9099999999999,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.841,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 438.47200000000004,
"heapDeltaBytes": 2271556,
"heapUsedBytes": 60462276,
"domNodes": -259,
"jsHeapTotalBytes": 5271552,
"scriptDurationMs": 68.37,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2085.5150000000435,
"styleRecalcs": 69,
"styleRecalcDurationMs": 16.924000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 846.8930000000001,
"heapDeltaBytes": 26255476,
"heapUsedBytes": 84034728,
"domNodes": -257,
"jsHeapTotalBytes": 3379200,
"scriptDurationMs": 308.20399999999995,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2071.2859999999864,
"styleRecalcs": 68,
"styleRecalcDurationMs": 15.946000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 838.0989999999999,
"heapDeltaBytes": 23069132,
"heapUsedBytes": 82336600,
"domNodes": -260,
"jsHeapTotalBytes": 6000640,
"scriptDurationMs": 296.967,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2089.9429999999484,
"styleRecalcs": 67,
"styleRecalcDurationMs": 16.615000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 991.957,
"heapDeltaBytes": -642108,
"heapUsedBytes": 63031556,
"domNodes": -265,
"jsHeapTotalBytes": 7548928,
"scriptDurationMs": 384.2829999999999,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3089.0249999999924,
"styleRecalcs": 66,
"styleRecalcDurationMs": 17.075999999999997,
"layouts": 60,
"layoutDurationMs": 7.116999999999999,
"taskDurationMs": 1052.74,
"heapDeltaBytes": 15442600,
"heapUsedBytes": 75813508,
"domNodes": -263,
"jsHeapTotalBytes": -233472,
"scriptDurationMs": 384.683,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3053.3349999999473,
"styleRecalcs": 66,
"styleRecalcDurationMs": 16.879,
"layouts": 60,
"layoutDurationMs": 6.789000000000001,
"taskDurationMs": 1035.8839999999998,
"heapDeltaBytes": 8420468,
"heapUsedBytes": 69027836,
"domNodes": -263,
"jsHeapTotalBytes": 5533696,
"scriptDurationMs": 377.43899999999996,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3121.4610000000675,
"styleRecalcs": 67,
"styleRecalcDurationMs": 19.150999999999996,
"layouts": 60,
"layoutDurationMs": 7.5760000000000005,
"taskDurationMs": 1124.4990000000003,
"heapDeltaBytes": 15258368,
"heapUsedBytes": 75861300,
"domNodes": -262,
"jsHeapTotalBytes": -233472,
"scriptDurationMs": 414.39400000000006,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "minimap-idle",
"durationMs": 2016.313000000025,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.690999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 435.502,
"heapDeltaBytes": 2860276,
"heapUsedBytes": 62951548,
"domNodes": -261,
"jsHeapTotalBytes": 552960,
"scriptDurationMs": 65.95,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "minimap-idle",
"durationMs": 2022.4279999999908,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.571000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 429.70900000000006,
"heapDeltaBytes": 12961388,
"heapUsedBytes": 70990520,
"domNodes": -259,
"jsHeapTotalBytes": 28672,
"scriptDurationMs": 74.99600000000001,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2020.5809999999929,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.106000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 524.414,
"heapDeltaBytes": 10606400,
"heapUsedBytes": 70454728,
"domNodes": -261,
"jsHeapTotalBytes": -495616,
"scriptDurationMs": 86.503,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 454.00699999999006,
"styleRecalcs": 48,
"styleRecalcDurationMs": 10.544999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 277.037,
"heapDeltaBytes": 8840340,
"heapUsedBytes": 57523400,
"domNodes": 22,
"jsHeapTotalBytes": 16515072,
"scriptDurationMs": 94.403,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 441.26700000003893,
"styleRecalcs": 48,
"styleRecalcDurationMs": 9.886,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 275.358,
"heapDeltaBytes": 9917692,
"heapUsedBytes": 59121536,
"domNodes": 22,
"jsHeapTotalBytes": 15466496,
"scriptDurationMs": 95.522,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 487.7129999999852,
"styleRecalcs": 48,
"styleRecalcDurationMs": 10.417000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 291.637,
"heapDeltaBytes": 9182012,
"heapUsedBytes": 57969660,
"domNodes": 20,
"jsHeapTotalBytes": 15728640,
"scriptDurationMs": 98.646,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-idle",
"durationMs": 1998.3760000000075,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.2540000000000004,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 278.90000000000003,
"heapDeltaBytes": 1234252,
"heapUsedBytes": 69854244,
"domNodes": 17,
"jsHeapTotalBytes": 17920000,
"scriptDurationMs": 13.259999999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2008.2660000000487,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.746,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 265.164,
"heapDeltaBytes": 22514504,
"heapUsedBytes": 71236864,
"domNodes": 20,
"jsHeapTotalBytes": 14417920,
"scriptDurationMs": 10.757000000000001,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2007.9010000000608,
"styleRecalcs": 11,
"styleRecalcDurationMs": 14.33,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 289.43700000000007,
"heapDeltaBytes": 22505396,
"heapUsedBytes": 71329216,
"domNodes": 21,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 14.540999999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1712.4979999999823,
"styleRecalcs": 76,
"styleRecalcDurationMs": 32.457,
"layouts": 16,
"layoutDurationMs": 3.879,
"taskDurationMs": 544.702,
"heapDeltaBytes": 13985872,
"heapUsedBytes": 62631952,
"domNodes": 62,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 77.248,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1686.2200000000485,
"styleRecalcs": 77,
"styleRecalcDurationMs": 29.441000000000003,
"layouts": 16,
"layoutDurationMs": 3.5700000000000003,
"taskDurationMs": 508.05699999999996,
"heapDeltaBytes": 14386440,
"heapUsedBytes": 62980608,
"domNodes": 64,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 74.517,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1701.8829999999525,
"styleRecalcs": 78,
"styleRecalcDurationMs": 32.601000000000006,
"layouts": 16,
"layoutDurationMs": 3.973,
"taskDurationMs": 603.423,
"heapDeltaBytes": -4092100,
"heapUsedBytes": 43798428,
"domNodes": -256,
"jsHeapTotalBytes": 14544896,
"scriptDurationMs": 83.02499999999999,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8132.661999999982,
"styleRecalcs": 250,
"styleRecalcDurationMs": 51.793000000000006,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3006.641,
"heapDeltaBytes": 16901864,
"heapUsedBytes": 75064616,
"domNodes": -256,
"jsHeapTotalBytes": 5214208,
"scriptDurationMs": 1024.1749999999997,
"eventListeners": -109,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8132.632999999942,
"styleRecalcs": 250,
"styleRecalcDurationMs": 50.315000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 2901.181,
"heapDeltaBytes": 11515352,
"heapUsedBytes": 67785332,
"domNodes": -257,
"jsHeapTotalBytes": 7311360,
"scriptDurationMs": 975.4549999999999,
"eventListeners": -111,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8133.4869999999455,
"styleRecalcs": 250,
"styleRecalcDurationMs": 53.91599999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3244.071,
"heapDeltaBytes": 12389632,
"heapUsedBytes": 70358512,
"domNodes": -259,
"jsHeapTotalBytes": 7049216,
"scriptDurationMs": 1012.3009999999999,
"eventListeners": -109,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "vue-large-graph-idle",
"durationMs": 10942.64800000002,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 10905.905,
"heapDeltaBytes": -34703092,
"heapUsedBytes": 172159296,
"domNodes": -8331,
"jsHeapTotalBytes": 24440832,
"scriptDurationMs": 501.205,
"eventListeners": -16464,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 10967.660000000024,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 10931.528999999999,
"heapDeltaBytes": -26594136,
"heapUsedBytes": 171929124,
"domNodes": -8336,
"jsHeapTotalBytes": 25751552,
"scriptDurationMs": 511.311,
"eventListeners": -16466,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 11263.811000000032,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11240.199,
"heapDeltaBytes": -41124316,
"heapUsedBytes": 172316316,
"domNodes": -8331,
"jsHeapTotalBytes": 26800128,
"scriptDurationMs": 528.278,
"eventListeners": -16464,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 12905.654999999968,
"styleRecalcs": 65,
"styleRecalcDurationMs": 17.072000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12878.265,
"heapDeltaBytes": -32909212,
"heapUsedBytes": 165258616,
"domNodes": -8331,
"jsHeapTotalBytes": -4132864,
"scriptDurationMs": 735.562,
"eventListeners": -16460,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 13066.508999999996,
"styleRecalcs": 65,
"styleRecalcDurationMs": 17.052999999999983,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13044.346000000001,
"heapDeltaBytes": -43115612,
"heapUsedBytes": 153329376,
"domNodes": -8339,
"jsHeapTotalBytes": -2297856,
"scriptDurationMs": 753.332,
"eventListeners": -16488,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 12801.870000000008,
"styleRecalcs": 65,
"styleRecalcDurationMs": 16.887999999999987,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12775.414,
"heapDeltaBytes": -19132616,
"heapUsedBytes": 176658616,
"domNodes": -8331,
"jsHeapTotalBytes": -11296768,
"scriptDurationMs": 757.5099999999999,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666763,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 439.82200000004923,
"styleRecalcs": 19,
"styleRecalcDurationMs": 21.721000000000004,
"layouts": 4,
"layoutDurationMs": 1.1440000000000001,
"taskDurationMs": 101.039,
"heapDeltaBytes": 5277268,
"heapUsedBytes": 56490340,
"domNodes": 168,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 19.744999999999997,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "workflow-execution",
"durationMs": 441.6840000000093,
"styleRecalcs": 19,
"styleRecalcDurationMs": 22.403,
"layouts": 4,
"layoutDurationMs": 0.9119999999999998,
"taskDurationMs": 99.248,
"heapDeltaBytes": 5044920,
"heapUsedBytes": 55078140,
"domNodes": 157,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 18.768,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "workflow-execution",
"durationMs": 445.96999999998843,
"styleRecalcs": 18,
"styleRecalcDurationMs": 24.744000000000003,
"layouts": 4,
"layoutDurationMs": 1.099,
"taskDurationMs": 114.05699999999999,
"heapDeltaBytes": 5154156,
"heapUsedBytes": 55707292,
"domNodes": 156,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 24.473000000000003,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000273
}
]
} |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/main.ts (1)
62-101: Add a focused test matrix for Spotlight Sentry config.Please add unit coverage for Spotlight on/off with/without DSN to lock in
dsn,enabled, andtracesSampleRatebehavior.Based on learnings: "Applies to src/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.ts` around lines 62 - 101, Add unit tests that exercise the Sentry.init config in main.ts for the four combinations of __SPOTLIGHT_ENABLED__ true/false and sentryDsn present/empty; for each combination assert the resulting config's dsn, enabled, and tracesSampleRate values match expected behavior (when __SPOTLIGHT_ENABLED__ && !sentryDsn -> dsn === 'https://spotlight@local/0', enabled === true; when sentryDsn provided -> dsn === sentryDsn; enabled should be __SENTRY_ENABLED__ || __SPOTLIGHT_ENABLED__; tracesSampleRate should be 1.0 when isCloud || __SPOTLIGHT_ENABLED__ else 0), by mocking or spying on Sentry.init to capture its argument and using test cases that toggle __SPOTLIGHT_ENABLED__, sentryDsn, and isCloud to validate Sentry.init was called with correct properties.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main.ts`:
- Around line 66-69: The dsn assignment currently uses the placeholder only when
__SPOTLIGHT_ENABLED__ && !sentryDsn, which still allows a real sentryDsn to be
used when spotlight is enabled; change the dsn logic so that whenever
__SPOTLIGHT_ENABLED__ is true it always returns the local placeholder
('https://spotlight@local/0') regardless of sentryDsn. Locate the dsn property
assignment in src/main.ts and update the condition to check
__SPOTLIGHT_ENABLED__ alone (using the symbols dsn, __SPOTLIGHT_ENABLED__, and
sentryDsn) so Spotlight mode forces the local DSN always.
---
Nitpick comments:
In `@src/main.ts`:
- Around line 62-101: Add unit tests that exercise the Sentry.init config in
main.ts for the four combinations of __SPOTLIGHT_ENABLED__ true/false and
sentryDsn present/empty; for each combination assert the resulting config's dsn,
enabled, and tracesSampleRate values match expected behavior (when
__SPOTLIGHT_ENABLED__ && !sentryDsn -> dsn === 'https://spotlight@local/0',
enabled === true; when sentryDsn provided -> dsn === sentryDsn; enabled should
be __SENTRY_ENABLED__ || __SPOTLIGHT_ENABLED__; tracesSampleRate should be 1.0
when isCloud || __SPOTLIGHT_ENABLED__ else 0), by mocking or spying on
Sentry.init to capture its argument and using test cases that toggle
__SPOTLIGHT_ENABLED__, sentryDsn, and isCloud to validate Sentry.init was called
with correct properties.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f734d1ba-c4ce-46d2-9f40-d9ae021f8198
📒 Files selected for processing (8)
.env_example.fallowrc.jsonCONTRIBUTING.mdglobal.d.tsscripts/vite-define-shim.tssrc/main.tsvite.config.mtsvitest.setup.ts
| dsn: | ||
| __SPOTLIGHT_ENABLED__ && !sentryDsn | ||
| ? 'https://spotlight@local/0' | ||
| : sentryDsn, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify current DSN branching for Spotlight mode.
rg -n -C2 "__SPOTLIGHT_ENABLED__ && !sentryDsn|spotlight@local/0|dsn:" src/main.tsRepository: Comfy-Org/ComfyUI_frontend
Length of output: 400
🏁 Script executed:
# Check for Spotlight-related setup and any tests
rg -n "Spotlight|spotlight" src/main.ts | head -20Repository: Comfy-Org/ComfyUI_frontend
Length of output: 325
🏁 Script executed:
# Look for related test files or configuration references
fd -t f "test|spec" --path "src" | xargs grep -l "Spotlight\|__SPOTLIGHT" 2>/dev/null || echo "No tests found with Spotlight references"Repository: Comfy-Org/ComfyUI_frontend
Length of output: 361
🏁 Script executed:
# Check where __SPOTLIGHT_ENABLED__ is defined to understand its purpose
rg -n "__SPOTLIGHT_ENABLED__" src/ --type ts -B 2 -A 2Repository: Comfy-Org/ComfyUI_frontend
Length of output: 948
🏁 Script executed:
# Search for any Spotlight integration or sidecar-related configuration
rg -n "sidecar|spotlight@local" src/ --type ts -B 3 -A 3Repository: Comfy-Org/ComfyUI_frontend
Length of output: 861
🏁 Script executed:
# Check the full Sentry initialization block to understand the complete intent
cat -n src/main.ts | sed -n '59,100p'Repository: Comfy-Org/ComfyUI_frontend
Length of output: 1947
🏁 Script executed:
# Look for environment variable definitions to understand when sentryDsn is set
rg -n "sentryDsn|__SPOTLIGHT_ENABLED__|__SENTRY_ENABLED__" src/ --type ts | grep -E "=|const|let|var" | head -20Repository: Comfy-Org/ComfyUI_frontend
Length of output: 110
🏁 Script executed:
# Search for any documentation or comments about Spotlight DSN behavior
rg -n -B 5 -A 5 "spotlight@local" src/Repository: Comfy-Org/ComfyUI_frontend
Length of output: 677
🏁 Script executed:
# Get the full sentryDsn definition and surrounding context
cat -n src/main.ts | sed -n '50,60p'Repository: Comfy-Org/ComfyUI_frontend
Length of output: 534
🏁 Script executed:
# Search web documentation for Spotlight behavior
web_search
Sentry Spotlight integration local events sidecar redirect DSNRepository: Comfy-Org/ComfyUI_frontend
Length of output: 165
🌐 Web query:
Sentry Spotlight integration local sidecar event redirection how it works
💡 Result:
Sentry Spotlight is a local development tool that uses a Sidecar server for real-time debugging of errors, traces, logs, and performance data without sending to production Sentry servers. How the local Sidecar event redirection integration works: 1. Run the Spotlight Sidecar: Use npx @spotlightjs/spotlight (starts on http://localhost:8969 by default), desktop app, Docker, or CLI. The Sidecar is a lightweight local proxy server. 2. Configure Sentry SDK: Enable via environment variable SENTRY_SPOTLIGHT=1 (auto-detects default URL) or explicitly set spotlight: true or spotlight: "http://localhost:8969/stream" in SDK init. Supports most SDKs (Node.js, Python, Java, Ruby, PHP, browser, etc.). 3. Event Redirection/Duplication in SDK: - SDKs implement a separate SpotlightTransport or pipeline that clones every envelope (errors, transactions, sessions, profiles, etc.) before sending to the production Sentry DSN. - Clone ensures original flow unaffected. - Forces 100% sample rate, enables PII, bypasses sampling/rate limits for Spotlight. - Sends cloned envelope via HTTP POST to Sidecar's /stream endpoint (same semantics as Sentry server). - Non-blocking: Failures (e.g., sidecar down) use exponential backoff retries then silently drop; normal Sentry sending continues unaffected. 4. Sidecar Processing: - Receives envelopes via HTTP POST. - Validates, stores in memory. - Streams real-time to connected clients via Server-Sent Events (SSE) at /stream. - Provides APIs for querying history (e.g., for MCP/AI tools). 5. UI/Consumption: View in browser UI (localhost:8969), desktop app, spotlight tail CLI for terminal streaming, or MCP server for AI integration. This "redirection" is actually duplication: events go to both production Sentry (if DSN set) and local Sidecar simultaneously for dev debugging. Diagram: Your Application → Sentry SDK → [Clone] → Sidecar (localhost:8969/stream) → [SSE] → UI/CLI/MCP ↓ Production Sentry (unaffected)
Citations:
- 1: https://spotlightjs.com/docs/sidecar/
- 2: https://develop.sentry.dev/sdk/foundations/client/integrations/spotlight/
- 3: https://spotlightjs.com/docs/architecture/
- 4: https://docs.sentry.io/platforms/java/guides/spring-boot/integrations/spotlight/
- 5: https://spotlightjs.com/docs/getting-started/
- 6: fix(spotlight): align behavior with SDK spec getsentry/sentry-python#5169
- 7: Spotlight Sidecar support in Sentry-Ruby getsentry/sentry-ruby#2175
🌐 Web query:
Spotlight.js Sentry DSN configuration events stay local
💡 Result:
Spotlight.js Sentry DSN configuration events stay local by routing Sentry SDK telemetry to the local Spotlight “sidecar” instead of to Sentry’s production servers. How it works (from the official Spotlight/Sentry integration docs): 1) Enable the Spotlight integration in your SDK with the special DSN/Spotlight settings. Spotlight runs locally and uses a local stream endpoint by default at http://localhost:8969/stream. - In the Sentry Spotlight SDK integration spec, enabling is done via the spotlight option: true enables Spotlight using the default URL (http://localhost:8969/stream), and a string enables using the provided URL. - SDKs also support the SENTRY_SPOTLIGHT environment variable to point SDKs at the local Spotlight server/stream. 2) The DSN you configure for development should be a placeholder that points to Spotlight, not your real Sentry project. Spotlight’s docs show using dsn: "https://spotlight@local/0" as a placeholder, while enabling spotlight/spotlightBrowserIntegration. 3) The Spotlight integration sends a local copy of events to the Spotlight sidecar and then the local Spotlight UI consumes those events via a local /stream endpoint (SSE). Spotlight is explicitly described as “Sentry for Development” that runs locally (including the sidecar) and lets you see events without needing production Sentry monitoring. Practical configuration pattern (as shown in Spotlight docs for web/Next.js): - Use a placeholder DSN for dev (e.g. https://spotlight@local/0). - Enable Spotlight in Sentry.init using spotlight: true (or spotlight: process.env.SENTRY_SPOTLIGHT === '1') and/or enable the browser integration spotlightBrowserIntegration. - Ensure SENTRY_SPOTLIGHT is set only in development so events go to your local Spotlight instance. If you want, tell me which Sentry SDK/framework you’re using (e.g., @sentry/nextjs, @sentry/node, browser SDK, etc.) and how you currently configure DSN, and I’ll translate the above into an exact config snippet for that setup.
Citations:
- 1: https://develop.sentry.dev/sdk/foundations/client/integrations/spotlight/
- 2: https://spotlightjs.com/docs/quickstart/nextjs/
- 3: https://spotlightjs.com/docs/getting-started/
- 4: https://docs.sentry.io/platforms/javascript/guides/capacitor/configuration/integrations/spotlight/
- 5: https://spotlightjs.com/
- 6: https://spotlightjs.com/architecture
- 7: https://spotlightjs.com/docs/getting-started
- 8: https://spotlightjs.com/docs/cli/reference/
- 9: https://spotlightjs.com/about
Force local DSN whenever Spotlight is enabled.
The current logic allows a production Sentry DSN to be used when Spotlight is enabled, which contradicts the comment's claim that "events are redirected to the sidecar and never leave the machine." Spotlight actually duplicates events to both the local sidecar AND the production Sentry endpoint simultaneously. To ensure development events stay local-only in Spotlight mode, always use the placeholder DSN.
🔧 Proposed fix
- dsn:
- __SPOTLIGHT_ENABLED__ && !sentryDsn
- ? 'https://spotlight@local/0'
- : sentryDsn,
+ dsn: __SPOTLIGHT_ENABLED__ ? 'https://spotlight@local/0' : sentryDsn,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| dsn: | |
| __SPOTLIGHT_ENABLED__ && !sentryDsn | |
| ? 'https://spotlight@local/0' | |
| : sentryDsn, | |
| dsn: __SPOTLIGHT_ENABLED__ ? 'https://spotlight@local/0' : sentryDsn, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main.ts` around lines 66 - 69, The dsn assignment currently uses the
placeholder only when __SPOTLIGHT_ENABLED__ && !sentryDsn, which still allows a
real sentryDsn to be used when spotlight is enabled; change the dsn logic so
that whenever __SPOTLIGHT_ENABLED__ is true it always returns the local
placeholder ('https://spotlight@local/0') regardless of sentryDsn. Locate the
dsn property assignment in src/main.ts and update the condition to check
__SPOTLIGHT_ENABLED__ alone (using the symbols dsn, __SPOTLIGHT_ENABLED__, and
sentryDsn) so Spotlight mode forces the local DSN always.
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #11580 +/- ##
===========================================
- Coverage 71.26% 54.96% -16.31%
===========================================
Files 1486 1377 -109
Lines 83612 70312 -13300
Branches 23040 18662 -4378
===========================================
- Hits 59584 38644 -20940
- Misses 23144 31123 +7979
+ Partials 884 545 -339
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 992 files with indirect coverage changes 🚀 New features to boost your workflow:
|
- Extract Sentry env opts (cloud/spotlight/silent) for readability - Hoist SPOTLIGHT_PLACEHOLDER_DSN constant - Move enableLogs into spotlight branch - Add PowerShell variant for SPOTLIGHT=true in CONTRIBUTING.md - Tune .fallowrc.json: cover storybook + package configs, exclude generated registry/manager types, tools/, .github/ Amp-Thread-ID: https://ampcode.com/threads/T-019de261-3205-769d-a3c0-e7a363ea5896 Co-authored-by: Amp <amp@ampcode.com>
PR Created by the Glary-Bot Agent
Summary
Exploratory PR from the Syntax.fm #998 tooling review in
#frontend-here-be-dragons. Adds two of the three recommendations from the thread so the team can evaluate real signal before committing to wider adoption:.fallowrc.jsonwith all rules atwarn; matching CI workflow YAML is below for a maintainer to add in a follow-up commit (see note at bottom)Changes
Spotlight (dev-only, opt-in)
__SPOTLIGHT_ENABLED__Vite define, true only whenNODE_ENV=development,DISTRIBUTION !== 'cloud', andSPOTLIGHT=truein env.src/main.ts: when enabled, addsspotlightBrowserIntegration+browserTracingIntegration+consoleLoggingIntegration+enableLogs: true, forces Sentry on with a placeholder DSN so events go to the local sidecar only. Cloud and prod paths are unchanged.CONTRIBUTING.md: runpnpx @spotlightjs/spotlightin one terminal,SPOTLIGHT=true pnpm devin another, openhttp://localhost:8969..env_example: flag documented alongside existing Sentry vars.pnpxon demand.Fallow audit config
.fallowrc.json: all rules set towarn. Entry points coversrc/main.ts,scripts/**,build/**plugins, all Vite/Vitest/Playwright/ESLint configs,apps/*/src/main.ts,packages/*/src/index.ts, stories, and tests.src/lib/litegraph/**andsrc/locales/**are ignored.Follow-up: CI workflow to add
The GitHub App used to open this PR lacks the
workflowspermission, so it cannot push files under.github/workflows/. The workflow below was authored and verified locally (syntactically valid per the repo's ownscripts/cicd/check-yaml.sh). A maintainer can drop this into.github/workflows/ci-fallow-audit.yamlin a follow-up commit on this branch:Review Focus
src/main.ts— the__SPOTLIGHT_ENABLED__branch forces Sentry on. In prod/cloud builds the define isfalse, so the whole path is dead-code eliminated and production behavior is byte-identical..fallowrc.jsonentry list — happy to adjust once we see the first audit output. Entries were chosen to give fallow enough seed points to traverse the full module graph without false-positive unused-file spam.Non-goals (parked)
chrome-devtools-mcp— separate MCP config change, not needed in this PR.Verification
pnpm typecheckcleanpnpm lintcleanpnpm knipcleanpnpm format:checkcleanscripts/cicd/check-yaml.shandcheck-json.shclean (verified on the workflow YAML before it was removed from this branch)SPOTLIGHT=true pnpm devboots cleanly; Spotlight integration verified live in browser (SDK attempts sidecar connection tolocalhost:8969and emits envelopes to the placeholder DSN as expected)Left as Draft so the team can decide on the workflow add and whether the fallow signal is worth promoting rules from
warn→errorover time.┆Issue is synchronized with this Notion page by Unito