You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
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
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)
Member, desktop, a list the member cannot read. The no-access state offers Retry, which cannot help.
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-detailRelatedList 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).
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.tsxapiHandler 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.
{
"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"
]
}
Activity
objectstack-fleet commentedon Oct 30, 2026
domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
Claim: PM loop round 1
Session:
session_01Utp6CbDAucNyQ4UtzCJKyhAccount:
os-billBranch:
claude/project-thread-8rp5dl(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27841Domain:
domain:uiSeat:
domain:ui#4File surface:
ui/packages/plugin-list/src/ListView.tsxload-error state; the related-list component that draws a refused read (as measured); tests beside; root.changeset/; ⛔RecordDetailView.tsxapiHandlerand theRelatedList.tsx~L811 docblock (in-flight PR #27793) (stop on breach; explain in the report)Container & model:
S,mode:thread,model: TIER_DEFAULT(dispatch-gates --tieronfec60e00: no path-derived mandate),effort: mediumClause-②: 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
objectstack-fleet commentedon Oct 30, 2026
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.
objectstack-fleet commentedon Oct 30, 2026
domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
ACCEPT → PR #27856 comment 6513485783 · 2026-10-30T02:30Z
Generated by Claude Code