Why: RDM be80ad4 (docs/design/render-node-id-contract.md) accepts workspace tree
node ids (dn-/sec-/legacy-) for review annotate/revise, adds treeNodeId to receipts,
rdm.render.review.view items and trace entries while nodeId stays canonical, points
anchored-to at the tree node id, and marks audit-reader trace entries with
contentMasked=true. Grouping review items by the canonical nodeId left them all
unlocated, and the workspace showed no render trace at all.
Evidence: npx tsc --noEmit exit 0; npx vitest run 39 files / 456 tests passed (12 new
in test/document-render-workspace.test.ts: treeNodeId grouping with nodeId fallback
and orphan rules for annotations and revisions, data-locate targets, annotate/revise
payload carrying the selected tree node id, layer=render filtering, anchored-to
against tree nodes without canonical fallback, masked entries never reading body
fields, empty state); npm run build ok; node scripts/check-knip.mjs 51 <= 51;
e2e/document-renderer.playwright.ts type-checks (tsc exit 0) and
npx playwright test --list loads 13 tests.
Impact: locateNodeId (treeNodeId first, nodeId fallback, otherwise unlocated) is
shared by diagnostics, review grouping and trace. Clicking an annotation or revision
group, a reply, or a trace node locates and re-scrolls like diagnostics. Annotate and
revise send the selected tree node id as nodeId. A 渲染链条 panel reads
rdm.inspect.relations (kind=doc) through the single guarded resource owner (stale
responses dropped) and reloads after preview, submit, export, review and decision
actions. Masked entries show metadata only with a 内容已遮蔽 marker and a panel
notice. The e2e spec is unchanged: §11-5b already asserts that the group title is the
selected tree node, and the leak scan is untouched. Not run against a live backend
or browser here.
Refs: RDM be80ad4 docs/design/render-node-id-contract.md; unified renderer spec 10
§11 items 4, 5 and 7; DESIGN.md §8, §9.2, §11.
Co-authored-by: Cursor <cursoragent@cursor.com>