Skip to content

fix(web): make the entity picker read as a search field and focus it on open - #736

Merged
mforce merged 4 commits into
mainfrom
fix/735-picker-affordance
Sep 9, 2026
Merged

mforce merged 4 commits into
mainfrom
fix/735-picker-affordance

Conversation

@mforce

@mforce mforce commented Sep 9, 2026 •

Copy link
Copy Markdown
Owner

Closes #735

What

The searchable entity picker (#512) did not read as a search box once open, on every one of its 11 instances across 7 screens.

  1. Input indistinguishable from the options. .named-picker-control input and .named-picker-listbox shared background, border, border-radius and near-identical type, and the input's value is the committed name, so it rendered as one more "Sim House A" row above the list.
  2. Opening neither focused nor selected. The engine's only focus() calls were the two Retry paths. A pointer open left the input blurred with the old name in it.

Change

  • NamedEntityPicker.tsx: an effect keyed on open focuses the input and selects its text. Declared after the open effect so the input exists in the same commit; Retry paths untouched.
  • styles.css: inset --surface-2 background, a leading magnifier drawn with ::before/::after (coloured by --muted), brand border while aria-expanded="true", listbox gap 0.25→0.4rem. Closed trigger, placeholder (spec FR-018), ARIA and type="text" unchanged.
  • styles.csp.test.ts: guard that walks every url() in styles.css and admits only same-origin paths or fragments — the API serves the sheet under img-src 'self' blob:, which the dev server does not enforce (codex review finding; see the PR comment).
  • NamedEntityPicker.openFocus.test.tsx: written red first (document.activeElement was body), green after.

Verification

  • npx vitest run: 121 files / 2708 tests green. npm run typecheck OK. Coverage floor holds (91.0% lines, 87.3% branches).
  • Real browser (Chromium, 1280×800, DPR 1) against the Aspire dev stack, both themes, on the Daily entry flock picker:
    • before: focused:false, selectionStart 11, selectionEnd 11
    • after: focused:true, selectionStart 0, selectionEnd 11
  • Re-captured with the production img-src directive injected on the document: glyph present in both themes, cspViolations=0.
  • 1:1 before/after captures (dark + light) taken from the live dev server serving this branch (AGENTS.md: three conventions earned while shipping #651 + #652 #662 rule); attached below by the owner.

Not in scope

Help page / glossary (no concept change). Native <datalist> on Settings (browser-drawn). Help page search already has the icon+placeholder pattern this matches.

Summary by CodeRabbit

  • Bug Fixes

    • Opening the named picker now focuses the search field and selects the current name, so typing immediately starts a search.
  • Style

    • Updated the picker’s search field styling with a search icon, improved background, and clearer active-state border.
    • Increased spacing between the search field and results list.

Before / after (1:1, desktop, night + day)

picker before and after

Phone width (390px) after, under the production img-src directive: glyph centred at the 2.75rem field height.

…on open

The open NamedEntityPicker input shared surface, hairline, radius and type
with the listbox rows beneath it, so it rendered as a highlighted first row
carrying the committed name; and opening from the trigger neither focused
the input nor selected its text, so nothing suggested typing. Affects all
11 picker instances across 7 screens.

- Effect keyed on `open`: focus the input and select its text, declared
  after the open effect so the input exists in the same commit.
- CSS: inset `--surface-2` background, leading magnifier glyph, brand
  border while `aria-expanded="true"`, slightly wider gap to the listbox.
  Trigger, placeholder, ARIA and `type` unchanged.
- Test written red first (activeElement was `body`), green after.

Closes #735
@coderabbitai

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: b5321921-db3d-4c31-82c9-2fa1d01d3356

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 3db2d22a-5584-49db-829a-70cf3ad2f958

📥 Commits

Reviewing files that changed from the base of the PR and between 2f6e242 and b39e15b.

📒 Files selected for processing (3)
  • web/src/components/NamedEntityPicker.openFocus.test.tsx
  • web/src/components/NamedEntityPicker.tsx
  • web/src/styles.css

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The named picker now focuses and selects its committed name when opened. New tests verify this behavior. CSS changes distinguish the input from listbox options with search-field styling and expanded-state branding.

Changes

Named picker improvements

Layer / File(s) Summary
Open focus and selection behavior
web/src/components/NamedEntityPicker.tsx, web/src/components/NamedEntityPicker.openFocus.test.tsx
The picker focuses its input and selects the committed name when enabled and opened. Tests verify the active element and complete text selection.
Search field visual styling
web/src/styles.css
The input uses an inset background, magnifier icon, adjusted padding, and brand-colored expanded state. The listbox gains additional top spacing.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~15–30 minutes

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to b39e1

Named entity pickers now focus and select the committed value when opened and have clearer search-field styling. The change is covered by an open-focus regression test, with no remaining merge-readiness risk identified.

Sequence Diagram(s)

sequenceDiagram
  participant TriggerButton
  participant NamedEntityPickerEngine
  participant ComboboxInput
  TriggerButton->>NamedEntityPickerEngine: Open picker
  NamedEntityPickerEngine->>ComboboxInput: Focus input
  NamedEntityPickerEngine->>ComboboxInput: Select committed name
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The implementation addresses the shared picker styling and open-focus behavior in issue [#735]. However, the added test covers focus and text selection but does not assert the expanded styling hook, w… Add a test assertion for the expanded styling hook, such as aria-expanded="true" or the corresponding expanded-state attribute/class, and verify the assertion fails before the CSS or state change and passes after it is restored.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changes are limited to the shared NamedEntityPicker behavior, its styling, and focused test coverage. The listbox spacing adjustment supports the requested visual distinction, and no unrelated cha…
Title check ✅ Passed The title is concise, conventional, and clearly describes the two main changes: styling the entity picker as a search field and focusing it on open.
Description check ✅ Passed The description explains the problem, implementation, scope, verification results, and linked issue. It omits the template's exact section headings and checklist, but the required technical informatio…
Full details: Linked Issues check

Explanation

The implementation addresses the shared picker styling and open-focus behavior in issue [#735]. However, the added test covers focus and text selection but does not assert the expanded styling hook, which is an explicit acceptance requirement.

Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/735-picker-affordance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mforce

mforce commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

The API serves the SPA under `img-src 'self' blob:` (SecurityHeaders.cs),
and CSP applies img-src to CSS background images, so the data-URI glyph
from the previous commit painted on the Vite dev server and vanished in
production — reproduced by capturing under that directive:
`Loading the image 'data:image/svg+xml,…' violates the following CSP`.

- The magnifier is now a bordered circle and a rotated bar on the
  control's ::before/::after, coloured by --muted so it follows the theme.
- styles.csp.test.ts walks every url() in styles.css and admits only
  same-origin paths or fragments; red on the data-URI, green now.

Found by the codex review of #736.
@mforce

mforce commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

External review finding (codex, gpt-5.6-sol), addressed in 9d788dc.

Finding: the magnifier was a data: SVG in background-image; the API serves the SPA under img-src 'self' blob: (SecurityHeaders.cs, pinned by SecurityHeadersTests), and CSP applies img-src to CSS backgrounds, so the glyph rendered on the Vite dev server and would vanish in production. Confirmed empirically by re-capturing with that directive injected on the document: cspViolations=1 on the data image.

Fix: the glyph is now drawn with ::before/::after (bordered circle + rotated bar, coloured by --muted). No url() remains in styles.css. New guard web/src/styles.csp.test.ts walks every url() token in the stylesheet and admits only same-origin paths or fragments — red on the data-URI, green after.

Verification on 9d788dc: 121 files / 2708 tests green, typecheck OK, coverage floor unchanged; re-capture under the img-src directive shows the glyph in both themes with cspViolations=0, input focused with the full name selected.

@mforce

mforce commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mforce

mforce commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…url() guard

Codex review of #736, round 2, against the previous fix:

- The glyph's vertical offsets were fixed rem values measured on the
  desktop field; the phone breakpoint raises `.entry-context input` to
  `min-height: 2.75rem`, which put the lens ~4px above centre on Daily
  entry. Both pseudo-elements now key off `top: 50%` of the control, whose
  only in-flow child is the input. Probed in Chromium at 1280 and 390px:
  lens offset from the input's centre is 0 at 38.6px and 44px heights.
- The disabled input dims itself on top of the whole-picker dim; the glyph
  sits outside the input and took only one step, so it read darker than
  its field. `:has(input:disabled)` gives it the same second step.
- `styles.csp.test.ts` matched only lowercase `url(` — CSS function names
  are case-insensitive, so `URL("data:…")` slipped through — and keyed
  same-origin on a leading slash, which admitted protocol-relative
  `//host/x.svg` and rejected a bare relative path. It now matches /i and
  classifies by scheme/protocol-relative prefix. Mutation set: uppercase
  data:, protocol-relative, uppercase scheme, unquoted blob: all red;
  bare relative, root-relative and #fragment stay green.
- docs/images/reviews/735-picker-before-after.png: the 1:1 before/after
  montage (night + day) the #662 rule asks a visual PR to carry.
@mforce

mforce commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

Codex review, round 2 (gpt-5.6-sol) against 9d788dc8 — four findings, all addressed in f71fb29.

# Severity Finding Verdict Fix
1 High styles.csp.test.ts matched only lowercase url(; URL("data:…") is valid CSS and slips through Confirmed regex is /i; mutant URL("data:…") now red
2 Medium same-origin keyed on a leading /: protocol-relative //host/x.svg passed, bare relative icon.svg failed Confirmed classify by scheme / // prefix; mutants //cdn…, HTTPS://…, unquoted blob: red; icons/x.svg, /icons/x.svg, #frag green
3 Medium fixed rem offsets de-centre the glyph where the phone breakpoint raises .entry-context input to 2.75rem (Daily entry) Confirmed — picker is inside .entry-context top: 50% of the control; probed at 1280 and 390px: lens offset from input centre = 0 at 38.6px and 44px heights
4 Low disabled input dims twice, glyph once Confirmed :has(input:disabled) dims the glyph the same second step

Verification on f71fb29: 121 files / 2708 tests green, typecheck OK, coverage floor holds; captures under the production img-src directive at both widths and both themes show the glyph with cspViolations=0, input focused with the full name selected.

Before/after (1:1, night + day): https://raw.githubusercontent.com/mforce/cluckwork/f71fb29e5131871bc95a8998fb4aa2697c4e57db/docs/images/reviews/735-picker-before-after.png

…border out of rem

Codex review of #736, round 3, against the previous fix:

- The guard classified raw CSS text: `url(d\61 ta:x)` and `u\72l(data:x)`
  are the same blocked request once the tokenizer decodes them, but
  passed as written. Escapes are now decoded before matching. Mutants
  hex-escaped data:, escaped function name and six-digit hex all red;
  an escaped colon inside a same-origin path stays green.
- The handle's start point folded the lens's 2px border into 0.425rem,
  which only holds at a 16px root. The offsets now keep px and rem apart;
  probed against the stylesheet in Chromium at 16px and 24px root, on the
  desktop height, the phone 2.75rem min-height and a line-height of 2:
  lens offset from the field centre 0, handle gap from the lens 0.
- The disabled glyph dim is keyed on `.named-picker.disabled` instead of
  `:has(input:disabled)`; both come from the one `disabled` prop, and the
  ancestor form needs no `:has()` support. Probed: input and glyph both
  at 0.6 inside the 0.6 ancestor.
@mforce

mforce commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

Codex review, round 3 (gpt-5.6-sol) against f71fb29e — three findings, all addressed in 10236c0. Loop stopped here by the owner's call (merge after CI); this round bought no defect a user at default settings would see.

# Severity Finding Verdict Fix
1 High (guard) guard classified raw text: url(d\61 ta:x) / u\72l(data:x) decode to a blocked data: request but passed Confirmed escapes decoded before matching; mutants hex-escaped data:, escaped function name, six-digit hex all red; escaped colon in a same-origin path green
2 Medium handle start folded the 2px lens border into 0.425rem; detaches at a larger root font size Confirmed at 24px root px and rem kept apart in calc(); probed at 16px/24px root × desktop/phone-2.75rem/line-height 2: lens offset 0, handle gap 0
3 Low :has(input:disabled) dropped whole by Firefox 114–120 Plausible (the sheet already uses :has() elsewhere, so the project accepts it) keyed on .named-picker.disabled anyway — simpler, no :has(); probed: input and glyph both 0.6 inside the 0.6 ancestor

Verification on 10236c0: 121 files / 2708 tests green, typecheck OK, coverage floor holds. Round tally: r1 = 1 product defect (CSP), r2 = 2 product + 2 guard, r3 = 0 product at defaults + 1 guard.

@mforce
mforce merged commit 66ef667 into main Sep 9, 2026
11 checks passed
@mforce
mforce deleted the fix/735-picker-affordance branch September 9, 2026 06:29
mforce added a commit that referenced this pull request Sep 10, 2026
A one-paragraph rule in `AGENTS.md`'s **Git / PR workflow** section,
beside the existing PR-title rule — same subject, title versus body.

## Why

PR #742 carried `(#722)` in its title and no closing keyword in its
body. GitHub treats a title reference as a reference: **the issue would
have survived the merge.** The repo's convention already is the keyword
— #733 opens `Closes #732`, #736 opens `Closes #735` — but nothing in
`AGENTS.md` said so, so a driver-authored runbook that specified the PR
title verbatim and the body's required paragraph verbatim simply never
mentioned it. Caught by the owner reading the PR one step from the merge
ask.

The note also records two mechanics that cost time to rediscover: verify
the linkage with the `closingIssuesReferences` GraphQL query rather than
by re-reading the body, and patch a PR body with `gh api -X PATCH`
because `gh pr edit` fails on this repo with a Projects-classic
deprecation error.

## Scope

One line replaced by two in `AGENTS.md`. No code, no behaviour change.

Split out of #742 at the owner's direction rather than riding along, so
#742 stays at the exact head its two review rounds and its full
verification ran on.

Co-authored-by: mforce <mforce@users.noreply.github.com>
mforce pushed a commit that referenced this pull request Sep 12, 2026
🤖 I have created a release *beep* *boop*
---


## [0.1.0](v0.0.4...v0.1.0)
(2026-09-12)


### ⚠ BREAKING CHANGES

* log in by farm code, with per-account email identity
([#532](#532)) (#564)

### Features

* **accounts:** add Account.Slug (farm code), suspend/reactivate,
list-accounts verb
([#531](#531))
([3fe9754](3fe9754))
* **accounts:** provision additional farms
([#581](#581))
([006f298](006f298))
* add Aspire local development AppHost
([#567](#567))
([2c9e6b9](2c9e6b9))
* add configurable worker sale allocation
([#619](#619))
([0955095](0955095))
* add searchable entity pickers
([#642](#642))
([60d2053](60d2053))
* **api:** provision-account takes an optional --timezone at creation
([#603](#603))
([#694](#694))
([a0aee39](a0aee39))
* **audit:** show the sales-line audit payload as a readable Details
column ([#745](#745))
([#749](#749))
([d26d389](d26d389))
* **auth:** add ApplicationUser.StepUpLogoutEpoch column
([#338](#338))
([#554](#554))
([18306ee](18306ee))
* certify over-cap simulation fixture bands
([#633](#633))
([a67b2e1](a67b2e1)),
closes [#627](#627)
* **cli:** rename-account verb to change a farm code
([#732](#732))
([#733](#733))
([4b70559](4b70559))
* **customers:** edit existing customer details
([#625](#625))
([#626](#626))
([062a55c](062a55c))
* **jobs:** single-runner leader gate for the durable job worker
([#271](#271))
([#555](#555))
([4148f9b](4148f9b))
* let owners change user email addresses
([#605](#605))
([842347b](842347b))
* log in by farm code, with per-account email identity
([#532](#532))
([#564](#564))
([68adb62](68adb62))
* **ratelimit:** distributed IP-keyed auth limiters
([#544](#544))
([#558](#558))
([ec14972](ec14972))
* **ratelimit:** distributed per-account report concurrency cap with
local-ceiling fallback
([#545](#545))
([#559](#559))
([1522e4e](1522e4e))
* **sales:** mark discounted lines, total the discount, and show it in
the Orders list ([#723](#723),
[#724](#724))
([#741](#741))
([1a07441](1a07441))
* **sales:** record list, old and new price in the order-line audit
payload ([#722](#722))
([#742](#742))
([97c866f](97c866f))
* **sales:** refuse an over-ceiling confirm from a Sales user
([#727](#727))
([#766](#766))
([8c0792a](8c0792a))
* **sales:** show what each order still owes, and filter the list to
unpaid ([#771](#771))
([ca59d68](ca59d68))
* **sales:** snapshot the list price on the order line and show the
discount ([#734](#734))
([cffed5e](cffed5e))
* **sales:** snapshot the product name and unit in the order-line audit
payload ([#747](#747))
([#748](#748))
([0481c06](0481c06))
* scope Worker reads to assigned flocks
([#388](#388))
([#611](#611))
([5884a9a](5884a9a))
* shared-state ports with Redis + in-process fallback
([#543](#543))
([#552](#552))
([f767fa9](f767fa9))
* suspend-account / reactivate-account operator verbs
([#534](#534))
([#573](#573))
([d0be26c](d0be26c))
* **tenancy:** write-side tenant guard + single-assignment TenantContext
([#546](#546))
([#561](#561))
([f371f1d](f371f1d))
* **web:** dashboard rework — capture-status tiles, 14-day trend, stock
as a stacked bar
([#654](#654))
([396ba23](396ba23))
* **web:** date-range filters on audit and expenses, and the stock lot
filter gets its bounded toolbar
([#666](#666),
[#667](#667),
[#653](#653))
([94b188f](94b188f))
* **web:** elevation hierarchy and sentence-case labels
([#651](#651),
[#652](#652))
([#661](#661))
([28db4c7](28db4c7))
* **web:** Expenses and Audit keep a clear-filters control while rows
are still showing
([#679](#679))
([#697](#697))
([b859982](b859982))
* **web:** expenses filters by a date range like its sibling screens
([#667](#667))
([f13858f](f13858f))
* **web:** key the farm brand palette per farm
([#586](#586))
([#600](#600))
([7183a43](7183a43))
* **web:** let operators forget remembered farms
([#598](#598))
([577d94e](577d94e))
* **web:** one-line provenance, bounded date filters, and empty states
that invite action
([#653](#653),
[#655](#655))
([#668](#668))
([80b53f4](80b53f4))
* **web:** prefill the farm code from ?farm= and remember it
([#535](#535))
([#588](#588))
([b7f5cc6](b7f5cc6))
* **web:** split authenticated routes into lazy chunks
([#620](#620))
([5089271](5089271))
* **web:** the audit log filters by a date range, and says which window
is empty ([#666](#666))
([63027e0](63027e0))
* **web:** typeset numbers as numbers and refresh the Help glossary
([#650](#650),
[#657](#657))
([af4fe11](af4fe11))


### Bug fixes

* **api:** order same-instant audit events by a durable monotonic key
([#700](#700))
([8fcf084](8fcf084))
* **api:** print the farm code from bootstrap-admin
([#589](#589))
([#594](#594))
([34032ac](34032ac))
* **audit:** show the price a line sold for, not its list price
([#759](#759))
([e6b37d0](e6b37d0))
* **audit:** store catalog enums by name and guard the add-item
transaction shape
([#751](#751))
([23609ff](23609ff))
* **auth:** reject invalid account claims
([#622](#622))
([8d6c7fe](8d6c7fe))
* **auth:** require step-up for durable user access
([#360](#360))
([#607](#607))
([f767dce](f767dce))
* **ci:** bound the npm audit calls and give the web job room to finish
([#686](#686))
([153b7a8](153b7a8))
* **ci:** escalate the audit bound to SIGKILL, so it actually bounds
([#686](#686))
([a0c8f4e](a0c8f4e))
* **ci:** fail closed on invalid vulnerability config
([#621](#621))
([1690db8](1690db8))
* **ci:** lockfix covers the two AppHost lock files, derived from the
sln
([efb05e6](efb05e6))
* **ci:** lockfix covers the two AppHost lock files, derived from the
sln
([8986d77](8986d77))
* **ci:** remove invalid XML comment from nuget.lockfix.config
([#541](#541))
([5f1bc0a](5f1bc0a))
* **ci:** the advisory vuln gate no longer blocks on an unusable report
([#686](#686))
([aaf6934](aaf6934))
* **ci:** the advisory vuln gate no longer blocks on an unusable report
([#686](#686))
([64f1f53](64f1f53))
* **i18n:** tl help text names the saleable flag and unit-system setting
what their labels call them
([#688](#688))
([#696](#696))
([bfd24d7](bfd24d7))
* **infra:** AccountId must be a non-nullable Guid or both tenant write
layers refuse ([#673](#673))
([#695](#695))
([2470c4e](2470c4e))
* require step-up for flock scope changes
([#609](#609))
([4151f89](4151f89))
* **sales:** keep a line's discount markers agreeing while its price is
edited ([#752](#752))
([#753](#753))
([c159b4b](c159b4b))
* **sales:** say which kind of missing list price a line has
([#774](#774))
([489180e](489180e))
* scope legacy logout to selected farm
([#624](#624))
([fae8d82](fae8d82))
* **seed:** drain the daily-entry lock sweep so deep simulation fixtures
validate ([#644](#644))
([730fa23](730fa23)),
closes [#638](#638)
* **tenancy:** AccountId is a concurrency token, so the database refuses
a detached cross-tenant write
([#562](#562))
([4d1dfa3](4d1dfa3))
* **tenancy:** AspNetUserRoles carries a tenant column, so a role write
naming another farm's user is refused
([#670](#670))
([fc0552a](fc0552a))
* **tests:** bump the image-pin allow-list counts for the AppHost
LocalPorts tests
([#593](#593))
([58d3056](58d3056))
* **tests:** the OTLP collector survives a lost port race and ignores
traffic that is not an export
([#672](#672),
[#676](#676))
([#677](#677))
([965c737](965c737))
* **web:** a scoped audit view filtered to nothing names both the record
and the range ([#666](#666))
([41bbfe1](41bbfe1))
* **web:** an abandoned dialog attempt's success no longer hijacks the
replacement on Customers, Daily Entry, Flocks, Grades and Products
([#703](#703))
([#705](#705))
([85605db](85605db))
* **web:** an abandoned dialog attempt's success no longer hijacks the
replacement on Inventory, Expenses, History and Stock
([#703](#703))
([#706](#706))
([60a4997](60a4997))
* **web:** an abandoned edit's success no longer hijacks the dialog that
replaced it on Users
([#703](#703))
([#710](#710))
([778faab](778faab))
* **web:** an abandoned order attempt's success no longer hijacks the
dialog that replaced it
([#702](#702))
([522c699](522c699))
* **web:** capture screens open on the flock you last used, and
assigning one no longer guesses
([#646](#646))
([#699](#699))
([7f8f317](7f8f317))
* **web:** constrain dialog session helpers to declared scopes
([#715](#715))
([389e3c8](389e3c8))
* **web:** date validation gets one boundary table instead of one case
per review round
([#666](#666))
([215f830](215f830))
* **web:** keep a paged window and an item panel on the user's newest
intent ([#645](#645))
([d81bccf](d81bccf))
* **web:** keep Sales order panels closed after pending writes
([#711](#711))
([f0f7492](f0f7492))
* **web:** keep Sales panels closed after pending Open reads
([#716](#716))
([620411f](620411f))
* **web:** make login take the cross-tab cookie lock so a racing refresh
cannot restore the wrong session
([#648](#648))
([ff18beb](ff18beb))
* **web:** make the entity picker read as a search field and focus it on
open ([#736](#736))
([66ef667](66ef667)),
closes [#735](#735)
* **web:** page truncated customer and movement tables with usePagedList
([7cfe4d6](7cfe4d6))
* **web:** reconcile Sales line edits with refreshed orders
([#717](#717))
([d7dd2c9](d7dd2c9))
* **web:** the audit date filter accepts low-numbered years, and its
empty state covers every narrowing
([#666](#666))
([af52d25](af52d25))
* **web:** the audit date filter rejects impossible dates, and its
history guard actually guards
([#666](#666))
([8d51846](8d51846))
* **web:** the expense range bounds are not capped at today, which the
month-end default exceeds
([#667](#667))
([7e01864](7e01864))
* **web:** the help text calls the expiry field what the field calls
itself ([#666](#666))
([2fd1f3c](2fd1f3c))
* **web:** the stock lot date range sits in the bounded toolbar
([#653](#653))
([43dec5e](43dec5e))


### Refactoring

* **web:** extract SalesPage's dialog-write wrapper into a shared
useDialogAction hook
([#703](#703))
([#704](#704))
([60ee9d9](60ee9d9))


### Documentation

* add k6 preparation steps to the dev-database fixture runbook
([#643](#643))
([a4f1f09](a4f1f09))
* add runbook for loading the simulation fixture into a dev database
([#639](#639))
([2d143b8](2d143b8))
* **agents:** a PR closes its issue from the body, not the title
([#744](#744))
([39be13c](39be13c))
* **agents:** drop the commit and push gate, and require screenshots on
UI changes ([#757](#757))
([6225172](6225172))
* **agents:** find guards by grepping registry readers; amend issues a
PR overtakes ([#580](#580))
([fe3fde8](fe3fde8))
* **agents:** the Playwright specs have been in CI since 2026-08-08
([#768](#768))
([68ee612](68ee612))
* **aspire:** record the second local database and pin the AppHost
dashboard ports ([#623](#623))
([713b941](713b941))
* compress AGENTS.md to one paragraph per rule, and draw the two orders
that matter ([#551](#551))
([997ae8a](997ae8a))
* item 7 names each screen's actual initial filter value
([#666](#666))
([70a53d8](70a53d8))
* multi-farm tenancy decision record and AGENTS/GLOSSARY sync
([#537](#537))
([#601](#601))
([2c34771](2c34771))
* name the scoped filtered-empty key and state the
[#653](#653) relationship
plainly ([#666](#666))
([0e93dac](0e93dac))
* note that a PackageReference in Directory.Build.props is invisible to
the dependency graph
([4845724](4845724))
* **plans:** commit the
[#722](#722) and
[#745](#745) design records
([#754](#754))
([c942fcd](c942fcd))
* record [#579](#579) as
won't-fix — suspension is immediate for use, not issuance
([#582](#582))
([7a3be40](7a3be40))
* record the [#508](#508)
audit ordering key and the tracked-file guard lesson
([#701](#701))
([08964e9](08964e9))
* **runbooks:** add procedure to rename the default farm's code after
upgrade ([#731](#731))
([2f6e242](2f6e242))
* screenshots of the running SPA in the README
([#550](#550))
([711488a](711488a))
* **sim:** commit the dashboard screenshot, capture the palette matrix,
and record the
[#651](https://github.com/mforce/cluckwork/issues/651)/[#652](https://github.com/mforce/cluckwork/issues/652)
conventions ([#660](#660),
[#662](#662),
[#663](#663),
[#664](#664))
([#665](#665))
([930ea30](930ea30))
* specify searchable entity picker
([#641](#641))
([91d4300](91d4300))
* split the README into audience-scoped docs and adopt repo-template
scaffolding ([#548](#548))
([b3f3fcf](b3f3fcf))
* surface Aspire local development workflow
([#568](#568))
([a343baa](a343baa))
* **web:** record the per-screen idempotency-key policies and runWrite's
refresh contract
([#703](#703))
([#707](#707))
([8bee651](8bee651))
* **web:** the date-cap help text covers every stocked item, not only
feed ([#666](#666),
[#667](#667))
([c8433c5](c8433c5))
* **web:** the help text claims only what is true of recording, and says
nothing about filter caps
([#666](#666),
[#667](#667))
([e2f63d1](e2f63d1))
* **web:** the help text describes the date-range filters that shipped
([#666](#666),
[#667](#667))
([c3275b7](c3275b7))
* **web:** the help text stops describing a cap the filters no longer
have ([#666](#666),
[#667](#667))
([49654cd](49654cd))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: cluckwork-lockfix[bot] <309265648+cluckwork-lockfix[bot]@users.noreply.github.com>
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.

Entity picker input indistinguishable from its options; opening does not focus or select

1 participant