Skip to content

fix(console): use the console's own required-field check on the remaining forms - #2918

Merged
freeqaz merged 8 commits into
mainfrom
ux-fixes-127
Oct 19, 2026
Merged

freeqaz merged 8 commits into
mainfrom
ux-fixes-127

Conversation

@freeqaz

@freeqaz freeqaz commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

#2907 (UX plan C3) moved most console forms to the console's own required-field check, but four forms still called reportValidity (finding 1979):

  • Plugin selections JSON on the Plugins tab,
  • Slack channel credentials (Channel Secrets),
  • the authentication source form on Credentials,
  • the workspace file editor.

On these forms, a refused field showed the browser's transient bubble. Nothing was left under the field, aria-invalid was not set, and the workspace editor's message came back as a bubble on every later Save.

A second Plugins tab problem (finding 1978): a refused save, or one with an unknown outcome, moved focus to the refusal or to Reload even when the reader had moved focus to another control in the section while the save ran, such as Plugin selections JSON. Only the page and the disabled Save button should give up focus.

Change

  • Field errors (1979): the four forms use validateFields (and novalidate where there is a <form>), as feat(console): form field pattern with linked help and errors #2907 did. Each refused field shows its error under the field, linked with aria-describedby and aria-invalid, and gets focus. A closed Plugin selections JSON section opens first. Errors clear when the field is fixed, and a workspace file's error also clears when Reload replaces the text.
  • Duplicate message removed: Plugin selections JSON no longer repeats its error in the grey status line, because the error now sits under the field.
  • Dead code removed: Create Agent's invalid listener is gone. That form is novalidate and nothing calls checkValidity on it, so the listener never ran. Removing it adds no hunk that fix(console): let the create preflight carry any configuration both creates accept #2833 or fix(console): grant Agent access to accepted Configuration Secrets #2255 conflicts with.
  • Focus guard (1978): the Plugins refusal (revealStatus) and the unknown-outcome Reload (focusSaveOutcome) are now scoped to the Save button, not the section. They take focus only from the page or from the disabled Save button. Every message string is unchanged.
  • Slack channel credentials: this form switches too, but no supported state reaches its check. Save stays disabled until both token bindings are set, and a picker with a binding passes the check. The only way to fail it is a cross-Namespace binding, which the API refuses. So the change keeps the form consistent with the others and has no test. An earlier head tested it by rewriting a response into that refused state, and that test is removed.
  • Storybook: Pages / Agent detail gets three stories: Plugin selections JSON error, Authentication source required fields and Workspace file too large. Their steps are in scripts/console-storybook/form-field-workflow.md, steps 5-7.
  • Sign-in form not changed: console.mjs still uses reportValidity. Sign-in is out of scope for this change and stays as it is.

UX plan C3 follow-up. Fixes findings 1979 and 1978.

Verification

  • New tests/browser/console-form-fields-2.test.mjs (mapped to checks-browser-2, suite audit has no issues). It has 8 tests:
    • Plugin selections JSON at 1440 and 390 px.
    • Authentication source form: API key with no Secret.
    • Workspace editor: a file over 16 KiB, with Save pressed twice, then fixed by an edit and by Reload.
    • Plugins focus: refusal (409) and unknown outcome (500). Each runs once with focus left on Save and once with focus moved to Plugin selections JSON.
  • On main: 6 of the 8 tests fail. The three forms fire the browser's invalid event (its bubble) and show no field error. In both "moved" focus tests, focus is pulled off Plugin selections JSON. The two "takes focus from the Save button" tests pass on main, as intended: they check that the guard is not too strict.
  • Pinned: the new file passed 20/20 runs on 2 CPUs (taskset -c 14-15, one test at a time). That run still included the Slack case, which has since been removed.
  • Full local browser run (both browser suites, 6 files at a time): 692/693 passed. The one failure is docs-compute-matrix, which needs github-slugger; that package is not installed locally. After the final review fixes, I re-ran the touched Plugins, save-refusal, detail and drafts files (84/84 passed) and every workspace and credentials file (221/221 passed).
  • prettier --check and eslint --max-warnings 0 pass on the touched files.

