Parent: #465. Found while validating #815; prerequisite for its moving-Symbol-key witness.
makeSymbolObj stores its caller's borrowed []const u8 directly in the primitive sidecar. Symbol(description) passes bytes returned by ToString, which may point inside a GC-managed String cell. The sidecar's description is unmanaged bytes, not a traced String edge. Once that string is collected or relocated, the live Symbol retains a dangling description even though its identity remains correct. Symbol.for and internal makeSymbol share this constructor.
Confirmed on the #815 candidate with both ReleaseSafe and Debug moving checkpoints: one actual nursery evacuation, a changed subject pointer, one key coercion, correct Symbol key identity and correct getter result, but the name becomes get [U] instead of get [x] in the forced tree-walker. This is independent of #815's VM naming flags. Debug diagnostics record [1,true,true,"get [U]",true] (call count, getter identity, held identity, accessor name, key identity).
The ownership boundary must retain the description in the Symbol owner's stable arena before publishing the sidecar, preserving absent versus empty descriptions and canonical WTF-8. Do not pin source strings, suppress moving GC, or patch the name consumer.
Acceptance:
- Own description storage at the shared Symbol construction boundary, including registered and internal Symbols.
- Regression coverage for borrowed/mutable source bytes, absent/empty/non-ASCII descriptions, real moving checkpoints, and allocation-failure cleanup.
- Verify metadata consumers and Symbol conformance, no-GIL/TSan, and issue-linked commit/CI evidence.
Exact retained diagnostics: /Users/chris/.cache/zig-js-vm-naming.7Wj8Ri/diagnostic-debug.log and the compressed ReleaseSafe binaries/hashes. No performance or blanket GC-safety claim.