Skip to content

feat(diagnostics): source locations on every finding, docs page per code - #4107

Merged
ryansolid merged 6 commits into
nextfrom
claude/project-thread-atg22y-locations
Oct 16, 2026
Merged

ryansolid merged 6 commits into
nextfrom
claude/project-thread-atg22y-locations

Conversation

@ryansolid

Copy link
Copy Markdown
Member

Summary

Before: a dev console report tells a human what went wrong and names the owner chain (in <App> › <TodoRow> › effect), but nothing in it leads to the code, and its only guidance line points at the agent repair skill. Scheduler-time findings (HOT_SCOPE_*, SILENT_HOLD, waterfalls, SSR codes) fire from the flush, so even the console gutter link lands inside the runtime.

After: the report carries a clickable source location and a human-facing docs link:

[STRICT_READ_UNTRACKED] Reactive value "count" read directly in a component body will not update. ...
  in <TodoRow> › label
  at http://localhost:3000/src/components/TodoRow.tsx:18:3
[STRICT_READ_UNTRACKED] what it means and how to fix it: https://v2.solidjs.com/reference/solid-js/diagnostics/strict-read-untracked
[STRICT_READ_UNTRACKED] agent repair guide: node_modules/solid-js/skills/reactivity-diagnostics/SKILL.md#strict_read_untracked — https://github.com/solidjs/solid/blob/next/packages/solid/skills/reactivity-diagnostics/SKILL.md#strict_read_untracked

Every DiagnosticEvent gains loc (the innermost located node on the subject's chain, so a scope compiled without a location falls back to its component's JSX site), and @solidjs/diagnostics artifacts and assertion messages carry it.

The location comes from the compilers, not from stack captures: sourceNames gains a third kind, locations, in both @solidjs/babel-plugin and @solidjs/compiler, plus a root option the build tool fills with the Vite root. Components get a 4th createComponent argument, bindings get loc beside name (spread as a 6th positional string, since spread takes no options object), and the native transformSourceNames pass writes loc beside the primitive's name. Both compilers print identical output; the parity suites assert it. locations follows dev like the other kinds. A dev-only string literal per site, nothing in production.

