fix(dashboard): name the remedy when a provider mutation needs a configured owner - #6428
Conversation
Design Review (Fable 5) — ✅ PASSDesign-level review of Design review complete. I examined the full diff (backend denial branch in Design-Verdict: PASS A real dead-end, fixed at the right seam: a machine-readable code scoped to the one caller class that saw live buttons, mirroring the existing stale-session discrimination pattern. The deny decision and audit path are untouched; the discriminator is negatively pinned for app tokens, non-local subjects, and unauthenticated callers; the parser extraction gives every mutation surface one place to learn future codes; and the spec is updated in the same commit. The "buttons render but can't succeed" symptom remains by explicit maintainer direction, so guidance-on-refusal is the proportionate shape here. [DESIGN-REVIEWED] d9a352c |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
First Principles Review (Fable 5) — ✅ PASSPremise-level review of All checks complete. The change is well-grounded: single backend gate covers every mutation, all mutation-rendering surfaces route through the one shared parser, the discrimination rule mirrors an existing precedent, and the committed screenshots/harness follow a massive existing repo convention. Two description claims don't match the diff (both in the harmless direction). Final review: First-Principles-Verdict: PASS Every item traces to one observed defect — a live button answering with a dead-end What this change shipsIntent: on a no-owner local install, replace the bare
Watch
Backend sibling count, for the record: all mutation endpoints funnel through [FIRST-PRINCIPLES-REVIEWED] d9a352c |
UX Review (Fable 5) — ✅ PASSUX-level review of UX-Verdict: PASS The dead-end Suggestions
[UX-REVIEWED] d9a352c |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
d865415 to
3098f48
Compare
3098f48 to
493d00e
Compare
|
BLOCKING — settings Link as a fourth action-row member (max-two-buttons-per-row): fixed in 493d00e.
The Link moved OUT of the action-row flex container entirely — it now renders on its own line below the row (fragment sibling, FINDING — "then sign in again" insufficient because
Verified against |
493d00e to
5d58d04
Compare
|
Subtraction — delete the 1-consumer re-export shim at
The re-export is gone; Subtraction — drop
Correct — the constants were a leftover from an earlier gate-appeasement attempt superseded by the enumerated exclusion. Inlined as on base, with a comment pointing at the config entry that licenses them. Watch — the
The body's "What changed" now carries a dedicated bullet declaring the exclusion, why it exists (the extraction moved the two command literals onto new lines, and terminal commands are wire strings), its repo-wide scope, and the closed-set growth rule. |
5d58d04 to
def30a7
Compare
def30a7 to
f5d19cb
Compare
|
Watch — the remedy is Slack-gated for the very population that hits it: fixed (the bridging clause) + needs-a-decision (the structural question), in f5d19cb.
The guidance now opens with the bridge — "This action needs a configured owner, which KiroCrew identifies by Slack member ID" — across the backend message and all 12 locales, so the Slack destination is explained rather than sprung. The structural half is real and is a maintainer decision, so I am putting it here rather than filing an unactionable issue: today the Slack Suggestion — use the destination field's exact label: fixed in f5d19cb.
Guidance and backend message now quote the exact label, across all locales. Suggestion — thread reply/comment refusals lack the settings link: fixed in f5d19cb.
|
f5d19cb to
9703210
Compare
…igured owner
On a standalone local install (no configured owner), the read endpoints
accept the signed machine-local dashboard subjects, so the Changes panel
renders live provider-mutation buttons (auto-merge, ready, resolve,
reply, comment, submit-review) — but every mutation is refused with a
bare `{"error": "forbidden"}` 403 (`_authorize_owner_request`, scoped to
reads by #117). The click dead-ends with no way to tell what to do.
Backend: the no-owner mutation denial now carries a machine-readable
`code: "owner_not_configured"` plus an actionable message — but ONLY for
signed machine-local dashboard subjects (empty app claim + subject in
local-app/local-startup), the exact caller class that could see live
buttons. Every other caller (app tokens, non-local subjects,
unauthenticated) keeps the generic body, mirroring the discrimination
rule of `stale_owner_session_response`. The deny decision itself is
unchanged: mutations remain owner-only.
Frontend: `pullRequestErrorDetails` moves to a shared module
(`utils/pullRequestErrors.ts`, re-exported from PullRequestPanel for
existing consumers), recognizes the code, and swaps in a localized
guidance message; every provider-mutation surface (Changes-panel
actions, review-thread resolve/reply/comment, Code Review Sage publish)
renders it, with a link to Settings → Channels → Slack where the
Owner ID lives. New i18n keys propagated across 12 locales + en-XA.
Adversarial review: 2 rounds, dual-model (GPT + Opus) blind reviewers.
Round 1: 1 actionable (guidance missing from thread/publish surfaces —
fixed by the shared module) + 1 locale nit (de "Owner-ID" → "Owner ID",
fixed). Round 2: 0 actionable; 1 advisory adopted (single parse in the
resolve error span), 1 noted in PR body (DraftReviewActions now unwraps
JSON-envelope error text like its sibling surfaces).
9703210 to
d9a352c
Compare
Problem / Motivation
On an install with no configured owner, a signed machine-local dashboard session
can read provider data and therefore sees live pull-request mutation controls.
Clicking one of those controls is correctly denied by the owner gate, but the
generic
403 forbiddenresponse gives the user no way to tell what is missing orhow to recover.
Why it matters
The UI currently presents an actionable control and then dead-ends. Users cannot
distinguish a missing owner from authentication, provider, or permission failure,
and the same refusal appears on several mutation surfaces. The remedy must be
consistent without weakening the owner-only authorization boundary or revealing
owner state to app tokens and unauthenticated callers.
What changed (motivation → approach → change)
machine-local dashboard subject (
local-app/local-startup) reaches amutation with no owner configured, return coded 403
owner_not_configuredwith the recovery steps. App tokens, non-local subjects, missing claims, and
unauthenticated callers retain the generic forbidden body.
website/src/utils/pullRequestErrors.ts. PullRequestPanel, IssuePanel,CommentThreads (resolve/reply/comment), and Code Review Sage import the shared
parser directly; there is no PullRequestPanel re-export shim.
Slack settings” link on every affected surface. The link sits outside the
two-button confirmation row.
gate enumerates only the two provider CLI wire commands (
gh auth loginandglab auth login) that the extracted parser returns verbatim.docs/system-specs/modules/learn-cron-dashboard.md.with both sides preserved:
missing_code=1262,opaque_body=18,dynamic_status=43,_compliant=1389.The screenshot harness runs the built SPA with fixed fixtures and clock, waits
for slot hydration, the exact 403 response, link visibility, and fonts, uses the
current role=tab contract and reduced motion, and selects the product's System
font while blocking Google Fonts origins. It contains no sleep, retry, timeout
relaxation, or visual tolerance. Two independent browser processes produced
byte-identical formal images.
Global overlap audit covered every open PR. #4085 and #4895 have real pair
conflicts and are themselves larger/main-conflicting; #6428 is easier-first.
#5161, #5305, #6646, #6789, and #6614 intersect only distinct provider/UI/locale
semantics and merge cleanly. No contributor hunk from those PRs is replaced.
Tests
PytestUnraisableExceptionWarning promoted to errors.
suites plus error-to-agent and remote-crew handoff coverage.
npm run build/ TypeScript: passed (6806 modules).passed.
B18B51E30A8465FC72A45FEC5BB505B313BD4F96C3D50C23D4909AB44EABEAF7;full
89E64ADDEB983E6CC105E165E07D484A95408EB4392DCF60A0292FEFB157695E.Manual verification
The formal full-page and review-size captures were visually inspected. The
refusal explains why Slack is involved, names the exact “Owner Slack member ID”
field, requires gateway restart and re-sign-in, preserves the Cancel/Confirm
layout, and links to
/settings/channels/slack.Screenshots / video
Full dashboard context
Related Issues
N/A — this fixes the dead-end recovery path without changing owner identity
policy. A non-Slack standalone owner identity remains a separate product choice.
Checklist
Contribution License Agreement