Skip to content

Frontend: document form validation rules and error copy register - #49041

Merged
RachelElysia merged 11 commits into
mainfrom
2026-07-validation
Jul 31, 2026
Merged

Frontend: document form validation rules and error copy register#49041
RachelElysia merged 11 commits into
mainfrom
2026-07-validation

Conversation

@RachelElysia

@RachelElysia RachelElysia commented Jul 9, 2026

Copy link
Copy Markdown
Member

Issue

Docs-only. Kicks off the validation standardization workstream — target behavior for a forthcoming shared validation hook and per-form migrations.

Description

  • Extends frontend/docs/patterns.md "Data validation" with 10 subsections: error timing, clearing, priority, submit button state, server-side errors, conditional validation, optional/disabled fields, input hygiene, in-flight lifecycle, visual affordances, and copy register.
  • Adds rules under a new "Validation" section in .claude/rules/fleet-frontend.md.
  • Describes target behavior; upcoming work will be done for a shared validation hook that applies these new patterns as well as per-form migrations

Screenrecording

Testing

  • Reviewed markdown rendering on GitHub for both files
  • QA'd all new/changed functionality manually

Extend Data validation in frontend/docs/patterns.md with 10 subsections
(timing, clearing, priority, submit state, server-side errors, conditional
validation, optional/disabled fields, input hygiene, in-flight lifecycle,
visual affordances, copy register). Add matching terse rules under a new
Validation section in .claude/rules/fleet-frontend.md.
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.32%. Comparing base (703dcf0) to head (0ee1b9d).
⚠️ Report is 574 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #49041      +/-   ##
==========================================
+ Coverage   68.11%   68.32%   +0.21%     
==========================================
  Files        3731     3864     +133     
  Lines      235434   238315    +2881     
  Branches    12520    13285     +765     
==========================================
+ Hits       160371   162837    +2466     
- Misses      60670    61082     +412     
- Partials    14393    14396       +3     
Flag Coverage Δ
frontend 61.29% <ø> (+1.99%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Shorten the Validation section in .claude/rules/fleet-frontend.md to a
strong "read patterns.md" trigger plus a handful of Fleet-specific
anti-defaults, so validation guidance lives in one place. Drop the
"shared validation hook is planned" mention and the backend/UI field key
mapping bullet.
Note that GitOps mode disabling the whole form is a valid reason to
disable the submit button. Explain why field errors clear on focus:
the error text replaces the label, so clearing on focus restores the
label the user needs to see while editing.
Name the mainstream React libraries (Formik, react-hook-form, MUI, Ant
Design) Fleet diverges from and the four categories where it diverges,
so the anti-defaults list has clearer motivation.
@RachelElysia
RachelElysia marked this pull request as ready for review July 13, 2026 17:03
Copilot AI review requested due to automatic review settings July 13, 2026 17:03
Comment thread frontend/docs/patterns.md Outdated

- Trim leading and trailing whitespace client-side before submitting. Send the trimmed value to the API.
- Whitespace-only content in a required field counts as empty.
- Cap free-text `maxLength` to the backend column length via `inputOptions={{ maxLength: N }}` on `InputField`. The native input silently truncates paste. See [Forms](#forms) in the top-level rules.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementing this consistency in #49216

Copilot stopped reviewing on behalf of RachelElysia due to an error July 13, 2026 17:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Updates Fleet’s frontend documentation to standardize React form validation behavior and UX conventions (error timing/placement, submit behavior, copy).

Changes:

  • Rewrites frontend/docs/patterns.md form validation guidance with detailed rules for error display/clearing, submit state, server errors, and lifecycle.
  • Adds an authoritative pointer and summary checklist to .claude/rules/fleet-frontend.md to steer contributors toward the patterns doc.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
frontend/docs/patterns.md Expands and clarifies form validation/UX rules (submit behavior, touched semantics, server errors, lifecycle, copy).
.claude/rules/fleet-frontend.md Adds a “Validation” rules section that links to the authoritative patterns doc and summarizes key divergences.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread frontend/docs/patterns.md Outdated
Comment on lines +351 to +355
- Never show a field's error before the user has interacted with that field. A field becomes interacted-with when the user types into it or blurs it while it holds a value.
- On blur of a field the user has interacted with, run validation and show the resulting error (if any) for that field only. Do not touch errors on other fields.
- On submit, show inline errors on every invalid field simultaneously, then return without submitting.
- Autofill counts as user interaction — treat autofilled fields as touched.
- On an Edit form, pre-filled values that are invalid do not show errors until the user interacts with the field.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, I think we should define dirty ("interacted-with" in L351 would fall into this category I think), touched, any other?

EDIT: we also mention isDirty below so I'd try to stay with just one term, either dirty or interacted-with to narrow down the cognitive load when reading this 😄

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging!! I just addressed this by consolidating to a single dirty term with a definition line and called out the field-level dirty vs form-level isDirty distinction so they don't get conflated <3

Comment thread frontend/docs/patterns.md
- On focus (click-in) of a field that has an error, clear that field's error immediately — do not wait for the user to type a valid value. The error text replaces the field's label (see [Visual affordances](#visual-affordances)), so clearing on focus restores the label and lets the user see what they're editing.
- Re-validate on blur, not on keystroke.
- Typing in one field never clears errors on other fields. Clearing is per-field.
- When a validation becomes irrelevant (e.g. a conditional requirement is removed by toggling a checkbox), clear the newly-irrelevant error immediately.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread frontend/docs/patterns.md Outdated

- Trim leading and trailing whitespace client-side before submitting. Send the trimmed value to the API.
- Whitespace-only content in a required field counts as empty.
- Cap free-text `maxLength` to the backend column length via `inputOptions={{ maxLength: N }}` on `InputField`. The native input silently truncates paste. See [Forms](#forms) in the top-level rules.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread frontend/docs/patterns.md Outdated

#### Conditional / dependent validation

- Cross-field checks (e.g. password + confirmation match) run on blur of either field. The error attaches to the field that is invalid, not to both.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updating so it shows on the confirmation.

Comment thread frontend/docs/patterns.md Outdated
Comment on lines +409 to +410
- The submit handler must guard against a second submission while one is in flight. Do not rely solely on the button being disabled.
- The Cancel button remains enabled during submission and closes the modal immediately. It does not abort the in-flight request; the request completes in the background.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree w/ Copilot here. I think "Cancel" buttons in modals should cancel in-flight requests (I believe react-query does this behind the scenes IF we use useMutation for mutative operations... but we still have lots of occurrences where we use plain Promises, so worth double-checking).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is cancelling requests possible for all actions? Is considering a confirmation dialog a good resolution to this problem for background instances, to ensure the user knows this will or will not cancel the request?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree w/ Copilot here. I think "Cancel" buttons in modals should cancel in-flight requests (I believe react-query does this behind the scenes IF we use useMutation for mutative operations... but we still have lots of occurrences where we use plain Promises, so worth double-checking).

Just checked — we only use useMutation in 3 places (all in policies: resetPolicy, createPolicy, useUpdatePolicyAutomations). And FWIW useMutation doesn't actually auto-cancel in-flight mutations — react-query cancels queries on unmount, but mutations run to completion unless the mutationFn wires up an AbortController itself. So we'd be building this from scratch either way.

Agree it might be worth a future iteration. @mike-j-thomas has also floated dropping the Cancel button entirely (users find it confusing during submission), which might be the cleaner resolution than wiring abort everywhere. For now the doc calls out the resilience requirement so we don't ship the toast-after-unmount bug in the meantime.

Is cancelling requests possible for all actions? Is considering a confirmation dialog a good resolution to this problem for background instances, to ensure the user knows this will or will not cancel the request?

I added a line to address this:

+- The Cancel button remains enabled during submission and closes the modal immediately. It does not abort the in-flight reques
+t; the request completes in the background. We don't require abort because most call sites use plain Promises (not useMutatio +n), and we don't want a confirmation dialog on Cancel — it adds friction to the common case for a rare one.
411 +- Because Cancel doesn't abort, the submission must be resilient to the modal being closed before the request resolves. Guard
+post-success side effects (toast, navigation, cache invalidation) so they don't fire against an unmounted component or a scree
+n the user has already left. Failures on a closed modal are dropped silently — no toast, no re-open.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, (auto-)canceling queries make sense, not mutations (I was wrong on the concept as well, since as soon as a mutative operation has begun, we'd also have to handle cancelation on the server which doesn't make much sense).

Comment thread frontend/docs/patterns.md Outdated
- Form fields are disabled while a submission is in flight — the user cannot edit during the request.
- The submit handler must guard against a second submission while one is in flight. Do not rely solely on the button being disabled.
- The Cancel button remains enabled during submission and closes the modal immediately. It does not abort the in-flight request; the request completes in the background.
- On success, fire the success toast BEFORE navigation (see [Notifications](../../.claude/rules/fleet-frontend.md#notifications) and #48088) and close the modal.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing thanks ai!

@RachelElysia

Copy link
Copy Markdown
Member Author

@mike-j-thomas , @rachaelshaw , @marko-lisica please review at your earliest convenience slack thread

rachaelshaw
rachaelshaw previously approved these changes Jul 14, 2026
Comment thread frontend/docs/patterns.md Outdated
- Form fields are disabled while a submission is in flight — the user cannot edit during the request.
- The submit handler must guard against a second submission while one is in flight. Do not rely solely on the button being disabled.
- The Cancel button remains enabled during submission and closes the modal immediately. It does not abort the in-flight request; the request completes in the background.
- On success, fire the success toast BEFORE navigation (see [Notifications](../../.claude/rules/fleet-frontend.md#notifications) and #48088) and close the modal.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RachelElysia This is confusing. I understood this like we should show toast and then navigate, but I assume you're describing this:

- notify.success/notify.error defer the actual toast creation by one tick (setTimeout). So if a handler calls notify.success(...) and then router.push(...) synchronously, the dismiss-on-navigate listener fires first (clearing whatever was on the old page), and the new toast is created after — landing it on the destination page instead of getting wiped by its own navigation.
- Ordering matters and is load-bearing: call notify.success before router.push/router.replace, not after. Reversing the order can break the toast's auto-dismiss timer (see #48088).

The "Add software" flow (SoftwareCustomPackage.tsx:134-160) is the canonical example:
notify.success(<>Package successfully added.</>);   // called first
...
router.push(PATHS.SOFTWARE_TITLE_DETAILS(...));      // navigation after
So it uploads, calls notify.success immediately on API success, then pushes to the software title details page — the toast survives the route change and shows up there.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging! The "BEFORE" phrasing was ambiguous. I meant the code-call order, not the visual order.

For context, the notify.success defers the toast creation by a tick via setTimeout, so if you call it before router.push, the deferred toast survives the navigation and shows up on the destination page. If you call it after, the dismiss-on-navigate listener wipes it. SoftwareCustomPackage.tsx:134-160 is the canonical example.

Updating to clarify!

Comment thread frontend/docs/patterns.md Outdated
- **No terminal periods.** The error renders in the label slot, and labels don't end with periods.
- Use `fleet` not `team` in new copy. The codebase still uses `team_id` etc.; that stays. See [Terminology](../../.claude/rules/fleet-frontend.md#terminology).

For errors the user can't fix by editing the field — server failures, timeouts, network errors — use a different register: **what happened + what to do**. Example: `Couldn't save your changes. Try again in a few minutes.` This is the one place where periods appear (two sentences).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RachelElysia Is this supposed to display inline?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mike-j-thomas clarified all server side errors will appear in the toast message, but can/should appear inline as well if they are specific to a specific field

@nulmete nulmete left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this up, I think it's going in a good direction 💯
LMK what you think about my comments below.

Comment thread .claude/rules/fleet-frontend.md Outdated

**Read `frontend/docs/patterns.md#data-validation` before adding or editing form validation — that doc is authoritative.** Fleet diverges from what mainstream React libraries (Formik, react-hook-form, MUI, Ant Design) do by default on submit-button behavior, error timing, error position, and copy tone. Pattern-matching from another React app will land you in these specific mistakes:
- No visible required-field indicator (no `*`, no `(required)` suffix). Users discover requirements via post-interaction errors.
- Submit button stays enabled with invalid fields. Only disable during in-flight submission, or when the form is disabled by GitOps mode. Handler shows errors and returns early.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submit button stays enabled with invalid fields

We'll need to make sure we do not fire any API calls even though users can click it

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to clarify the submit handler should not call the API if it finds clientside errors

Comment thread .claude/rules/fleet-frontend.md Outdated
- No visible required-field indicator (no `*`, no `(required)` suffix). Users discover requirements via post-interaction errors.
- Submit button stays enabled with invalid fields. Only disable during in-flight submission, or when the form is disabled by GitOps mode. Handler shows errors and returns early.
- Field errors clear on **focus**, not on typing.
- Re-validate on blur, never on keystroke.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not revalidate on blur if the form is not dirty (i.e. we haven't typed anything yet), right? Otherwise we'll get all those red inputs when clicking in and out of an input field with nothing typed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, this is what I modified to:

  • Empty required field, user tabs through without typing → not dirty → no blur error, submit surfaces it ✓
  • User types a, deletes back to empty, blurs → dirty (sticky) → presence error shows ✓
  • Edit form pre-filled invalid, user tabs through without typing → not dirty → no error ✓
  • Edit form user edits and reverts to initial → dirty (sticky) → validation still fires

Comment thread frontend/docs/patterns.md Outdated
an "optimistic" user experience. The user is only told they have an error once they navigate
away from a field or hit enter, actions which imply they are finished editing the field, while they are informed they have fixed
an error as soon as possible, that is, as soon as they make the fixing change. e.g.
- Never show a field's error before the user has interacted with that field. A field becomes interacted-with when the user types into it or blurs it while it holds a value.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I think this answers my comment above related to Re-validate on blur, never on keystroke.

Comment thread frontend/docs/patterns.md Outdated
Comment on lines +351 to +355
- Never show a field's error before the user has interacted with that field. A field becomes interacted-with when the user types into it or blurs it while it holds a value.
- On blur of a field the user has interacted with, run validation and show the resulting error (if any) for that field only. Do not touch errors on other fields.
- On submit, show inline errors on every invalid field simultaneously, then return without submitting.
- Autofill counts as user interaction — treat autofilled fields as touched.
- On an Edit form, pre-filled values that are invalid do not show errors until the user interacts with the field.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, I think we should define dirty ("interacted-with" in L351 would fall into this category I think), touched, any other?

EDIT: we also mention isDirty below so I'd try to stay with just one term, either dirty or interacted-with to narrow down the cognitive load when reading this 😄

Comment thread frontend/docs/patterns.md Outdated
- Never show a field's error before the user has interacted with that field. A field becomes interacted-with when the user types into it or blurs it while it holds a value.
- On blur of a field the user has interacted with, run validation and show the resulting error (if any) for that field only. Do not touch errors on other fields.
- On submit, show inline errors on every invalid field simultaneously, then return without submitting.
- Autofill counts as user interaction — treat autofilled fields as touched.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autofill would mean we interacted with the field(s). I think touched is implicitly included in interacted-with (as a user, whenever you want to interact with a field, i.e. type something, select a checkbox/radio, you have to touch it first)

Perhaps ^ is more of a "global" rule not specific to autofilling.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I consolidated the section around a single term. touched and "interacted-with" are both gone. Dirty is now the only field-level state name and it's defined as "once the user types into it or the browser autofills it" (sticky for the session). Autofill is folded into that definition rather than a separate bullet, so the redundancy you flagged goes away. Curious what you think of the updated version! Going to push shortly when I address everything

Comment thread frontend/docs/patterns.md
setFormErrors(validateFormData(formData));
};
```
- Presence errors take priority over format errors. If a field is both empty and format-invalid, show the presence error.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we define validation errors, you're saying that we should define something like a priority for each of them (if we have multiple)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the only ones I have seen a second validation along with "is present", in which "is present" should always take priority

Comment thread frontend/docs/patterns.md Outdated
- Whitespace-only content in a required field counts as empty.
- Cap free-text `maxLength` to the backend column length via `inputOptions={{ maxLength: N }}` on `InputField`. The native input silently truncates paste. See [Forms](#forms) in the top-level rules.
- If the max length is unusual (e.g. a 48-character password), show an inline error on the field instead of relying on silent truncation.
- Autofill counts as user interaction — treat autofilled fields as touched.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already mentioned above in L354

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, dropping in the upcoming revision!

Comment thread frontend/docs/patterns.md Outdated
Comment on lines +409 to +410
- The submit handler must guard against a second submission while one is in flight. Do not rely solely on the button being disabled.
- The Cancel button remains enabled during submission and closes the modal immediately. It does not abort the in-flight request; the request completes in the background.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree w/ Copilot here. I think "Cancel" buttons in modals should cancel in-flight requests (I believe react-query does this behind the scenes IF we use useMutation for mutative operations... but we still have lots of occurrences where we use plain Promises, so worth double-checking).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

The new “dirty” definition is internally inconsistent with the later rule that submit marks all fields dirty, which should be clarified to prevent divergent implementations.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Comments suppressed due to low confidence (1)

frontend/docs/patterns.md:351

  • The definition of field-level dirty says it only becomes dirty when the user types or the browser autofills, but a later bullet says submit "marks all fields dirty" (explicit exception). As written, that’s internally inconsistent and could lead to divergent implementations; update the dirty definition to include submit attempts (or rename the submit behavior).
A field is **dirty** once the user has typed into it or the browser has autofilled it. It stays dirty for the session, even if the value returns to its initial state. Field errors gate on `dirty`. Form-level `isDirty` ("form has changes") is a separate concept — see [Submit button state](#submit-button-state).
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

The prior phrasing ("submit marks all fields dirty") stretched the
definition of dirty to cover a system-triggered event. Cleaner to keep
dirty as pure user-interaction and describe submit as a checkpoint that
bypasses the gate.
Copilot AI review requested due to automatic review settings July 30, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

It introduces at least one non-clickable/incorrect doc reference link and a validation rule that is ambiguous enough to cause inconsistent implementations.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Comments suppressed due to low confidence (3)

.claude/rules/fleet-frontend.md:112

  • The link to the validation patterns doc is likely broken in GitHub, because frontend/docs/patterns.md#data-validation is relative to .claude/rules/ and will resolve to .claude/rules/frontend/docs/patterns.md. Use a root-relative or correctly relative path so the rule can be followed easily.
**Read `frontend/docs/patterns.md#data-validation` before adding or editing form validation — that doc is authoritative.** Fleet diverges from what mainstream React libraries (Formik, react-hook-form, MUI, Ant Design) do by default on submit-button behavior, error timing, error position, and copy tone. Pattern-matching from another React app will land you in these specific mistakes:

frontend/docs/patterns.md:329

  • This sentence reads like existing forms already follow the target behavior (“implement the rules directly”), but the next clause says they’re being migrated. Consider rewording to avoid implying that everything already matches the new rules.
The rules below describe the target behavior. Existing forms implement the rules directly and are migrated one at a time. New forms should follow these rules on day one.

frontend/docs/patterns.md:387

  • This cross-field guidance is ambiguous about which blur triggers the mismatch check. If implemented on blur of either field, it would end up setting an error on a different field than the one being blurred (contradicting the earlier “blur validates that field only” rule). Clarify that mismatch validation runs on blur of the dependent/confirmation field.
- Cross-field checks (e.g. password + confirmation match) run on blur only when both fields are non-empty. If either is empty, skip the check — the empty field's own required-error covers it. On mismatch, attach the error to the dependent/confirmation field, not the source.
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

…rminology bullet

- Fix relative link from .claude/rules/fleet-frontend.md to
  frontend/docs/patterns.md#data-validation (was resolving one level too high).
- Reword "Existing forms implement the rules directly" to make clear
  that not every form complies yet.
- Cross-field mismatch check now specifies blur of the
  dependent/confirmation field only, consistent with "blur validates
  that field only".
- Roll the bold "Verb + object + constraint" divider into the sentence
  above so GitHub doesn't render it as a fake heading.
- Collapse the double blank line before "Error message copy register".
- Drop duplicative fleet-vs-team bullet; keep the Terminology pointer.
Copilot AI review requested due to automatic review settings July 30, 2026 21:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

The new validate example trims for presence but validates format against the untrimmed value, creating an inconsistency with the stated “trim before submit” hygiene rule.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Comments suppressed due to low confidence (1)

frontend/docs/patterns.md:342

  • In the validate example, presence checking uses trim() but the format check uses the untrimmed value. This would treat trailing/leading whitespace as a format error even though the doc later recommends trimming before submit; using the trimmed value for both checks keeps the example consistent and avoids accidental whitespace validation bugs.
  if (!formData.email.trim()) {
    errors.email = "Enter your email";
  } else if (!isValidEmail(formData.email)) {
    errors.email = "Enter a valid email";
  }
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Batched fixes from a local Copilot-flavored sweep, plus the trim-both-checks
example update from the last review round:

- Validate example trims once and uses trimmed value for both presence
  and format checks (matches the input-hygiene rule).
- Submit rule: "return without submitting" now conditioned on invalid
  fields, not absolute.
- Focus-clear rule now names click, tab, and programmatic focus so
  keyboard users aren't excluded by the "(click-in)" parenthetical.
- Multiple-server-errors rule now says each field-specific error still
  gets its own toast — drops the ambiguous "prefer summary when many"
  hedge that contradicted the always-toast rule two bullets above.
- Cross-field mismatch rule now states what happens when the source
  field is edited after the confirmation blur (error stays until
  confirmation re-edits/re-blurs).
- Optional-field bullet no longer restates the submit-button rule;
  points at Submit button state instead.
- "No terminal periods" scoped to field errors; the toast register
  carve-out is named on the same line.
- System/transport error paragraph now explicitly excludes field-
  specific server errors (which stay in the verb+object register).
- fleet-frontend.md period rule aligned to the patterns.md scoping.
Copilot AI review requested due to automatic review settings July 30, 2026 21:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Ready to approve

The changes are documentation-only, internally consistent, and remove a previously ambiguous reference without introducing broken links or contradictory guidance.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Ready to approve

Changes are documentation-only and the updated anchors/links and examples appear consistent and renderable based on the reviewed markdown context.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@RachelElysia
RachelElysia merged commit 70a8023 into main Jul 31, 2026
25 checks passed
@RachelElysia
RachelElysia deleted the 2026-07-validation branch July 31, 2026 15:14
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.

8 participants