Open-PR conflicts. For each PR touching these files, I compared git merge-tree against main with git merge-tree against this head:

PR conflict hunks vs main vs this head
#2833 0 0
#2255 create.mjs 2, docs 3 same
#2258 docs 3 same
#906 6 (docs, storybook) same; the scenarios.mjs hunk is byte-identical
#2599 detail.mjs 1 (import block), others same count. The detail.mjs import hunk grows by the validateFields line
#2596, #2915 0 0
#2518, #2445, #2253, #2524 unrelated files same
ux-fixes-126 (#2916, and its test-only branch) none on these files none

Media. These are the real console after a refused Save, at 1440 and 390 px. "Before" shows the browser bubble: it is gone on the next click or keypress, and nothing stays under the field. "After" shows the error under the field. The media commit is removed again before landing, as the maintainer workflow requires: committed media is linked by SHA, then deleted. The Storybook stories above show the same states. I rendered them locally at 1440 and 390 px from the prepared fixture assets, and each shows its error under the field.

Media commit 101672499.

Form Width Before (main) After
Plugin selections JSON 1440
Plugin selections JSON 390
Authentication source (listbox closed) 1440
Authentication source (listbox closed) 390
Workspace editor 1440
Workspace editor 390

Picker listbox open over the error right after the check (finding 1988, as on Create Agent since #2907):

Risks

Low. A note on 1978: in browsers where clicking a button does not focus it (Safari and Firefox on macOS), focus stays on whatever the reader last used. If that was Plugin selections JSON, the refusal now leaves focus there instead of taking it. The refusal is still a role=alert block, so it is announced. The changes are client-side only and keep every string. One visible difference: when a required Secret picker gets focus from the check, its listbox opens over the new error, as it already does on Create Agent since #2907 (filed as finding 1988). In the authentication source form, the Service Accounts method shows the error "API key Secret is required." under a field labelled "Service account token Secret". That message already existed and is now persistent instead of a bubble. It has the same cause as finding 1987.

…ning forms

Plugin selections, Slack channel credentials, the authentication source
form and the workspace file editor still used reportValidity, so a refused
field showed the browser's transient bubble. They now use validateFields
with novalidate, as #2907 did for the other forms (finding 1979). The
Create Agent invalid listener is removed: that form is novalidate and
nothing calls checkValidity on it, so it never ran.

A refused or unknown Plugins save now takes focus only when it is on the
page or the disabled Save button, not when the reader moved it to another
control in the section while the save ran (finding 1978).
…ms and the Plugins focus guard

Each test fails on main: Plugin selections JSON, Slack channel credentials,
the authentication source form and the workspace editor fired the browser's
invalid event (its bubble) and showed no error under the field, and a
Plugins save refusal or unknown outcome pulled focus off the Plugin
selections JSON the reader had moved to. Mapped to checks-browser-2.
…e field error

The error now sits under Plugin selections JSON, so the grey status line
no longer says it a second time.
…es the text

Reload sets the editor without an input event, so the error under the
field and aria-invalid stayed on a file that was now valid. The read
paths that reset the custom validity now clear the field error too
(internal review).
@freeqaz
freeqaz requested a review from a team as a code owner October 19, 2026 07:44
@clawsweeper

clawsweeper Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. labels Oct 19, 2026
@clawsweeper

clawsweeper Bot commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge.

What this changes

This PR replaces transient browser validation bubbles with persistent field errors on four remaining console forms and preserves focus during failed Plugins saves.

Example: Save AGENTS.md containing 16,385 ASCII characters.

  • Before: The browser shows a transient validation bubble.
  • After: The editor receives focus and shows a persistent error explaining the 16 KiB limit.

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) Useful and focused production changes with credible static visual evidence, held by an unsupported regression fixture and unmet repository UI requirements.
Proof confidence 🦐 gold shrimp (3/6) Real behavior proof is not required for maintainer- or bot-authored pull requests.
Patch quality 🦐 gold shrimp (3/6) 3 actionable review findings remain.

Product

Kind: Bug fix · Worth it: Yes · Fix scope: Complete
User problem: Invalid entries leave only transient browser bubbles, and failed Plugins saves can pull focus away from the control the user moved to.
Reason: This maintainer-authored follow-up implements the direction already established by the merged console validation work without adding options or changing accepted input.

Merge readiness

⛔ Needs changes before merge - 3 items remain

Keep open: this PR usefully completes the existing console validation pattern, but its Slack test and UI evidence handling need focused repairs.

Priority: P2
Reviewed head: 1016724995390e07fc1b4b1a10fa4afed3f28ce6

Before merge

  • Replace the unsupported cross-Namespace Slack fixture (P2) - This interception rewrites a valid Configuration into a cross-Namespace Secret binding solely to enable Save while making the picker invalid. Current controller admission rejects that binding, and PostgreSQL decoding rejects it as persisted state, so the claimed older Configuration is not a supported application state. This violates AGENTS.md's test-integrity requirement and does not prove a reachable Slack regression. Use a scenario reachable through supported owners, or remove this case if the existing Save gate makes the invalid-field branch unreachable.
  • Add Storybook coverage for the changed form interactions (P3) - The form behavior changes here, but no matching Storybook scenarios or workflow instructions are updated. AGENTS.md explicitly requires those updates for console UI changes, and existing Storybook scenarios already support actions such as clicking Save. The PR's explanation that these states require a Save click therefore does not establish a limitation. Extend the owning scenarios to expose the new validation and focus states.
  • Remove committed PR screenshots from the source tree (P3) - This PR adds thirteen verification images under .pr-media/2918. AGENTS.md explicitly prohibits committing PR evidence and requires native GitHub attachments instead. Remove the added image files from the final diff and retain the evidence through reviewer-accessible attachments.

Findings

  • [P2] Replace the unsupported cross-Namespace Slack fixture — tests/browser/console-form-fields-2.test.mjs:143-146
  • [P3] Add Storybook coverage for the changed form interactions — apps/controller/src/console/agents/plugins.mjs:298-302
  • [P3] Remove committed PR screenshots from the source tree — .pr-media/2918/plugin-json-390-after.png:1

Tests

  • Missing end-to-end proof: Supply the required walkthrough showing repeated validation, correction or Reload, and Plugins focus preservation; the inspected screenshots establish only static after-Save states.
Agent review details

How this fits together

The OCC console validates form input before submitting existing Agent and workspace API requests; these changes affect field feedback and focus without changing server authorization or persistence.

flowchart TD
  User[Console user] --> Forms[Agent forms]
  Forms --> Validation[Shared field validation]
  Validation --> Errors[Persistent errors and field focus]
  Forms --> API[Existing OCC API]
  API --> Outcome[Save outcome and focus guard]
Loading

Technical review

Best possible solution:

Retain the shared validation changes, repair the unsupported Slack test, add the affected Storybook scenarios, and publish compliant UI evidence outside the source tree.

Do we have a high-confidence way to reproduce the issue?

Current main still calls reportValidity in the affected owners and scopes Plugins outcome focus to the entire section; supplied before/after images corroborate the visible validation problem, but no target code was run during this review.

Is this the best way to solve the issue?

Reusing validateFields and the existing focus helpers is the smallest appropriate production fix; the fabricated Slack response does not establish a supported regression scenario.

Full review comments:

  • [P2] Replace the unsupported cross-Namespace Slack fixture — tests/browser/console-form-fields-2.test.mjs:143-146
    This interception rewrites a valid Configuration into a cross-Namespace Secret binding solely to enable Save while making the picker invalid. Current controller admission rejects that binding, and PostgreSQL decoding rejects it as persisted state, so the claimed older Configuration is not a supported application state. This violates AGENTS.md's test-integrity requirement and does not prove a reachable Slack regression. Use a scenario reachable through supported owners, or remove this case if the existing Save gate makes the invalid-field branch unreachable.
    Confidence: 0.99
  • [P3] Add Storybook coverage for the changed form interactions — apps/controller/src/console/agents/plugins.mjs:298-302
    The form behavior changes here, but no matching Storybook scenarios or workflow instructions are updated. AGENTS.md explicitly requires those updates for console UI changes, and existing Storybook scenarios already support actions such as clicking Save. The PR's explanation that these states require a Save click therefore does not establish a limitation. Extend the owning scenarios to expose the new validation and focus states.
    Confidence: 0.99
  • [P3] Remove committed PR screenshots from the source tree — .pr-media/2918/plugin-json-390-after.png:1
    This PR adds thirteen verification images under .pr-media/2918. AGENTS.md explicitly prohibits committing PR evidence and requires native GitHub attachments instead. Remove the added image files from the final diff and retain the evidence through reviewer-accessible attachments.
    Confidence: 1

Overall correctness: patch is incorrect
Overall confidence: 0.96

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 6e6b95815617.

Provenance checked

  • Required-field validation and unreachable Create Agent invalid listener keeps the original intent (e4f63d4: The shared form-field PR established persistent linked errors, explicitly deferred these remaining forms, and identified the Create Agent listener as unreachable after novalidate.)
  • Plugin JSON validation keeps the original intent (33646c3: The plugin editing workflow rejects non-object JSON before saving Agent selections; this PR preserves that check and changes its presentation.)
  • Plugins refusal and unknown-outcome focus changes intended behavior with a stated reason (a380df2: Refusals were brought into view after Save disabled itself; the narrower guard retains that behavior while preserving focus explicitly moved elsewhere.)
  • Workspace read baseline and validation state keeps the original intent (27b406f: Workspace reads compare against the textarea-normalized baseline to avoid rewriting untouched CRLF files; clearing the displayed field error leaves that behavior intact.)

Testing

Proof path: shipped entry point.

Security

None.

Evidence

Acceptance criteria:

  • pnpm test:files -- tests/browser/console-form-fields-2.test.mjs
  • node scripts/ci/run-tests.mjs audit
  • pnpm lint
  • pnpm format:check
  • pnpm check:workspace

What I checked:

Likely related people:

  • Free Wortley: Raw commit 7474d8b adds apps/controller/src/console/dom.mjs:534 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 7474d8b8e5b8; files: apps/controller/src/console/dom.mjs)
  • kevinlin-openai: Raw commit 835b1cc adds apps/controller/src/console/agents/workspace.mjs:65 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 835b1cc9b775; files: apps/controller/src/console/agents/workspace.mjs)
  • stevenlee-oai: Raw commit 33646c3 adds apps/controller/src/console/agents/plugins.mjs:137 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 33646c3738a9; files: apps/controller/src/console/agents/plugins.mjs)

Review metrics

Metric Value Why it matters
Production and test changes Production +33/-26; browser tests +315 Most new coverage protects distinct field-error and asynchronous-focus behavior; the Slack case instead depends on unsupported response data.

Labels

Label changes:

  • add P2: Persistent validation errors and preserved focus improve recoverable Agent editing workflows with limited blast radius.
  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🦐 gold shrimp.
  • add status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action.

Label justifications:

  • P2: Persistent validation errors and preserved focus improve recoverable Agent editing workflows with limited blast radius.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action.

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Provide the required interaction walkthrough and native GitHub media attachments.

Rating scale

6/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.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

Reviewed October 19, 2026, 4:00 AM ET / 08:00 UTC.

…ies for the field errors

The Slack channel test rewrote a Configuration into a cross-Namespace
Secret binding, which the API refuses, so it proved no reachable state:
Save stays disabled until both tokens are bound, and a bound picker
passes. The switch to validateFields stays for consistency.

Storybook gains Plugin selections JSON error, Authentication source
required fields and Workspace file too large under Pages / Agent detail,
with workflow steps in form-field-workflow.md (review feedback).
@freeqaz
freeqaz temporarily deployed to integration-qa-pr October 19, 2026 08:16 — with GitHub Actions Inactive
@freeqaz
freeqaz temporarily deployed to integration-qa-pr October 19, 2026 08:16 — with GitHub Actions Inactive
@freeqaz
freeqaz merged commit 5963327 into main Oct 19, 2026
48 of 49 checks passed
@freeqaz
freeqaz deleted the ux-fixes-127 branch October 19, 2026 08:35

This branch had an error being deployed

1 failed deployment
integration-qa-pr — b2135a84 Deployed Oct 19, 2026 by freeqaz via QA advisory (OpenShell) #2528
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant