Closures used to be stringified by VarDumper::asPrimitives (inspector)
and DumpContext::exportClosure (storage), so the frontend only rendered
them as highlighted PHP when they happened to be top-level strings that
matched a regex. Nested closures under keys like `definition`/`reset`
fell through to the default JsonView truncation, and `fn(` without a
space after `fn` never matched the regex.
Route every serialization path through a shared structured descriptor
instead:
- Kernel: new `Inspector\ClosureDescriptor::describe()` returns
`{__closure, source, file, startLine, endLine}` for any Closure.
- Kernel: new `Inspector\Primitives::dump()` wraps VarDumper::asPrimitives
with a recursive pre-walk that swaps Closures for the descriptor. All
inspector controllers (config, auth, cache, git, routing, request,
translation, Yii2 inspect, CLI inspect) now use it.
- Kernel: `DumpContext::dumpObject` and `Dumper::sanitizeForJson` emit
the same descriptor for the storage path, so every dumped collector
payload preserves closure source.
- Frontend: `JsonRenderer` detects the descriptor at any depth via a
pre-walk that replaces markers with sentinel strings rendered as
`CodeHighlight` through JsonView.String. Regex is also broadened to
accept `fn(` and `function(` without a trailing space as a fallback.
https://claude.ai/code/session_01SQvGds4wd69Vg1f5V7FMxw