diff --git a/.agents/skills/webjs/references/built-ins.md b/.agents/skills/webjs/references/built-ins.md
index 262843896..1c48337a5 100644
--- a/.agents/skills/webjs/references/built-ins.md
+++ b/.agents/skills/webjs/references/built-ins.md
@@ -93,6 +93,8 @@ html``
That emits `/public/app.css?v=` in production and gets the immutable year; the same url un-marked gets a ~1h cap and can serve stale bytes from a CDN after a deploy until something purges it. `asset()` resolves on the server; the browser has no resolver and returns the path unchanged. Call it from a PAGE, LAYOUT, or metadata route, which render only on the server. Inside a component that ships to the browser it silently costs you the caching: hydration is a full client re-render, so the bare path overwrites the hashed one and the asset downloads twice. The url stays valid either way, so this is a convention rather than a `webjs check` rule (`webjs doctor` does flag the plain form, see below). Under `webjs.basePath`, include the prefix yourself (`asset('/app/public/x.css')`): the framework base-path-prefixes only the urls it emits, so an author-written url is already yours to prefix. Two more constraints: call it INSIDE the render function, because a module-scope call is a side effect the elision analyser reads as client work and it ships the whole module; and mark only files that change with a DEPLOY, because the hash is memoized for the process lifetime, so a `public/` file rewritten in place at runtime would keep its old url while being served `immutable` for a year. Off in dev, so dev output is byte-identical. Only `public/` paths resolve; anything else (and a path that fails to resolve) is returned untouched.
+`asset()` is a PROVIDER SEAM: `@webjsdev/server` installs the resolver at boot by importing `@webjsdev/core` and calling a setter, and that only reaches your app when both sides load the SAME copy of core. Two copies on disk are two independent sets of module-scope state, so the setter lands on one and your `asset()` reads the other, which returns bare paths and never says why. `cspNonce()` and the bound-form identity resolver behind `