DEV.guideUrl now points at the docs page per code (solidjs/solid-docs#1658 adds the 66 pages, generated from the skill; the host flips to docs.solidjs.com at stable with the JSDoc links). solidjs/solid-vite-plugin#421 passes the new options and probes for compiler support so older compilers keep working.

Runtime surface: loc?: string beside name on NodeOptions, StoreOptions, SignalOptions, MemoOptions and the effect options (documented as compiler-written), createComponent(Comp, props, name?, loc?), DiagnosticEvent.loc. No new exports.

How did you test this change?

  • @solidjs/signals: 5321 passed (new "diagnostics source locations" tests; dist-artifact tests assert _loc is an observe-tier slot absent from prod).
  • solid-js: 869 passed, test-types ok (server createComponent loc, console footer text, cross-package field reservation).
  • @solidjs/web: 1405 + 1731 + 511 passed (effect/insert/spread loc on nodes and events, performance tracks guide URL). One server spec, server-functions-adapter-request.spec.tsx "keeps a rebuild failure's own error", fails locally on Node 22 because new Request accepts a fake signal there; it is unrelated to this change and passes on the Node 24 CI uses.
  • @solidjs/diagnostics: 37 passed.
  • @solidjs/babel-plugin: 305 passed (new locations fixture, defaults, root).
  • @solidjs/compiler: 6242 passed including cross-mode parity, option matrix and parity probes against the Babel plugin; cargo test (default, --no-default-features, tsrx), cargo clippy -D warnings, cargo fmt --check clean.
  • node scripts/generate-diagnostics-json.mjs --check up to date; prettier clean; changesets (patch) for every touched package.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ka6h98478umeFadcRV1KLY


Generated by Claude Code

claude and others added 5 commits October 15, 2026 22:32
Dev and observe builds keep a `loc` (`file:line:col`, root-relative — the
compiler's `sourceNames` `locations` kind) in a `_loc` slot beside `_name`
on owner, computed, effect, signal and store-property nodes, and on the
client and server component owners through a fourth `createComponent`
argument. `emitDiagnostic` resolves `DiagnosticEvent.loc` as the innermost
located node on the subject's chain, the way `ownerPath` is found, and
`reportDiagnostic` prints it as an `at` line after `in` — a dev-server URL in
a browser so the devtools link it to Sources. `@solidjs/web`'s `effect` and
`insert` take `loc` in their options, `spread` a trailing `loc`.

`DEV.guideUrl(code)` is now the code's docs page
(`https://v2.solidjs.com/reference/solid-js/diagnostics/<slug>`); the console
footer leads with it for a human and keeps the repair skill (installed path
and GitHub anchor) on a second line for an agent. `diagnostics.json` is
regenerated and the skill documents the `at` line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ka6h98478umeFadcRV1KLY
A third `sourceNames` kind, `locations`, and a `root` option. Where a
component or binding label is emitted, its source location rides beside it:
`createComponent(Home, props, "Home", "src/App.tsx:12:5")` (the `<Home`
opening element, DOM and SSR), `{ name: "div.class", loc: "src/App.tsx:14:7" }`
(the attribute start; a merged effect points at its first attribute), a
hole's `{` for its insert, and the first spread attribute's start as one more
`spread` argument after the tag. The path is relative to `root` (default:
Babel's `cwd`), posix separators, line and column 1-based. `locations`
follows `dev` like the other kinds; no `filename` → no location.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ka6h98478umeFadcRV1KLY
`sourceNames` gains a `locations` kind and `transform()` a `root` option.
Where a name is emitted, its source position follows as
`<path>:<line>:<column>` (path relative to `root`, default cwd, posix;
line and column 1-based): a fourth `createComponent` argument at the
opening element (DOM and SSR), `loc` after `name` in a binding's options
(the attribute's start, a hole's `{`; a merged effect points at its first
binding, a property split out of `style={{}}`/`class={{}}` at that
attribute), and one more positional string after the tag for `spread`.
Nothing without a `filename`. `locations` follows `dev` like the other
kinds.

`transformSourceNames` gains `locations` (default true) and `root`, and
writes `loc` right after `name` in the options literal it writes or
completes, keeping an explicit `name` and never overriding an explicit
`loc`.

`relative_id` and the refresh pass's `line_column` move to
`shared::location`, which the no-`node` build can reach. The parity
harness now gives Babel the same `<fixture>.jsx` filename oxc gets, so
the location strings can be compared once both compilers print them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ka6h98478umeFadcRV1KLY
The split-out object of `class={["a", { b: x() }]}` and a `{...items()}`
child are holes with a name, so they carry a location too; this keeps both
compilers' dev output identical (the parity suites compare them by text).
The native compiler's copy of the Babel `locations` fixture source is
dropped now that the Babel fixture is on the same branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ka6h98478umeFadcRV1KLY
The compiler's `locations` kind writes `{ name, loc }` into the options
of `createSignal`, `createMemo` and the effect primitives, so the public
option interfaces accept it beside `name`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ka6h98478umeFadcRV1KLY
@changeset-bot

changeset-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fb2dcaf

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@solidjs/babel-plugin Patch
@solidjs/compiler Patch
@solidjs/diagnostics Patch
@solidjs/signals Patch
solid-js Patch
@solidjs/web Patch
test-integration Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
@solidjs/universal Patch
board-example Patch
server-dbmon-example Patch
todos-server-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Size (brotli, eager entry chunk)

scenario head vs base minified vs base minified vs recorded cap lazy chunks (not counted)
signals: core floor (createSignal/Memo/Effect/Root/flush) 7.24 KB 0 B 0 B 0 B 7.25 KB ✅
signals: + createStore 14.77 KB 0 B 0 B 0 B 14.79 KB ✅
signals: + isPending/latest 9.94 KB 0 B 0 B 0 B 9.96 KB ✅
signals: + until/refresh (truth readers, no optimism) 7.85 KB 0 B 0 B −1132 B 7.86 KB ✅
app: render + one signal (the simple-app floor) 9.55 KB 0 B 0 B 0 B 9.56 KB ✅
app: hydrating (no stores) with Show/For/Loading/Errored/lazy 17.85 KB −9 B (−0.1%) +2 B +2 B 17.87 KB ✅ lazy-page.js 0.04 KB
app: hydrating + every store primitive family 29.84 KB −64 B (−0.2%) +2 B +2 B 29.91 KB ✅ lazy-page.js 0.04 KB
app: CSR with Show/For/Loading/Errored/lazy 12.84 KB −19 B (−0.1%) +2 B +2 B 12.87 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier (same app on the observe artifacts) 14.50 KB +54 B (+0.4%) +204 B 0 B 14.51 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier + attribution engine enabled 29.11 KB +51 B (+0.2%) +204 B 0 B 29.12 KB ✅ lazy-page.js 0.04 KB
app: compiled floor (one template, one text hole, one delegated click) 9.75 KB 0 B +2 B +2 B 9.77 KB ✅
app: compiled CSR (JSX todo app: spread/merge/omit, events, class/style, keyed For, Show, Loading + lazy, store) 25.69 KB +40 B (+0.2%) +4 B +4 B 25.66 KB ⚠️ over by 26 B, 16 B minified headroom stats.js 0.18 KB
app: compiled hydrating (the same JSX todo app through hydrate(), compiled hydratable) 31.63 KB −72 B (−0.2%) +4 B +4 B 31.72 KB ✅ stats.js 0.20 KB
frames: eager client consumer (frames client + transport, lazy codec) 11.87 KB 0 B 0 B 0 B 11.87 KB ✅
page: base server components (hydrating + dynamic + frames + sf reference) 34.51 KB −95 B (−0.3%) +2 B +2 B 34.62 KB ✅ assets.js 0.78 KB, bind.js 2.54 KB, decode.js 6.25 KB, lazy-page.js 0.04 KB, regions.js 0.80 KB, trace.js 9.04 KB, wire.js 1.35 KB
page: live server components (base + live/GET + action + isPending/latest) 38.71 KB −31 B (−0.1%) +2 B +6 B 38.70 KB ⚠️ over by 12 B, 14 B minified headroom assets.js 0.78 KB, bind.js 2.54 KB, decode.js 6.25 KB, lazy-page.js 0.04 KB, regions.js 0.80 KB, trace.js 9.04 KB, wire.js 1.35 KB
page: compiled base server components (the base page as JSX: templates with class/style/attributes/events, For/Show; no spread) 35.40 KB +51 B (+0.1%) +2 B +2 B 35.37 KB ⚠️ over by 34 B, 18 B minified headroom assets.js 0.78 KB, bind.js 2.54 KB, decode.js 6.25 KB, regions.js 0.80 KB, sc-comments.js 0.20 KB, trace.js 9.01 KB, wire.js 1.35 KB
page: compiled live server components (the compiled base page + live/GET + action + isPending/latest) 41.46 KB −22 B (−0.1%) +2 B +2 B 41.21 KB ⚠️ over by 247 B, 18 B minified headroom eager (counted): web.js 21.96 KB; assets.js 0.78 KB, bind.js 2.55 KB, decode.js 6.25 KB, regions.js 0.79 KB, sc-comments.js 0.19 KB, trace.js 8.99 KB, wire.js 1.35 KB
page: base + router (base page + @solidjs/router: createRouter, two routes, preload, useNavigate) 41.94 KB −6 B (−0.0%) +2 B +2 B 41.96 KB ✅ assets.js 0.78 KB, bind.js 2.56 KB, decode.js 6.25 KB, lazy-page.js 0.04 KB, regions.js 0.80 KB, server.js 1.02 KB, serverForms.js 3.59 KB, trace.js 9.03 KB, wire.js 1.36 KB
page: live + router (live page + @solidjs/router: createRouter, two routes, preload, useNavigate) 48.15 KB +32 B (+0.1%) +2 B 0 B 48.16 KB ✅ eager (counted): client.js 28.17 KB; assets.js 0.78 KB, bind.js 2.56 KB, decode.js 6.25 KB, lazy-page.js 0.04 KB, regions.js 0.81 KB, server.js 1.02 KB, serverForms.js 3.30 KB, trace.js 9.01 KB, wire.js 1.36 KB
server: floor (getRequestEvent + isServer) 1.33 KB 0 B 0 B 0 B 1.34 KB ✅
server: renderToString (the server-render floor) 20.57 KB 0 B 0 B +8 B 20.58 KB ✅

⚠️ Over the brotli cap within the minified allowance (passes)

  • app: compiled CSR (JSX todo app: spread/merge/omit, events, class/style, keyed For, Show, Loading + lazy, store): over brotli cap by 26 B; minified 80,210 B vs 80,206 B recorded with the cap (+4 B) — 16 B of the 20 B minified allowance left; +4 B minified over this PR's base
  • page: live server components (base + live/GET + action + isPending/latest): over brotli cap by 12 B; minified 119,546 B vs 119,540 B recorded with the cap (+6 B) — 14 B of the 20 B minified allowance left; +2 B minified over this PR's base
  • page: compiled base server components (the base page as JSX: templates with class/style/attributes/events, For/Show; no spread): over brotli cap by 34 B; minified 109,102 B vs 109,100 B recorded with the cap (+2 B) — 18 B of the 20 B minified allowance left; +2 B minified over this PR's base
  • page: compiled live server components (the compiled base page + live/GET + action + isPending/latest): over brotli cap by 247 B; minified 124,012 B vs 124,010 B recorded with the cap (+2 B) — 18 B of the 20 B minified allowance left; +2 B minified over this PR's base

Bundled with Rolldown (what Vite ships), brotli q11, decimal KB. A scenario fails only when it is over its brotli cap and its minified size is more than 20 B over the minified recorded with the cap; over the cap within that allowance is brotli layout noise and passes with a warning. Caps and their recorded minified in scripts/size/scenarios.js; the floor and page caps in floor-caps.json are frozen (lower only, or Size-Exception: in the PR body). npm run ratchet lowers caps per RC; it never raises one (scripts/size/README.md).

@coveralls

coveralls commented Oct 15, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 38819579478

Coverage increased (+0.03%) to 77.539%

Details

  • Coverage increased (+0.03%) from the base build.
  • Patch coverage: 5 of 5 lines across 3 files are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1331
Covered Lines: 1089
Line Coverage: 81.82%
Relevant Branches: 1042
Covered Branches: 751
Branch Coverage: 72.07%
Branches in Coverage %: Yes
Coverage Strength: 27.71 hits per line

💛 - Coveralls

…e caps it moves

One owner walk for the location (an owner has a `_parent` key, a signal
hops to its `_owner`), the store's label and location looked up once per
property node: -116 B minified on the observe tier. The rest (+204 B
minified, observe-tier only; prod byte-identical except the two bytes of
`createComponent`'s fourth parameter) is the feature, so the two observe
scenarios and the live + router page are re-based on the measurement with
dated ledger lines. No frozen floor moves.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ka6h98478umeFadcRV1KLY
@codspeed

codspeed Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Merging this PR will improve performance by 5.86%

⚠️ 1 benchmark spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
✅ 194 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ dbmon shallow full tick 53.7 ms 50.7 ms +5.86%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing claude/project-thread-atg22y-locations (fb2dcaf) with next (ccd9948)

Open in CodSpeed

@ryansolid
ryansolid merged commit 4a99c84 into next Oct 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants