Summary
Once #148 surfaces direct-fetch HTTP status on customcontent_orphan_observed, the viewer can also use that information at render time to distinguish "user lacks permission to the underlying CC's source page" from generic "load failed". This unlocks a more actionable empty state — pointing the user toward requesting access rather than just offering a retry.
Prerequisite
Depends on #148 — currently loadCustomContentWithOrphanRecovery returns directFetchStatus: 'other_error' for both 403 and 5xx and thrown failures. The viewer cannot tell which it was without that issue's work.
Also stacks on #151 — the generic "couldn't load" empty state is the universal fallback; this issue adds a smarter variant on top.
Why this matters
A real failure mode at scale: a Confluence page contains a macro whose customContent is owned by a different page that the viewing user does not have read permission for (common after page copies, space-level permission changes, or restricted-page hierarchies). Today these users see a silent blank macro and have no way to discover that the fix is "ask for access to the source page" rather than "report a bug".
Proposed UX
When the viewer detects the load failed with HTTP 403 (via the new direct_fetch_status / direct_fetch_http_status plumbed by #148):
┌─────────────────────────────────────────────┐
│ 🔒 You may not have permission to view │
│ this diagram's source content │
│ │
│ The diagram is stored on a Confluence │
│ page you can't access. Ask the page owner │
│ for read access, or contact your admin. │
│ │
│ [I have permission, retry] │
└─────────────────────────────────────────────┘
- Lock icon and the phrase "permission" make the cause unambiguous
- Retry button covers the case where the user thinks they should have access (e.g., admin granted but Confluence still caching)
- No customContentId shown — the user can't use it for anything; admin can find it via the Mixpanel orphan event if needed
Decision: only render this for HTTP 403, not other auth-flavored failures
False positives on permission language (telling a user "you don't have permission" when they actually do) erode trust more than false negatives (saying "couldn't load" when it was really permission). Be conservative on what gets the lock-icon variant.
Out of scope
- Server-side enrichment of the lock-state message with the actual source page title or URL (would require a separate read against the V2 API to look up container metadata, which by definition the user doesn't have access to). Keep the message generic.
- Editor-mode equivalent — editors generally trigger their own permission errors during save, separate flow
- Bulk admin tooling to identify which users hit which restricted pages — analytics task, not UX
Related
Summary
Once #148 surfaces direct-fetch HTTP status on
customcontent_orphan_observed, the viewer can also use that information at render time to distinguish "user lacks permission to the underlying CC's source page" from generic "load failed". This unlocks a more actionable empty state — pointing the user toward requesting access rather than just offering a retry.Prerequisite
Depends on #148 — currently
loadCustomContentWithOrphanRecoveryreturnsdirectFetchStatus: 'other_error'for both 403 and 5xx and thrown failures. The viewer cannot tell which it was without that issue's work.Also stacks on #151 — the generic "couldn't load" empty state is the universal fallback; this issue adds a smarter variant on top.
Why this matters
A real failure mode at scale: a Confluence page contains a macro whose customContent is owned by a different page that the viewing user does not have read permission for (common after page copies, space-level permission changes, or restricted-page hierarchies). Today these users see a silent blank macro and have no way to discover that the fix is "ask for access to the source page" rather than "report a bug".
Proposed UX
When the viewer detects the load failed with HTTP 403 (via the new
direct_fetch_status/direct_fetch_http_statusplumbed by #148):Decision: only render this for HTTP 403, not other auth-flavored failures
False positives on permission language (telling a user "you don't have permission" when they actually do) erode trust more than false negatives (saying "couldn't load" when it was really permission). Be conservative on what gets the lock-icon variant.
Out of scope
Related
src/components/Viewer/GenericViewer.vue— should match the style of the existing recovery banner and the new tier-1 empty state from feat(ux): show empty-state UI when macro fails to load (NULL_DIAGRAM) #151src/model/ApWrapper2.ts:540-587— wheredirectFetchStatuswould be propagated to the renderer