Skip to content

fix(dashboard): name the remedy when a provider mutation needs a configured owner - #6428

Merged
bolichen97 merged 1 commit into
mainfrom
fix/owner-not-configured-hint
Aug 30, 2026
Merged

fix(dashboard): name the remedy when a provider mutation needs a configured owner#6428
bolichen97 merged 1 commit into
mainfrom
fix/owner-not-configured-hint

Conversation

@CrysisDeu

@CrysisDeu CrysisDeu commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

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 forbidden response gives the user no way to tell what is missing or
how 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)

  • Keep every provider mutation owner-only. When and only when a signed
    machine-local dashboard subject (local-app / local-startup) reaches a
    mutation with no owner configured, return coded 403 owner_not_configured
    with the recovery steps. App tokens, non-local subjects, missing claims, and
    unauthenticated callers retain the generic forbidden body.
  • Parse provider mutation envelopes once in
    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.
  • Replace the server's English fallback with localized guidance and an “Open
    Slack settings” link on every affected surface. The link sits outside the
    two-button confirmation row.
  • Add the two strings across all 12 locales plus en-XA/manual catalogs. The i18n
    gate enumerates only the two provider CLI wire commands (gh auth login and
    glab auth login) that the extracted parser returns verbatim.
  • Record the authorization/privacy invariant in the owning system spec,
    docs/system-specs/modules/learn-cron-dashboard.md.
  • Resolve the generated error-code baseline from latest main and regenerate it
    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

  • Strict backend no-owner matrix: 8/8 passed with RuntimeWarning and
    PytestUnraisableExceptionWarning promoted to errors.
  • Latest-main UI integration: 154/154 passed across the three changed consumer
    suites plus error-to-agent and remote-crew handoff coverage.
  • Production npm run build / TypeScript: passed (6806 modules).
  • Error-code contract and i18n relative-to-main checks: passed.
  • Repository backend/static gates, Node syntax check, and non-PNG diff check:
    passed.
  • Deterministic screenshot SHA-256: crop
    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

Owner-not-configured guidance, review crop

Full dashboard context

Owner-not-configured guidance, full dashboard

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

  • At most two commits (exactly one), with a Conventional Commits title
  • Existing affected tests pass and regression coverage is included
  • Self-review completed; repository style gates pass
  • Owning system spec and localization catalogs are updated
  • No secrets, credentials, or internal references in the diff

Contribution License Agreement

@CrysisDeu
CrysisDeu requested a review from a team August 27, 2026 23:03
@CrysisDeu
CrysisDeu requested a review from a team as a code owner August 27, 2026 23:03
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 27, 2026
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of d9a352c3f0298d614fe2ae740124810b8e5d08e2 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Design review complete. I examined the full diff (backend denial branch in source_providers.py, the shared pullRequestErrors.ts extraction, the three UI surfaces, the i18n gate exclusion, tests, and the spec update) against the PR's stated intent.

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

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of d9a352c3f0298d614fe2ae740124810b8e5d08e2 and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] d9a352c

False positive or not applicable? A repository writer can comment:
/ai-review override gpt d9a352c3f0298d614fe2ae740124810b8e5d08e2: <one-sentence reason>

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — ✅ PASS

Premise-level review of d9a352c3f0298d614fe2ae740124810b8e5d08e2 — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

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 forbidden — and lands at the single gate and single parser that own it.

What this change ships

Intent: on a no-owner local install, replace the bare forbidden that provider-mutation buttons dead-end into with guidance naming the exact setting — a FIX.

  1. No-owner mutation refusal carries owner_not_configured + remedy, signed-local subjects only — justified, mirrors stale_owner_session_response
  2. Changes-panel action refusals show localized guidance + settings link — justified (the reported defect)
  3. Thread resolve/reply/comment refusals show the same guidance + link — justified, sibling of same cause
  4. Code Review Sage publish refusal shows guidance + link — justified, sibling of same cause
  5. Sage publish errors now unwrap the JSON envelope — rides along, declared, free via shared parser
  6. pullRequestErrorDetails moved to utils/pullRequestErrors.ts — justified move (3 new consuming surfaces; 5 import sites total)
  7. Two i18n keys across 12 locales + en-XA — mandated by the i18n invariant
  8. i18n gate exclusion ^(?:gh|glab) auth login$ — justified; enumerated, closed set of wire strings
  9. Committed capture harness + 2 PNGs — repo convention (340 capture-*.mjs siblings, ~2,700 PNGs under temp-screenshots/)
  10. Spec doc + error-code baseline updated — mandated (spec-same-commit rule; ratchet snapshot)

Watch

  • The description says pullRequestErrorDetails is "re-exported from PullRequestPanel for existing consumers" — no re-export exists in the diff (grepped PullRequestPanel.tsx: it only imports the new path); the existing consumers (IssuePanel, the test) were re-pointed instead. Nothing breaks, but a human merging on the description would believe a compat shim exists. Fix the description, or nothing.
  • The description says the baseline snapshot "absorbs ~24 unrelated _compliant drift entries" — the diff changes exactly one line (_compliant 1387→1389, the ratchet for this PR's two coded bodies). The claim describes an earlier revision; correct it so the release-time reader doesn't hunt for drift that isn't there.

Backend sibling count, for the record: all mutation endpoints funnel through _authorize_owner_request via _owner_mutation_response (grepped: the only no-flag call site, source_providers.py:4450), and the one other parser consumer (IssuePanel) has zero mutations — so no unfixed siblings.

[FIRST-PRINCIPLES-REVIEWED] d9a352c

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — ✅ PASS

UX-level review of d9a352c3f0298d614fe2ae740124810b8e5d08e2 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

UX-Verdict: PASS

The dead-end forbidden becomes a self-contained remedy — cause, exact setting, and a direct link — rendered consistently across all three refusal surfaces and verified in pixels.

Suggestions

  • In the crop screenshot the guidance sits beside the existing "Ask the agent" link, giving the same error two competing next steps; the settings link is the real remedy, so consider suppressing askAgent on the ErrorNotice when ownerNotConfigured is true in PullRequestActions.

[UX-REVIEWED] d9a352c

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed d9a352c3f0298d614fe2ae740124810b8e5d08e2 — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] d9a352c

