Skip to content

Permission refusals drawn as failures: a no-access list offers Retry, and an unreadable related list shows a red "You do not have permission to perform this action." #27841

Closed

Description

@objectstack-fleet

domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

Filing gate: ③ triage-directed split. Triage on #27520 (6495077487) said: 「Each row is either taken, with its own card where it needs code」. This card is row 20.
Business reason: a member who may not read something is told, in effect, that something broke. Retry on a no-access list promises a fix a retry cannot bring. A red action-error line on a record the member opened only to read says they did something wrong, when they did nothing.
Reader who acts: domain:ui (the list's load-error state; the record page's related lists).
Duplicate search: search_issues "no-access state offers Retry permission refusal related list red You do not have permission to perform this action" → #26190 and #23529, both closed, both different (a form toast's reason, and MCP refusal details). New.

What happens (row 20 of #27520, 18.3.0 RC walkthrough at c4c515ad)

  1. Member, desktop, a list the member cannot read. The no-access state offers Retry, which cannot help.
  2. On an invoice the member can read, the Line Items section shows a red "You do not have permission to perform this action." although the member did nothing.

Mechanism (read on origin/main, not reproduced here)

  • ui/packages/plugin-list/src/ListView.tsx has a forbidden load state (list.loadErrorForbiddenTitle / …Message) and a list.retry button. Its own comments already reason that a retry cannot change an API-disabled refusal. Measure whether the forbidden (403) and unauthorized states still draw Retry.
  • The related list's refusal line reads a generic action-permission sentence ("You do not have permission to perform this action. Contact your administrator if you need access."). Measure which component draws the Line Items section (plugin-detail RelatedList or the record page's related column), and what it draws for a refused read.

Expected (the row's own suggestion)

  • A permission refusal (403) on a list read offers no Retry. A transient failure (network, 5xx) keeps it.
  • A related list the member cannot read is drawn as a quiet, neutral "No access" line, not a red error, and not an action-refusal sentence. Once readable, it renders as before.

Acceptance

  • One measured repro on current main first, for each half.
  • Tests:
    • A 403 list read draws no Retry. Control: a 500 still draws Retry.
    • A refused related-list read draws the neutral line with no destructive styling and no action sentence. Control: a readable related list draws its rows.
    • An ablation for each.
  • No new i18n key expected (reuse the existing no-access copy); if one is needed, it is Clause-②: yes (widening).

Links back: #27520 (row 20)


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

    Claim: PM loop round 1
    Session: session_01Utp6CbDAucNyQ4UtzCJKyh
    Account: os-bill
    Branch: claude/project-thread-8rp5dl (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-27841
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: ui/packages/plugin-list/src/ListView.tsx load-error state; the related-list component that draws a refused read (as measured); tests beside; root .changeset/; ⛔ RecordDetailView.tsx apiHandler and the RelatedList.tsx ~L811 docblock (in-flight PR #27793) (stop on breach; explain in the report)
    Container & model: S, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier on fec60e00: no path-derived mandate), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: a 403 list read offers no Retry (transient failures keep it); an unreadable related list draws a neutral no-access line, not a red action-refusal; pinned with controls and ablations
    Thread-read: none
    Premise: holds
    Serial constraints cleared: in-flight: PR #27793 (regions excluded); merge origin/main before the PR

    Dispatched 2026-10-30T01:41Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
    os-dev-report

    {
      "issue": 27841,
      "status": "done",
      "branch": "claude/project-thread-8rp5dl",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27856",
      "session": "session_01Utp6CXjatjJmMJkdAQoyqn (card thread, mode:thread; seat session_01Utp6CbDAucNyQ4UtzCJKyh)",
      "premise_still_valid": true,
      "summary": "Premise measured true on origin/main 38651598: ListView drew list-error-retry for every load-error kind but api-disabled, so a 403 offered Retry; RelatedListLoadFailed drew every kind as role=alert with destructive styling and Retry, and preferred declaredUserMessage, which on a refused read is the server's generic permission_denied action sentence. Now ListView omits Retry for forbidden too; RelatedListLoadFailed draws forbidden as role=status, neutral border/background and muted icon, no Retry, and always the existing list.loadErrorForbidden* copy. No new i18n key, no copy changed; other kinds unchanged.",
      "files_changed": [
        ".changeset/refused-read-no-retry.md",
        "ui/packages/plugin-list/src/ListView.tsx",
        "ui/packages/plugin-list/src/__tests__/ListView.forbiddenNoRetry.test.tsx",
        "ui/packages/plugin-list/src/__tests__/ListView.errorStateComponent-7143.test.tsx",
        "ui/packages/plugin-detail/src/RelatedList.tsx",
        "ui/packages/plugin-detail/src/__tests__/RelatedList.readFailed-26415.test.tsx"
      ],
      "tests": "fa457cd3: pnpm --dir ui exec vitest run (4 files: ListView.forbiddenNoRetry, ListView.errorStateComponent-7143, ListView.loadErrorKind, RelatedList.readFailed-26415) Test Files 4 passed (4), Tests 39 passed (39), lock VERDICT command-exit 0. Ablation: ListView.tsx and RelatedList.tsx restored to 38651598 via git restore, markers absent (grep -c 0 0), same new tests: 2 failed | 14 passed (16), VERDICT command-exit 1, failures = the two refusal tests (Retry present; role alert where status expected); restored by git checkout HEAD, git diff HEAD empty, blob hashes = HEAD. Tests import src, so no dist rebuild in the ablation. type-check plugin-list + plugin-detail (tsc --noEmit && tsconfig.test.json) VERDICT command-exit 0 on the pre-merge head; merge brought no change under either package.",
      "gates": "dispatch-gates --commands on fa457cd3: 34 derived, each recorded as `<command> :: exit 0` (all 34 exit 0, check-sdui-manifest included); --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. pnpm check:new-line-citations (ui): 0 new, exit 0. CI families: in_progress at report time, see PR check-runs.",
      "line_budget": "n/a — no ledgered file touched",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "deviations": [
        "Commit trailers on the three branch commits carry a mistyped session id (session_01Utp6CqyAqjJmMJkdAQoyqn, two letters off from this run's session_01Utp6CXjatjJmMJkdAQoyqn); the thread branch fails force-push criterion 1, so history is left as is. PR body footer carries the right id.",
        "Harness post-PR assignee/reviewer step skipped per the maintainer's routing rule; relay read-back shows no assignees.",
        "Live-stack repro not run; the main-tree reading is the ablation run plus the code reading of declaredUserMessage."
      ],
      "mcp_calls": "1 — mcp__claude-code-remote__subscribe_pr_activity (PR event subscription, not a GitHub write); no mcp__github__ calls",
      "api_writes": "2 — relay pr_create (repository_dispatch -> POST /repos/objectstack-ai/objectstack/pulls, draft, read-back identical 5427 bytes); this os-dev-report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/27841/comments). git pushes not counted.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — unauthorized (401) still offers Retry in ListView and RelatedList; the card names 403 only. dedupe words: sign in required retry, 401 list retry",
        "carrier: none · noted, not filed — ListView forbidden panel keeps its text-destructive/60 ShieldAlert icon. dedupe words: no access panel destructive icon"
      ]
    }

    Report taken 2026-10-30T02:15Z.

  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

    ACCEPT → PR #27856 comment 6513485783 · 2026-10-30T02:30Z


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uipm:dispatchedpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions