Defect sweep R1 — 4 cross-tenant IDOR holes closed in copy-source mutations, comment-resolve authz, 8 a11y/leak fixes
Multi-agent defect hunt (a11y / resource-leak / effect-correctness / null-safety / convex-correctness), each finding adversarially verified before fix. SECURITY (copy-source convex, inherited by every consumer via npx rr add): user-management mutations cancelInvite / resendInvite / removeTeam / addTeamMember / removeTeamMember all gated requirePermission on args.tenantId but then operated on a raw inviteId/teamId with no ownership match — a tenant-A admin could delete/rotate/mutate tenant-B invites, teams, and team memberships (cross-tenant IDOR). Each now loads the doc and verifies doc.tenantId === args.tenantId before touching it. comments.resolve only checked authentication, not ownership (unlike update/remove) — any signed-in user could toggle resolvedAt on any thread; now mirrors the c.actorId guard. A11Y: icon-only buttons with no accessible name got aria-labels — browser history close, assistant step reorder/delete, image-editor bold/italic + AI send, notion-database calendar prev/next-month + property rename/delete, media-viewer toolbar (IconBtn now forwards its tooltip label to aria-label). LEAK: image-picker UploadTab created a blob URL per pick and never revoked it — added a cleanup effect keyed on the preview URL. effect-correctness + null-safety dimensions came back clean. slices:check 70 ok + tsc green.