Verdict parsed from the review's SHA-scoped output markers for commit d9a352c3f0298d614fe2ae740124810b8e5d08e2.

False positive or not applicable? A repository writer can comment:
/ai-review override fable d9a352c3f0298d614fe2ae740124810b8e5d08e2: <one-sentence reason>

@CrysisDeu
CrysisDeu force-pushed the fix/owner-not-configured-hint branch from d865415 to 3098f48 Compare August 27, 2026 23:21
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Aug 27, 2026
@CrysisDeu
CrysisDeu force-pushed the fix/owner-not-configured-hint branch from 3098f48 to 493d00e Compare August 27, 2026 23:37
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 27, 2026
@CrysisDeu

Copy link
Copy Markdown
Collaborator Author

BLOCKING — settings Link as a fourth action-row member (max-two-buttons-per-row): fixed in 493d00e.

Ownerless install -> confirm auto-merge -> Cancel, Confirm, Ask agent, and Slack settings render together -> action row exceeds the cap and wraps.

The Link moved OUT of the action-row flex container entirely — it now renders on its own line below the row (fragment sibling, mt-1.5), so the confirm state's row carries exactly Cancel + Confirm. Verified with a fresh capture of the refusal flow (screenshots in the PR body, re-pinned to 493d00e).

FINDING — "then sign in again" insufficient because owner_id is boot-read: fixed in 493d00e.

DashboardState.owner_id remains empty until restart, so mutations still return 403.

Verified against messaging.py (api_slack_config stages KIROCREW_OWNER_ID via env_updates and flags restart_required; state.owner_id is constructor-read). The backend message and the owner_not_configured_guidance value across all 12 locales + en-XA now read "…, restart the gateway, then sign in again."

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Aug 27, 2026
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 28, 2026
@CrysisDeu
CrysisDeu force-pushed the fix/owner-not-configured-hint branch from 493d00e to 5d58d04 Compare August 28, 2026 00:07
@CrysisDeu

Copy link
Copy Markdown
Collaborator Author

Subtraction — delete the 1-consumer re-export shim at PullRequestPanel.tsx:117: fixed in 5d58d04.

two import paths for one symbol is a second spelling.

The re-export is gone; IssuePanel.tsx and the test file import pullRequestErrorDetails from utils/pullRequestErrors directly.

Subtraction — drop GH_AUTH_LOGIN/GLAB_AUTH_LOGIN and inline the literals: fixed in 5d58d04.

the new config pattern, not the ALL-CAPS name, is what passes the gate.

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 ^(?:gh|glab) auth login$ i18n-gate exclusion ships undeclared: fixed (prose-only) in the PR body.

permanent gate surface: any future file may hardcode those strings ... a human should see it land.

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.

@CrysisDeu
CrysisDeu force-pushed the fix/owner-not-configured-hint branch from 5d58d04 to def30a7 Compare August 28, 2026 00:43
@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 28, 2026
@CrysisDeu
CrysisDeu force-pushed the fix/owner-not-configured-hint branch from def30a7 to f5d19cb Compare August 28, 2026 01:23
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: passed Eligible automated validation passed for the current revision labels Aug 28, 2026
@CrysisDeu

Copy link
Copy Markdown
Collaborator Author

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 "why Slack?" leap is unexplained ... confirm this three-step remedy is really the only path.

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 KIROCREW_OWNER_ID is the only owner identity, so on a Slack-less standalone install the remedy requires entering a Slack-shaped ID the user may not have. Two candidate resolutions were already weighed at this PR's inception: (a) a non-Slack owner identity for standalone installs, or (b) allowing signed machine-local sessions to run these mutations with no owner configured (reverses #117's scoping). The maintainer chose guidance-over-widening for this PR; whether to also build (a) or (b) is a product call I'd ask the maintainer to rule on — happy to implement either as a follow-up.

Suggestion — use the destination field's exact label: fixed in f5d19cb.

the settings field is labeled "Owner Slack member ID".

Guidance and backend message now quote the exact label, across all locales.

Suggestion — thread reply/comment refusals lack the settings link: fixed in f5d19cb.

they render the guidance without the "Open Slack settings" link that Resolve, panel actions, and Sage publish all get.

ReplyBox gained an optional errorAction slot; both the per-thread reply box and the top-level comment box now render the shared OwnerSettingsLink beside the guidance on the coded refusal, so all five mutation surfaces are consistent.

@CrysisDeu
CrysisDeu force-pushed the fix/owner-not-configured-hint branch from f5d19cb to 9703210 Compare August 28, 2026 01:32
@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision merge conflict Branch has merge conflicts with its base — author must resolve before merge and removed readiness: checking Automated validation is still running labels Aug 28, 2026
@bolichen97
bolichen97 enabled auto-merge August 29, 2026 23:59
…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).
@bolichen97
bolichen97 force-pushed the fix/owner-not-configured-hint branch from 9703210 to d9a352c Compare August 30, 2026 00:39
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: passed Eligible automated validation passed for the current revision merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: checking Automated validation is still running labels Aug 30, 2026
@bolichen97
bolichen97 merged commit ce5f4ec into main Aug 30, 2026
76 of 77 checks passed
@bolichen97
bolichen97 deleted the fix/owner-not-configured-hint branch August 30, 2026 04:25
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 30, 2026
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.

2 participants