Skip to content

feat(web): show analysts no control the API refuses them - #2552

Merged
Makisuo merged 10 commits into
observer/shellfrom
observer/read-only-pages
Nov 13, 2026
Merged

Makisuo merged 10 commits into
observer/shellfrom
observer/read-only-pages

Conversation

@Makisuo

@Makisuo Makisuo commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

Sixth layer of the Analyst role, on top of #2550. The API already refuses an analyst every change outside dashboards; this layer stops the dashboard from offering the ones that would only fail, on Errors, incidents, investigations and status pages.

What changed

  • Policy table. It now names the changes a member can make and an analyst cannot: error_issues:triage, incidents:respond, investigations:run, status_page_incidents:post, recommendations:triage. The API enforces them in one place (the analyst gate), so these exist for pages to read.
  • Dashboards are untouched. An analyst builds, edits and shares dashboards like every other role, so the dashboard pages have no grant checks and look the same for everyone.
  • Errors list. Without error_issues:triage: no row selection or bulk bar, the status and severity pickers are plain readouts, the right-click menu has no status or claim entries, and the x, s and p shortcuts do nothing.
  • An error issue. Status and severity are readouts; no Claim, Extend or Release, no Unmerge, no pull request Attach or Detach, no comment box. The timeline, comments, occurrences and linked pull requests stay.
  • Get it fixed. Needs investigations:run. Without it, an analyst sees an investigation that already ran but is offered neither a new one nor the agent prompt.
  • Incidents. Without incidents:respond: no Acknowledge on an alert incident or an inbox row; no Resolve, Link issue or Unlink on an anomaly. Controls that already need alerts:manage are unchanged.
  • Status pages. Without status_page_incidents:post: no Post incident, Post update, Resolve, Edit details or Delete. Controls that already need status_pages:edit are unchanged.
  • Investigations. Without investigations:run: no investigate bar on the list, and no Investigate on an incident or anomaly. A finished investigation still opens.
  • Lab. /lab/alerts, /lab/anomaly-alerts and /lab/status-pages take ?role=admin|member|analyst. /lab/errors?role= and /lab/error-issue?role= render the pages for either viewer.

Members, engineers and admins see what they saw before. In Clerk mode a page learns the viewer's grants a moment after it opens, so their write controls appear with that answer, as on the alerts pages.

Screenshots

Errors:

Member Analyst
Issue
Activity
List

Incidents and status pages:

Member Analyst
Alert incident
Inbox
Status page incident

Verified

  • apps/web: the dashboards, errors, alerts hub and incident, anomalies, investigations, status pages and lab tests, 923 passed. Scene tests pair an analyst with a member on each gated page.
  • packages/domain: policy tests, 16 passed.
  • @maple/web typecheck, the CI lint config on every changed file, the UI class pattern check and the kit index.
  • The lab pages driven in a headless browser for the screenshots.

Known gaps

  • "Create alert" in a dashboard widget's menu still shows to an analyst; the alerts create page it opens already needs alerts:manage.
  • The errors list's bulk bar has no screenshot: the lab never draws it. A scene test on the real page covers it for both viewers.
  • The investigation detail page still offers Resolve, Run again and Reopen to an analyst on this layer; the next layer removes them.
  • "New rule" and an incident's "Edit rule" link are shown to members and analysts alike, though saving a rule needs alerts:manage. That predates this work.
  • The anomaly detail page and the investigations list have no lab entry, so they are covered by scene tests only.

The policy table now names the changes a member can make and an observer cannot. The API refuses them all in one place; these names are for the dashboard, so a page can leave out controls that would only fail.

On the dashboards list, a board and the templates gallery, a viewer without dashboards:edit gets no create, import, duplicate, tag, delete, edit mode, rename or version restore, and one without dashboards:share gets no Share or Embed chart. Opening a board with ?mode=edit shows them the read view. Reading stays whole: time range, variables, version history and JSON export.
@coderabbitai

coderabbitai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 17 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: baf0b2af-465e-439f-b716-23026b28cac6
Commits

Reviewing files that changed from the base of the PR and between 2cc964e and ebf5352.

Files selected for processing: 60

  • apps/web/src/lab/alerts-lab/update.ts
  • apps/web/src/lab/alerts-lab/view-page.ts
  • apps/web/src/lab/alerts-lab/view.ts
  • apps/web/src/lab/anomaly-alerts-lab.ts
  • apps/web/src/lab/entries/error-issue.ts
  • apps/web/src/lab/entries/status-pages.ts
  • apps/web/src/lab/errors-lab/hub-step.ts
  • apps/web/src/lab/errors-lab/model.ts
  • apps/web/src/lab/errors-lab/update.ts
  • apps/web/src/lab/errors-lab/view.ts
  • apps/web/src/lab/registry.ts
  • apps/web/src/lab/viewer-role.ts
  • apps/web/src/lab/worst-case-lab/view-errors.ts
  • apps/web/src/page/alerts/hub/analyst.scene.test.ts
  • apps/web/src/page/alerts/hub/view-inbox.ts
  • apps/web/src/page/alerts/hub/view.ts
  • apps/web/src/page/alerts/incident/analyst.scene.test.ts
  • apps/web/src/page/alerts/incident/incident.scene.test.ts
  • apps/web/src/page/alerts/incident/view.ts
  • apps/web/src/page/anomalies/detail/analyst.scene.test.ts
  • apps/web/src/page/anomalies/detail/command.ts
  • apps/web/src/page/anomalies/detail/message.ts
  • apps/web/src/page/anomalies/detail/model.ts
  • apps/web/src/page/anomalies/detail/update.ts
  • apps/web/src/page/anomalies/detail/view-body.ts
  • apps/web/src/page/anomalies/detail/view-sidebar.ts
  • apps/web/src/page/anomalies/detail/view.ts
  • apps/web/src/page/errors/hub/analyst.scene.test.ts
  • apps/web/src/page/errors/hub/command.ts
  • apps/web/src/page/errors/hub/message.ts
  • apps/web/src/page/errors/hub/model.ts
  • apps/web/src/page/errors/hub/row-menu.ts
  • apps/web/src/page/errors/hub/selection.ts
  • apps/web/src/page/errors/hub/update.ts
  • apps/web/src/page/errors/hub/view-list.ts
  • apps/web/src/page/errors/hub/view-row.ts
  • apps/web/src/page/errors/hub/view.ts
  • apps/web/src/page/errors/issue/analyst.scene.test.ts
  • apps/web/src/page/errors/issue/command.ts
  • apps/web/src/page/errors/issue/composer.scene.test.ts
  • apps/web/src/page/errors/issue/message.ts
  • apps/web/src/page/errors/issue/model.ts
  • apps/web/src/page/errors/issue/update.ts
  • apps/web/src/page/errors/issue/view-activity.ts
  • apps/web/src/page/errors/issue/view-agent-handoff.ts
  • apps/web/src/page/errors/issue/view-panels.ts
  • apps/web/src/page/errors/issue/view-sidebar.ts
  • apps/web/src/page/errors/issue/view.ts
  • apps/web/src/page/errors/kit/grants.ts
  • apps/web/src/page/errors/kit/pickers.ts
  • apps/web/src/page/investigations/list/analyst.scene.test.ts
  • apps/web/src/page/investigations/list/view.ts
  • apps/web/src/page/kit/session-grants.ts
  • apps/web/src/page/status-pages/editor/analyst.scene.test.ts
  • apps/web/src/page/status-pages/editor/model.ts
  • apps/web/src/page/status-pages/editor/view-canvas.ts
  • apps/web/src/page/status-pages/editor/view-panel.ts
  • apps/web/src/page/status-pages/editor/view.ts
  • packages/domain/src/policy/permissions.ts
  • packages/views/src/ui/INDEX.md
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@maple-review-bot

This comment has been minimized.

On the errors list and an issue, a viewer without error_issues:triage reads everything but gets no status or severity picker, claim, release, merge or unmerge, pull request attach or detach, comment box, row selection or bulk bar. The pickers become plain readouts, and the list's x, s and p shortcuts do nothing for them.

The Get it fixed panel needs investigations:run: without it, an observer sees an investigation that already ran but is offered neither a new one nor the agent prompt, which tells an agent to claim the issue.
@Makisuo Makisuo changed the title feat(dashboards): show observers no control that changes a dashboard feat(web): show observers no control that changes a dashboard or an issue Nov 13, 2026
@maple-review-bot

This comment has been minimized.

A viewer without incidents:respond gets no Acknowledge on an alert incident or an inbox row, and no Resolve, link or unlink on an anomaly. One without status_page_incidents:post gets no Post incident, Post update, Resolve, Edit details or Delete on a status page. One without investigations:run gets no investigate bar on the investigations list and no Investigate on an incident or anomaly.

Controls that already need alerts:manage or status_pages:edit are unchanged. The labs take ?role=admin|member|observer.
@Makisuo Makisuo changed the title feat(web): show observers no control that changes a dashboard or an issue feat(web): show observers no control that changes shared work Nov 13, 2026
@maple-review-bot

This comment has been minimized.

@maple-review-bot

This comment has been minimized.

# Conflicts:
#	packages/domain/src/policy/roles.ts
An analyst builds and shares dashboards like any other role, so the dashboard pages no longer read session grants or hide controls, and the dashboards:edit and dashboards:share permissions are gone. Errors, incidents, investigations and status pages still hide what the API refuses.
@Makisuo Makisuo changed the title feat(web): show observers no control that changes shared work feat(web): show analysts no control the API refuses them Nov 13, 2026
@maple-review-bot

maple-review-bot Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 10/10 · safe to merge. All 59 files read; gates match the domain permission table and every page that reads grants fetches them, only the brief pre-session window differs from before.
no findings · tests covered · risk medium

Hides the dashboard's write controls wherever the viewer's grants lack the matching permission: errors list and issue, incident and anomaly pages, status page editor, investigations list. The gates read the same domain permission table the API enforces, and the new scene tests pin both the analyst and member views.

  • Errors list and issue drop every error_issues:triage control, with x/s/p inert.
  • Incident and anomaly pages gate acknowledge, resolve, link and investigate on incidents:respond and investigations:run.
  • Status page editor hides post, update, resolve and delete behind status_page_incidents:post.
  • The policy table gains five member-level permissions the pages now read.

ebf5352 · What was checked, coverage and telemetry details are on the Maple / review check. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo added this pull request to stack #2556 November 13, 2026 12:45
@Makisuo
Makisuo merged commit d6f95be into main Nov 13, 2026
46 of 58 checks passed
@Makisuo
Makisuo deleted the observer/read-only-pages branch November 13, 2026 12:56
@maple-review-bot

Copy link
Copy Markdown

Maple: after this shipped

⚠️ Something changed after this shipped. Details below.

Deployed to maple-web (pr-2544) at 2026-11-13 13:20 UTC as cd8f430, the first version after the merge (the merge commit itself never reported). Compared the 60 minutes before and after.

New errors since the deploy:

  • HttpClientError (maple-web, 4/h)
Operation Calls/h Errors p95
FetchErrorsSummary 0 → 0 0.0% → 0.0% 0 → 0 ms

Maple looks once, an hour after the first deploy that carries the merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant