solid2-agent-kit 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,7 +16,7 @@ Supported agents: **Cursor** and **Claude Code**.
16
16
  | Always-applied hard rules | `.cursor/rules/solid-2.mdc` (glob-attached to `*.tsx`/`*.jsx`) | managed block in `CLAUDE.md` | 23 hard rules + banned Solid 1.x API table + eslint-plugin-solid lint-heritage table. Injected without the agent having to decide to read anything |
17
17
  | Agent skill | `.cursor/skills/solid-2/` | `.claude/skills/solid-2/` | Execution-model primer, decision tables, canonical patterns, a “still runs / write the other form” table, review checklist, official-doc URL index |
18
18
  | Shared agent context | managed block in `AGENTS.md` | same (Claude Code users can reference it from `CLAUDE.md`) | Core principles and pointers, always in context |
19
- | Mechanical gate | `solid2-kit check` | same | Whole-file regex guard over `src/**/*.{ts,tsx}`: fails on props destructuring (including multi-line signatures), `{ ...props }` rest copies, `{list().map(...)}` in JSX, React imports/hooks/JSX props/`React.lazy`/`useOptimistic`/`onDoubleClick`, Solid 1.x imports/APIs/components/JSX namespaces, `vite-plugin-solid`, old-router `<Route>`/`<A>`/`<HashRouter>`/`<FileRoutes>`, `MetaProvider`, `Context.Provider`, camelCase style keys, `key` props, `value()!` hand-narrowing, hand-rolled loading signals, `=== undefined` readiness branches, SolidStart leftovers (`@solidjs/start`, `h3`, `createAsync`, `useSubmission`, `"use client"`), Next.js imports/`useRouter`/`typeof window`, `history.pushState`, and `render(<App />)` (pass a function) |
19
+ | Mechanical gate | `solid2-kit check` | same | Whole-file regex guard over `src/**/*.{ts,tsx}`: fails on props destructuring (including multi-line signatures), `{ ...props }` rest copies, `{list().map(...)}` in JSX, React imports/hooks/JSX props/`React.lazy`/`useOptimistic`/`onDoubleClick`, Solid 1.x imports/APIs/components/JSX namespaces, `vite-plugin-solid`, old-router `<Route>`/`<A>`/`<HashRouter>`/`<FileRoutes>`, `MetaProvider`, `Context.Provider`, camelCase style keys, `key` props, `value()!` hand-narrowing, hand-rolled loading signals, `=== undefined` readiness branches, SolidStart leftovers (`@solidjs/start`, `h3`, `createAsync`, `useSubmission`, `"use client"`), Next.js imports/`useRouter`/`typeof window`, `history.pushState`, `render(<App />)` (pass a function), `isPending(user())` / `latest(user())` (pass the accessor), and `<Dynamic>` (use `dynamic()`) |
20
20
 
21
21
  Managed blocks are delimited with `<!-- solid2-agent-kit:*:start/end -->` markers; everything
22
22
  outside them is yours. Kit-owned files (`solid-2.mdc`, the skill directories) are overwritten
@@ -351,6 +351,20 @@ const CHECKS = [
351
351
  message:
352
352
  'Readiness branch on a zero-arg call. Read async values under <Loading> (first load) / isPending() (refetch) and let errors reach <Errored>. For non-reactive utils, assign to a variable and test that.',
353
353
  },
354
+ {
355
+ // `isPending(user())` / `latest(data())` evaluate the read before the
356
+ // helper runs. `isPending(user)` and `isPending(() => user())` do not match.
357
+ id: 'pending-accessor-call',
358
+ pattern: /(?<![.\w])(?:isPending|latest)\(\s*[A-Za-z_$][\w$]*\(\s*\)/g,
359
+ message:
360
+ 'Pass the accessor to isPending/latest, not the call: isPending(user), not isPending(user()). Calling it first evaluates the read before the helper runs.',
361
+ },
362
+ {
363
+ id: 'dynamic-jsx',
364
+ pattern: /<Dynamic[\s/>]/g,
365
+ message:
366
+ '`<Dynamic>` is a JSX convenience wrapper. Application code should use dynamic(() => ...) from "@solidjs/web" so the component identity stays stable.',
367
+ },
354
368
  ];
355
369
 
356
370
  // .ts / .tsx sources, excluding .d.ts declaration files.
@@ -3,4 +3,4 @@
3
3
  - All TSX in this project is Solid 2.0. Components run **once**; reactivity flows through signals/stores to JSX. React patterns (props destructuring, `className`, per-keystroke `onChange`, state-synced-by-effect, `.map()` lists) and Solid 1.x APIs (`createResource`, `onMount`, `solid-js/store`, `Suspense`, path setters) are bugs here.
4
4
  - Before writing TSX, read the `solid-2` skill (`.cursor/skills/solid-2/SKILL.md` for Cursor, `.claude/skills/solid-2/SKILL.md` for Claude Code): patterns, decision tables, official doc URLs. Hard rules auto-attach from `.cursor/rules/solid-2.mdc` in Cursor and live in a managed block in `CLAUDE.md` for Claude Code.
5
5
  - After editing TSX, run the `solid2-kit check` mechanical gate — it fails on React/Solid 1.x tokens and props destructuring. Use the project's `lint:solid` script if one exists; otherwise `npx solid2-agent-kit check` (with the kit as a devDependency) or `npx github:lightsound/solid2-agent-kit check`. Do not install `eslint-plugin-solid` (built for Solid 1.x; misreads Solid 2 idioms).
6
- - Solid 2.0 shipped recently and breaks with 1.x — never trust pre-2.0 Solid docs, tutorials, or training knowledge. Verify APIs against https://v2-rebuild--solid-docs-v2.netlify.app/llms.txt (markdown mirror of https://v2.solidjs.com/, which blocks non-browser fetchers). If the project has `@solidjs/router`, server functions, or `@solidjs/meta`, use those official pages (see the skill's official-docs index) — not React Router, Next.js, SolidStart 1.x, or Solid Router 0.x/1.x JSX `<Route>`/`<A>`. Core `action` from `solid-js` is a generator transaction; router `action`/`query` from `@solidjs/router` are POST forms and a read cache. Pass a function to `render`/`hydrate`: `render(() => <App />, root)`. Component teardown is `onSettled`, not `onCleanup`. One router instance; in-app navigation is `useNavigate` / `<a href>`, not `window.location`.
6
+ - Solid 2.0 shipped recently and breaks with 1.x — never trust pre-2.0 Solid docs, tutorials, or training knowledge. Verify APIs against https://v2-rebuild--solid-docs-v2.netlify.app/llms.txt (markdown mirror of https://v2.solidjs.com/, which blocks non-browser fetchers). If the project has `@solidjs/router`, server functions, or `@solidjs/meta`, use those official pages (see the skill's official-docs index) — not React Router, Next.js, SolidStart 1.x, or Solid Router 0.x/1.x JSX `<Route>`/`<A>`. Core `action` from `solid-js` is a generator transaction; router `action`/`query` from `@solidjs/router` are POST forms and a read cache. Core `refresh(source)`, router `revalidate(key)`, and server-function `return reload(...)` are three different APIs. Pass the accessor to `isPending(user)`, not `isPending(user())`. Pass a function to `render`/`hydrate`: `render(() => <App />, root)`. Component teardown is `onSettled`, not `onCleanup`. One router instance; in-app navigation is `useNavigate` / `<a href>`, not `window.location`.
@@ -78,10 +78,14 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
78
78
  accessor) only when that identity change should show the fallback again. Pass an async
79
79
  memo to a child as the **accessor** (`user={user}`), and read `props.user()` under that
80
80
  child's `<Loading>` — `user={user()}` throws not-ready at the parent, so the child's
81
- boundary never sees it. Do not `try/catch` `NotReadyError` around a read, and do not
81
+ boundary never sees it. Do not start `fetch` (or any request) at component-body top
82
+ level — that runs once at mount and is not a reactive source. Do not `try/catch` `NotReadyError` around a read, and do not
82
83
  use `loadingValue` / `seedLoadingValue` as the default first-flight UI — those skip
83
- `<Loading>`. `{latest(() => x())}` is a preview, not the visible answer (`isPending`
84
- is the indicator). "Prop with local edits" = writable derivation:
84
+ `<Loading>`. `{latest(() => x())}` is a preview, not the visible answer. `isPending`
85
+ is the *refetch* indicator after a settled answer exists — do not use it as the
86
+ first-load spinner (`<Loading>` owns that). Pass the accessor: `isPending(user)`,
87
+ not `isPending(user())` (that evaluates the read before `isPending` runs).
88
+ "Prop with local edits" = writable derivation:
85
89
  `createSignal(() => props.value)` or `createStore(() => props.value, fallback)`.
86
90
  `createOptimistic` is for an in-flight mutation, not a local editing session.
87
91
  11. Context: the context object is the provider — `<MyContext value={{ theme, setTheme }}>`
@@ -139,7 +143,11 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
139
143
  counter/version signal read inside the computation. Input-driven refetch is automatic
140
144
  (tracked inputs re-run the computation) and subscriptions push. Legit uses: after a
141
145
  mutation inside an `action`, and explicit reload buttons. Pair with `affects(source)`
142
- when the reload should present as pending.
146
+ when the reload should present as pending — a bare `refresh()` re-asks quietly.
147
+ These are three different APIs: core `refresh(source)` reruns a reactive source;
148
+ router `revalidate(getUser.key)` invalidates the query cache; server-function
149
+ `return reload({ revalidate: "todos" })` asks the integration to refresh cached
150
+ data. Do not mix them.
143
151
  20. **External collections flow into stores through reconciliation.** First choice:
144
152
  function-form `createStore(() => source, fallback)` / `createProjection` (reconcile
145
153
  automatically, keyed by `"id"`). Manual merge: `setStore(reconcile(fresh, "id"))` or
@@ -175,9 +183,13 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
175
183
  fallback body), not event-time mutations. `JSON.stringify(store)`
176
184
  / `structuredClone(store)` without `snapshot(store)` can throw or leak proxies. Async SSR
177
185
  without `<Loading>` *works* but blocks the HTML shell until every read settles — wrap
178
- reads, or stream with `renderToStream`.
179
- Navigation-shaped updates (a setter, then async computeds holding previous values)
186
+ reads, or stream with `renderToStream`. `pipe` / `pipeTo` / `readable` each consume
187
+ a stream render — use exactly one. `createRoot` is for tests, libraries, and
188
+ non-render entry points; inside a component let `render` / the component owner
189
+ own the scope. Async reads inside `<Portal>` start on the client — hoist the
190
+ read above the portal. Navigation-shaped updates (a setter, then async computeds holding previous values)
180
191
  do not need core `action` — reach for it only when writes happen *after* async work.
192
+ Invoke actions from handlers, not from memos or effects.
181
193
  23. **Composition.** Pass `props.children` through when you only render them. Inspect or
182
194
  iterate children with `children(() => props.children)` (then `.toArray()`), never a
183
195
  setup-time snapshot. Code-split with `lazy(() => import("./X"))` under `<Loading>` —
@@ -199,8 +211,17 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
199
211
  `window.location` / `history.pushState`. Trusted identity is
200
212
  `getRequestEvent()`, never a caller-supplied user id. Unscripted forms use
201
213
  the function `.url` (or a router `action`), not a hand-built `/_server/`
202
- path. Document head is
203
- `<Title>` / `<Meta>` from `@solidjs/meta` with **no** `MetaProvider`.
214
+ path. Client history adapters do not select the SSR URL — pass
215
+ `<Router url={request.url}>` (or rely on the request event). Secrets belong in
216
+ `virtual:env/server`, never the `client` env map / `import.meta.env`. Document head is
217
+ `<Title>` / `<Meta>` from `@solidjs/meta` with **no** `MetaProvider`. A static
218
+ `<title>` in the document shell is the fallback; do not hardcode other tags
219
+ (`<meta name="description">`) that Solid Meta should manage.
220
+ Return `respond(value, { status })` from a `"use server"` function, not
221
+ `Response.json` — a raw `Response` is HTTP-handler control flow; scripted
222
+ callers should receive the value. JSON-encodable server-function arguments
223
+ only, unless `enableRichArguments()` was called once in the client entry
224
+ (`Date` / `Map` / `Set` throw without it).
204
225
 
205
226
  ## Lint heritage: eslint-plugin-solid (Solid 1.x) intents carried into this file
206
227
 
@@ -264,6 +285,7 @@ When unsure about any API, verify against the official Solid 2.0 docs — fetcha
264
285
  listed in `references/official-docs.md` next to the `solid-2` skill. Do not guess from
265
286
  Solid 1.x or React memory.
266
287
 
267
- `createTrackedEffect` and `storePath` exist in Solid 2 but are not defaults: use two-phase
268
- `createEffect` unless a single tracked callback is required, and draft setters instead of
269
- `storePath` except while converting 1.x path setters.
288
+ `createTrackedEffect`, `createRenderEffect`, and `storePath` exist in Solid 2 but
289
+ are not defaults: use two-phase `createEffect` unless a single tracked callback is
290
+ required, and draft setters instead of `storePath` except while converting 1.x
291
+ path setters.
@@ -67,7 +67,8 @@ Most React `useEffect` code should NOT become `createEffect`:
67
67
  | Inspect, count, or iterate children | `children(() => props.children)` then `.toArray()` |
68
68
  | Code-split a component | `lazy(() => import("./X"))` read under `<Loading>` |
69
69
  | Named export from a lazy module | `lazy(() => import("./pages"), { export: "About" })` |
70
- | Pick a component/tag from reactive state | `dynamic(() => ...)` from `@solidjs/web` (stable identity) |
70
+ | Pick a component/tag from reactive state | `dynamic(() => ...)` from `@solidjs/web` (stable identity; prefer over `<Dynamic>`) |
71
+ | Overlay / modal | `<Portal>` — hoist async reads *above* the portal (reads inside start on the client) |
71
72
  | Browser-only widget (charts, maps, `window`) | `clientOnly(() => import("./Chart"))` from `@solidjs/web` (`{ lazy: true }` defers the import until first render) |
72
73
  | Server vs browser branch | `isServer` / `isDev` from `@solidjs/web` (build-time constants), not `typeof window` |
73
74
  | SSR-stable `id` / `for` / `aria-*` pairing | `createUniqueId()` |
@@ -199,7 +200,8 @@ const Result = dynamic(() => (detailed() ? Detailed : Compact));
199
200
  `dynamic()` returns a **stable** component whose source can be a component, an
200
201
  intrinsic tag name, a promise, or empty. Prefer it over swapping a component
201
202
  variable in JSX (`const View = tab() ? A : B` freezes the choice at setup).
202
- `<Dynamic>` is the JSX spelling of the same primitive.
203
+ `<Dynamic>` is the JSX spelling of the same primitive; application code should
204
+ use `dynamic()` so the component identity stays stable.
203
205
 
204
206
  ### Two-phase effect (imperative boundary only)
205
207
 
@@ -245,15 +247,18 @@ return (
245
247
  where the memo was created, and not of page chrome (header/nav) that should stay mounted.
246
248
  Boundary placement is purely a UX decision — it does not change when fetches start
247
249
  (no waterfalls: nested components set up and fetch in parallel). After first paint the
248
- boundary keeps settled content during refetch; `isPending(() => results())` is the
250
+ boundary keeps settled content during refetch; `isPending(results)` (pass the accessor,
251
+ not `isPending(results())`) is the
249
252
  indicator. Use `on={query()}` (the **value**, not the accessor `on={query}`) only when
250
253
  that identity should put the fallback back on screen.
254
+ Do not use `isPending` as the first-load spinner.
251
255
  - Read every reactive input **before the first `await`** in an async computation. A read
252
256
  after `await` does not subscribe; production can sit pending with no retry.
253
257
  - `<Errored>` function fallbacks receive an error **accessor** and a `reset` callback:
254
258
  `fallback={(error, reset) => ...}`.
255
- - Refetch: `refresh(results)`. In-flight indicator: `isPending(() => results())`.
256
- Freshest in-flight value for imperative code: `latest(results)`.
259
+ - Refetch: `refresh(results)`. In-flight indicator: `isPending(results)` (or
260
+ `isPending(() => results())`). Freshest in-flight value for a preview:
261
+ `latest(results)`. Do not start `fetch` at component-body top level.
257
262
  - Coordinate reveal order of sibling `<Loading>` boundaries with `<Reveal order="sequential" | "together" | "natural">`.
258
263
 
259
264
  **Never hand-roll these states.** No `[loading, setLoading]` signal, no
@@ -634,7 +639,10 @@ shared cache, no preload reuse, and no `revalidate`. Do not wrap mutations in
634
639
  `query` — wrapping an undeclared server function makes it GET. `query.get(key)`
635
640
  throws if there is no entry; `query.set` does not accept a promise. Cache is
636
641
  request-scoped on the server and application-scoped in the browser. After a
637
- router mutation, `revalidate(...)` — not core `refresh()`. Replace SolidStart /
642
+ router mutation, `revalidate(...)` — not core `refresh()`, and not
643
+ `reload()` (that is a *server-function return* that asks the integration to
644
+ refresh cached data: `return reload({ revalidate: "todos" })`). `live()` sources
645
+ update through the open stream; do not `revalidate` them. Replace SolidStart /
638
646
  Router 0.x leftovers: no `createAsync`, `useSubmission` (singular), `cache()`,
639
647
  router `json()`, or `<FileRoutes />` (`fileRoutes(pageRoutes)` instead).
640
648
 
@@ -656,7 +664,11 @@ revalidate: `redirect` / `reload` from `@solidjs/web` (see the server-function
656
664
  guides). For 404 during SSR: `httpStatus(404)` from `@solidjs/web`.
657
665
  `throw new Error("…")` is stripped to `"Internal Server Error"` in production —
658
666
  use `markSafeError` or `respond(..., { status })` for intentional client-facing
659
- failures. `GET()` is only for idempotent reads (URLs leak into logs/history);
667
+ failures. Do not `return Response.json(...)` from `"use server"` — that is
668
+ HTTP-handler control flow; `respond(value, { status })` is what a scripted
669
+ caller unwraps. JSON-encodable arguments only unless `enableRichArguments()`
670
+ was called once in the client entry (`Date` / `Map` / `Set` throw without it).
671
+ `GET()` is only for idempotent reads (URLs leak into logs/history);
660
672
  import it from `@solidjs/web/server-functions`, never `@solidjs/start`.
661
673
  `live()` yields **current state** (each yield replaces the last); do not treat
662
674
  it as an append-only event log, and hoist the memo so consumers share one
@@ -676,8 +688,12 @@ import { Title, Link, Meta } from '@solidjs/meta';
676
688
  ```
677
689
 
678
690
  Solid Meta 1.x has **no provider**. Render tags anywhere; later wins, unmount
679
- restores. `useHead` from `@solidjs/web` is the lower-level registry. Do not
680
- hardcode a second `<title>` in the document shell.
691
+ restores. `useHead` from `@solidjs/web` is the lower-level registry — prefer
692
+ `<Title>` / `<Meta>` / `<Script>` (JSON-LD) for application metadata. Several
693
+ tags that should swap as one unit belong in `<Head>`. A static `<title>`
694
+ in the document shell is the fallback when no `<Title>` is mounted; do not
695
+ hardcode a second `<meta name="description">` (the registry leaves foreign tags
696
+ alone, so both would coexist). Do not hardcode a second `<title>` in the document shell.
681
697
 
682
698
  ## These still run — write the other form
683
699
 
@@ -742,7 +758,7 @@ how to reuse. Prefer the form on the right.
742
758
  | `<For each={resolved()}>` after `children()` | `{resolved.toArray()}` — resolved children are not a reactive data list |
743
759
  | `<input onChange={...}>` for keystrokes | `onInput` — native `onChange` is blur/commit |
744
760
  | `createOptimistic` for a local edit draft | writable derivation / plain store. Optimistic is for an in-flight mutation |
745
- | `createMemo(async fn, { loadingValue })` as the default first-flight UI | `<Loading>` for first flight; `loadingValue` is an escape hatch |
761
+ | `createMemo(async fn, { loadingValue })` / `{ seedLoadingValue: true }` as the default first-flight UI | `<Loading>` for first flight; those options are escape hatches (store projections use `seedLoadingValue`) |
746
762
  | `setSubmitted(true)` + an effect that watches it | do the work in the handler or an `action` |
747
763
  | `findUser(userId)` with the id from the client as identity | `getRequestEvent()!.locals.userId` |
748
764
  | `action={"/_server/" + id}` in app JSX | `action={createTodo.url}` or a router `action` |
@@ -753,6 +769,27 @@ how to reuse. Prefer the form on the right.
753
769
  | `{latest(() => results())}` as the visible list | settled read + `isPending()` for the indicator; `latest` is for previews |
754
770
  | `typeof window !== "undefined"` | `isServer` / `isDev` from `@solidjs/web` |
755
771
  | `Object.assign({}, props)` / expecting `merge` to skip `undefined` like 1.x `mergeProps` | `omit` / `merge` — explicit `undefined` **overrides** (Object.assign) |
772
+ | `isPending(user())` / `latest(user())` | `isPending(user)` / `latest(user)` — pass the accessor (or `() => user()`). Calling it first evaluates the read before the helper runs |
773
+ | `isPending(...)` as the first-load spinner | `<Loading>` owns first flight; `isPending` is the *refetch* indicator after a settled answer exists |
774
+ | `refresh(getUser.key)` / `revalidate(user)` / `return refresh()` from `"use server"` | three APIs: core `refresh(source)` reruns a reactive source; router `revalidate(getUser.key)` invalidates the query cache; `return reload({ revalidate: "todos" })` asks the integration to refresh cached data |
775
+ | `revalidate(liveSource)` | `live()` updates through the open stream; do not revalidate it |
776
+ | `refresh(user)` without `affects` when the reload should look pending | pair `affects(user)` with `refresh(user)` — a bare `refresh` re-asks quietly |
777
+ | `<Dynamic component={tab() ? A : B} />` | `const View = dynamic(() => (tab() ? A : B))` — `<Dynamic>` is a JSX convenience; app code should use `dynamic()` for stable identity |
778
+ | `<meta name="description" content="...">` hardcoded in the document shell | `<Meta>` from `@solidjs/meta`. A static `<title>` is the fallback; other hardcoded tags coexist with the registry |
779
+ | `useHead({ tag: "meta", ... })` for ordinary page tags | `<Title>` / `<Meta>` / `<Script>` (JSON-LD). `useHead` is the lower-level registry |
780
+ | two `<Meta property="og:image">` meant as one replacement set | wrap them in `<Head>` — a later group replaces the earlier set as one unit |
781
+ | `createRoot(() => ...)` inside a component | let `render` / the component owner own the scope. `createRoot` is for tests, libraries, and non-render entry points |
782
+ | `JSON.stringify(store)` / `structuredClone(store)` | `snapshot(store)` — proxies throw or leak reactivity |
783
+ | `import { env } from "virtual:env/client"` (or `import.meta.env`) for a signing secret | `import { env } from "virtual:env/server"` — client map values are public |
784
+ | `renderToStream(...).pipe(res)` and also `.readable` / `await` on the same result | exactly one consumer: `pipe` / `pipeTo` / `readable` / await |
785
+ | `return Response.json(user, { status: 201 })` from `"use server"` | `return respond(user, { status: 201 })` — scripted callers receive `user`, not a `Response` |
786
+ | `<Portal>{user().name}</Portal>` for an async read | hoist the read above the portal (children start on the client); async UI inside one wants its own `<Loading>` |
787
+ | a client history adapter to pick the SSR location | `<Router url={request.url}>` (or the request event). Client adapters do not select the server URL |
788
+ | `fetch('/api')` at component-body top level | `createMemo(async () => { ... fetch ... })` under `<Loading>` — a top-level fetch runs once at mount and is not a reactive source |
789
+ | `<input type="checkbox" value={on()} />` | `checked={on()}` for the toggle; `value="string"` only when grouping radios or listing submitted values |
790
+ | `createMemo(() => { void save(); return x(); })` / an effect that calls an action | invoke actions from handlers, not from memos or effects |
791
+ | `createSignal(props.value)` / `createStore(props.items)` | `createSignal(() => props.value)` / `createStore(() => props.items, fallback)` — a bare `props.x` at setup is a snapshot |
792
+ | `createRenderEffect` / `createTrackedEffect` as the default effect | two-phase `createEffect(compute, apply)` |
756
793
 
757
794
  ```tsx
758
795
  // WRONG — post-await read never subscribes; production can hang pending
@@ -832,6 +869,16 @@ render(<App />, document.getElementById('app')!);
832
869
  render(() => <App />, document.getElementById('app')!);
833
870
  ```
834
871
 
872
+ ```tsx
873
+ // WRONG — user() runs before isPending, so the helper never sees the accessor
874
+ <article aria-busy={isPending(user())}>{user().name}</article>
875
+
876
+ // CORRECT — pass the accessor (or () => user()); <Loading> still owns first flight
877
+ <Loading fallback={<p>Loading…</p>}>
878
+ <article aria-busy={isPending(user) ? 'true' : 'false'}>{user().name}</article>
879
+ </Loading>
880
+ ```
881
+
835
882
  ```tsx
836
883
  // WRONG — tab() is read once at setup; View never changes
837
884
  const View = tab() ? Detailed : Compact;
@@ -872,8 +919,10 @@ always-applied rules installed alongside this skill.
872
919
  - [ ] Effects are two-phase and only at imperative boundaries; apply does not read stores.
873
920
  - [ ] Async reads sit under `<Loading>` (the data slot, not chrome); errors under `<Errored>`.
874
921
  Reactive inputs of an async memo are read before the first `await`. No hand-rolled
875
- `loading`/`error` signals or `=== undefined` readiness branches; refetch via
876
- `refresh`, not counter signals.
922
+ `loading`/`error` signals or `=== undefined` readiness branches. First load is
923
+ `<Loading>`; refetch indicator is `isPending(user)` (the accessor, not `user()`).
924
+ Core `refresh(source)`, router `revalidate(key)`, and `return reload(...)` are
925
+ different APIs — do not mix them.
877
926
  - [ ] External collections reconcile into stores (function-form `createStore` /
878
927
  `reconcile`), never wholesale draft assignment or `setTodos((t) => t.filter(...))`
879
928
  as the identity-preserving update.
@@ -889,7 +938,9 @@ always-applied rules installed alongside this skill.
889
938
  `window.location`. Router `action`/`query` come from `@solidjs/router` (POST forms + cache), not
890
939
  core `action`/`refresh`. Forms: `<form action={save} method="post">`.
891
940
  - [ ] `render(() => <App />, root)` — a function, not `render(<App />)`. `hydrate` when
892
- HTML already exists. Stream async/lazy trees (`await renderToStream(...)`).
941
+ HTML already exists. Stream async/lazy trees (`await renderToStream(...)`) with
942
+ exactly one consumer (`pipe` / `pipeTo` / `readable`). Snapshot stores with
943
+ `snapshot(store)`, not `JSON.stringify`. Secrets live in `virtual:env/server`.
893
944
  - [ ] `jsxImportSource` is `@solidjs/web`; Vite plugin is `@solidjs/vite-plugin`.
894
945
  No `@solidjs/start`, `vinxi`, `"use client"`, or Next.js imports.
895
946
  - [ ] Single return per component; no early returns on reactive conditions.
@@ -99,6 +99,7 @@ read these before writing route JSX.
99
99
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-js/lifecycle-actions/on-settled.md
100
100
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-js/lifecycle-actions/refresh.md
101
101
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-js/advanced/specialized-reactivity/on-cleanup.md
102
+ - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-js/advanced/store-advanced/snapshot.md
102
103
 
103
104
  ## Reference: components and JSX (solid-js)
104
105
 
@@ -138,9 +139,12 @@ read these before writing route JSX.
138
139
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/jsx-properties/inner-html.md
139
140
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/server-functions.md
140
141
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/server-functions/get.md
142
+ - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/server-functions/rich-arguments.md
141
143
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response.md
142
144
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response/get-request-event.md
143
145
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response/redirect.md
146
+ - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response/reload.md
147
+ - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-web/request-response/respond.md
144
148
 
145
149
  ## Reference: @solidjs/router, @solidjs/meta, @solidjs/vite-plugin
146
150
 
@@ -148,10 +152,12 @@ read these before writing route JSX.
148
152
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/router-factory.md
149
153
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/navigation.md
150
154
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/data.md
155
+ - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/history.md
151
156
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-router/filesystem.md
152
157
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/title.md
153
158
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/meta.md
154
159
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/link.md
160
+ - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/script.md
155
161
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/solid-meta/head.md
156
162
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/vite-plugin-solid.md
157
163
  - https://v2-rebuild--solid-docs-v2.netlify.app/reference/vite-plugin-solid/options.md
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solid2-agent-kit",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "Teach AI coding agents (Cursor, Claude Code) to write correct Solid 2.0 instead of React or Solid 1.x — always-applied rules, an agent skill, and a mechanical pattern gate.",
5
5
  "type": "module",
6
6
  "license": "MIT",