Repository navigation
fix(console): disabled reasons name the grant and who gives it - #3695
Conversation
…ilure describer Stop, Delete, the Agents list row actions, the Secrets page, Create Secret, Namespace create, plugin save, repository choices, Preset load and Agent sharing now show the API's own 400/409 sentence, a 429's wait from Retry-After, and never ask to check entered values where there is no form.
…ret refusals Fixtures that invented server sentences now send the API's real ones (contract mismatch, lifecycle conflict, generic conflict, Secret name conflict); the Secret delete recheck reads the real referenced-by text, and a 429 with Retry-After names the wait.
…s through the describer Create Agent's 409 shows the API's typed reason; a denial for a viewer who read the Installation (an Installation administrator) names nobody to ask. describeFailure passes an AccessNeed through. console.md no longer says backend error text is never rendered.
Findings 2539, 2545, 2548, 2549. One module, agents/access-reasons, holds the shared reasons: what the reader's access does not include, then "Ask an Installation administrator for <grant>", naming the action the API checks (Agent operate to stop, Agent delete to delete, AgentRevision read per version, Configuration update, Secret read/update/delete/operate, SandboxPolicy read, Agent manage_policy and SandboxPolicy update). - Agent detail: the hidden-version panel, new-version hint and empty history no longer send readers to an "Agent administrator". - Agent page 403 says Agent read from an Installation administrator instead of "ask its owner to share it". - Agents list: row notes say which grant is missing; Stop hides when a first deployment failed and nothing runs. - Secrets: create, read, replace and delete denials name their grant; an empty list without create says no Secrets are shared with you; a bound Secret the reader cannot read says its name is hidden instead of "Metadata unavailable (access denied)". - Network policy 403s name their grant; the editing setting says only an Installation administrator who administers the Agent can change it; the internal "No Driver configurations" sentence is gone. - Namespaces empty state, workspace, diagnostics and save refusals use the same Installation administrator wording.
…e list's hidden Stop The network policy reason lost its typographic apostrophe when it moved to access-reasons, so the page no longer matched the existing copy. A new case pins that the Agents list offers no Stop once a failed first deployment left nothing running, and the policy denial case checks the internal Driver configurations sentence is gone.
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
…s its tooltip
The row's reserved note line is one short line, so a full reason was cut
to "Your access does not incl…". The line now says which grant is
missing ("Needs Agent delete", "Needs Agent operate", "Needs operate
and delete"); the whole reason, with who gives it, is its tooltip and
what the icons' description reads.
Findings 2559-2564 from review-3690. - Secrets page: a taken Secret name shows the API's sentence; only the generic conflict text keeps the "may already be in use" hedge (2559). - One explainedConflict() helper decides whether a 409 explains itself, shared by the describer, the Secrets page, the Secret picker and Namespaces, so a missing message is treated the same everywhere (2562). - Stop, deletion, the Agents list and Secret deletion pass their own sentence for a conflict the API does not explain (2561). - describeFailure drops its unused need/administrator options; tooManyRequests is module-private, and the shared retryAfterText() also serves the Logs tab, showing a long Retry-After in minutes (2562, 2564). - The malformed-request text no longer asks for a request ID that some views do not show; a Secret 400 without an API sentence keeps "Check the input and enter it again" (2564). - Storybook: secretsDeleteConflict and createRepositoriesRecovery send real API sentences instead of the placeholder (2560). The console flow doc says which backend text failure views show (2564). - Tests: Agent page Stop and Agents list row Stop/Delete 400 and 409, including a conflict the API does not explain (2563); the Secrets name conflict and a 10-minute Retry-After.
…a 429 in the Secrets refusals case The new Agent page Stop case waited for idle fetches without tracking them, and the Secrets refusal case's POST count and the reload case's settle text still assumed the old name-conflict hedge and one 429.
…console-6 # Conflicts: # apps/controller/src/console/secrets.mts
…the credential retry's read refusal Review of #3690: the inline Create Secret dialog is exercised with a 409 that carries no message, the API's generic conflict text and the real duplicate-name sentence; the first two keep the dialog's name and Namespace guidance. The credential-access Retry case asserts the read's own refusal sentence instead of any text between the fixed ends.
|
Codex review: needs changes before merge. What this changesThis PR names missing grants and the Installation administrator who can provide them, and hides Stop after an observed failed first deployment. Example: A reader lacks operate and delete access to Guarded Agent.
Review scores
ProductKind: Bug fix · Worth it: Yes · Fix scope: Complete Merge readiness⛔ Needs changes before merge - 1 item remains The shared grant wording is useful, but the previously reported Stop regression remains reachable when a newer deployment is unreadable. Priority: P2 Before merge
Findings
Provenance
Tests
Agent review detailsHow this fits togetherThe browser Console combines advisory permissions, filtered revision lists, and API responses into controls and recovery guidance; the controller authorizes each operation independently. flowchart TD
User[Console user] --> Console[Browser Console]
Console --> API[Controller API]
API --> IAM[Selected IAM Driver]
API --> State[Agent and revision state]
API --> Responses[Permissions and filtered revisions]
Responses --> Console
Technical reviewBest possible solution: Keep the shared grant guidance and remove the Stop suppression based on filtered revision history. Do we have a high-confidence way to reproduce the issue? Source establishes the failing path: a caller with Agent operate can read failed v1 but not queued v2, so the list hides Stop despite an operation the caller can cancel; no target code was executed. Is this the best way to solve the issue? Centralizing the access wording fits the existing Console, but filtered revision history cannot establish that no deployment remains stoppable. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against 6eb7ad4dd34d. Provenance checked
TestingProof path: shipped entry point. SecurityNone. EvidenceAcceptance criteria:
What I checked:
Likely related people:
LabelsLabel changes: No label changes. Label justifications:
Rating scale6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior. WorkflowClawSweeper edits this one comment on every review. Comment HistoryReview history (1 earlier review cycle)
Reviewed October 25, 2026, 10:10 PM ET / October 26, 2026, 02:10 UTC (Revision 2). |
…n administrator holds that grant Namespace IAM Roles cannot grant create, so asking "an administrator for Configuration create access" pointed at a grant nobody else can give.
Every device-authorization route authorizes Agent create in the Namespace, which no Namespace Role grants.
The squash's tree equals #3690's final head, already merged here, so the conflicted files keep this branch's side; the merged tree is unchanged.
…ach request's grants - access-reasons.mts owns the ask clause (askForGrant, askAdministratorTo, notIncluded) and the create reasons; list.mts, workspace, diagnostics, detail, create, secret-picker, presets, namespaces, secrets and console.mjs build their text from it (finding 2578). - Device login 403s name what each request checks: Create Agent's start, Agent detail's start, poll and cancel (finding 2577). - The Agents list decides a failed first deployment again on each deployment read, so a retained list keeps Stop for a retried first deployment (finding 2580). - An unknown administrator with no Namespaces keeps the access hint (finding 2583). - Nits: drop unused AGENT_DEPLOY_DENIED, 'Needs Agent operate and delete', Storybook step, Sandbox log grant comment (finding 2582). - Tests take the new reasons from tests/helpers/refusal-text.mjs (finding 2579).
…d first deployment The retried deployment's record is routed as queued, as the in-memory fixture runs no deployment work. The unknown-administrator case denies the bootstrapped Namespace so the empty state shows. The row line stays 'Needs operate and delete': with 'Agent' it overflows the reserved line at 1280 pixels (169 vs 147), and the test now asserts that it fits.
|
@clawsweeper re-review |
|
🦞👀 Re-review progress:
|
…asons # Conflicts: # apps/controller/src/console/agents/deletion.mts # apps/controller/src/console/agents/list.mts # docs/reference/console.md # tests/browser/console-agent-detail.test.mjs # tests/browser/console-agent-draft-guards.test.mjs
…t list.mts #3699 added a case that imported accessDenied from list.mts; this branch folded it into access-reasons.mts (notIncluded), so the file failed to load in Checks and Conformance 1. The case now compares with namespaceListDenied, the written-out expectation.
Follows #3690 (landed), which added the shared failure describer.
Problem
Disabled controls and refused requests often did not say which grant was missing, or who could give it (findings 2539, 2545, 2548, 2549):
Bound Secret · sec_… · Metadata unavailable (access denied).Change
agents/access-reasons.mtsis the one module that words who grants access. It owns the ask clause (askForGrant"Ask an Installation administrator for .",askAdministratorTofor a step only that role can take, and the clause for use inside a sentence),notIncluded(what the reader's access does not include, then the grant), and the create reasons.list.mts'saccessDenied,AGENT_CREATE_DENIEDand save refusals, and the inline copies in workspace, diagnostics, detail, create, secret-picker, presets, namespaces, secrets andconsole.mjs, now build their text from it. The ask ends as the API's named denials from fix(iam): name the missing grant in a caller's own denial #3699 do. Each grant is the action the API checks for that request. I read each handler or route to confirm it:packages/occ/src/index.ts,authorizeDeviceAuthorizationScopeand the start, poll and cancel paths):docs/reference/console.mdanddocs/reference/console/versions-and-channel-drafts.md. The Storybook scenario step text is updated too.Left alone, because other open work owns these lines:
logs.mtsand the console's Namespace-loss lines (another open console access PR).credentials.mts(fix(console): read the default slack account's token keys and show channel refusals on credentials #3686). It still useslist.mts'saskInstallationAdministrator; a TODO there says to move it toaskAdministratorToonce fix(console): read the default slack account's token keys and show channel refusals on credentials #3686 lands.Verification
tsc(console and tests), eslint and prettier are clean on the touched files.1c4224af(merged with mainbcddbeae, which includes fix(iam): name the missing grant in a caller's own denial #3699), on a remote test VM in a private network namespace.tests/integration/caller-denial-api.test.tspasses on the merge. Headcf034d58changes onlytests/conformance/http-errors.test.mjs(a fix(iam): name the missing grant in a caller's own denial #3699 case imported the removedaccessDenied); it passes 122/122, and the console conformance and integration files pass 89/89.5bc2b940): both device login cases (Create Agent start; Agent detail start, poll and cancel), the Agents list Back case for a retried first deployment, and the unknown-administrator Namespaces case.Agents list row for a reader (before, after):
Risks
failed.message()and the detail Delete refusal show the API's named denial when it sends one, and this PR's text otherwise. The create wording agrees with fix(iam): name the missing grant in a caller's own denial #3699's: only an Installation administrator can create these.