Skip to content

fix(web): keep Sales order panels closed after pending writes - #711

Merged
mforce merged 2 commits into
mainfrom
feat/703-sales-panel-liveness
Sep 8, 2026
Merged

mforce merged 2 commits into
mainfrom
feat/703-sales-panel-liveness

Conversation

@mforce

@mforce mforce commented Sep 8, 2026 •

Copy link
Copy Markdown
Owner

Closing a Sales order-detail panel during a pending write or detail refresh could let the late result reopen it. Panel writes now capture the order ID and publish panel state only while that order remains active. Writes, reads, list refreshes, and the distinct order/payment idempotency-key ordering are preserved; payment confirmations remain page-owned.

Refs #703, PR5. Only SalesPage.tsx and SalesPage.test.tsx change. Close also discards the line editor synchronously, preventing a stale draft from covering newly fetched values after reopening. Both modal action bodies and all 94 original Sales tests remain byte-identical. #703 stays open for PR6; #708/#709 remain separate chores. Pre-existing behavior is tracked separately: #712 covers the primary Open-read race; #713 covers live-editor reconciliation after a refetch or status change.

Validation on f56fc12ee246e8779d0b1e0b82e9e7a742a563d5, independently rerun by the driver:

  • 2,664 tests passed across 119 files; Sales 117 = 94 + 23. Typecheck, coverage floors, build, and service-worker verification passed.
  • Restoring the base source with the new tests gives 14 failures, including Close during writes and reads.
  • 10 proof mutations killed; 2 survive. Removing the payments publication guard is not observable through the closed panel's DOM; reopening clears payments. A separate reach probe proves that publication executes. The late editor cleanup guard also survives because Close already resets the editor. Both limits are recorded.
  • Every mutation restored to a typechecked source and a passing 117-test Sales suite. Clean diff/marker checks and exact modal/test-prefix comparisons passed.
  • Final-head CI and reviewer status are tracked on this PR; approval on an earlier commit is not final-head clearance. Backend/security checks are CI-attested, not local executions.

Summary by CodeRabbit

  • Bug Fixes
    • The order panel can now be closed while updates, refreshes, or payment actions are in progress.
    • Prevented completed background operations from updating a different order after switching or closing the panel.
    • Success messages are no longer shown after an action is abandoned by closing the panel.
    • Preserved unsaved line-item edits when reopening the same order.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

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: 5e56f9ea-30ea-4164-b1eb-241309fb9642

📥 Commits

Reviewing files that changed from the base of the PR and between c46ee2f and f56fc12.

📒 Files selected for processing (2)
  • web/src/routes/SalesPage.test.tsx
  • web/src/routes/SalesPage.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/routes/SalesPage.tsx

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


📝 Walkthrough

Walkthrough

SalesPage now guards asynchronous order and payment updates with captured order IDs. New tests cover panel dismissal during in-flight operations, success-message behavior, list refreshes, idempotency-key policies, payment voids, and editor-state preservation.

Changes

Sales panel lifecycle

Layer / File(s) Summary
Active-order tracking and refresh guards
web/src/routes/SalesPage.tsx
The page records the active order ID and applies order and payment results only when that order remains active.
Order mutation lifecycle guards
web/src/routes/SalesPage.tsx
Confirm, cancel, and order-void flows use captured order IDs before updating order state or success messages. Both panel Close buttons reset the active order and edit-item state.
Panel lifecycle and write contracts
web/src/routes/SalesPage.test.tsx
Tests cover dismissal during writes and refreshes, suppressed success messages, list refreshes, idempotency-key reuse and rotation, payment void behavior, and preserved editor state.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to f56fc

Sales panels remain closed after late order or payment operations settle, while associated refresh and idempotency behavior remains covered. No concrete current-head merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant SalesPage
  participant OrderAPI
  participant PaymentAPI
  participant ActiveOrderState
  SalesPage->>OrderAPI: Submit mutation with captured order ID
  OrderAPI-->>SalesPage: Return mutation result and refreshed order
  SalesPage->>ActiveOrderState: Apply result if captured ID remains active
  SalesPage->>PaymentAPI: Refresh payments for captured order ID
  PaymentAPI-->>SalesPage: Return payment data
  SalesPage->>ActiveOrderState: Apply payment data if captured ID remains active
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description explains the problem, implementation, scope, issue references, validation results, mutation-testing limits, and known follow-up work. It does not reproduce the checklist, but it provid…
Title check ✅ Passed The title is concise, conventional, and accurately identifies the primary change: keeping Sales order panels closed during pending writes.
  • Fix all pre-merge checks with AI
✨ 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 feat/703-sales-panel-liveness

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 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Please review the complete PR diff at c46ee2f, including panel Close during pending writes and preserved idempotency-key ordering.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='c46ee2f53003cb6038b9331d4ed48e5963a24e5b'
git status --short
git show --no-ext-diff --format=fuller --stat "$target"
base="$(git merge-base "${target}^" "$target")"
printf '\n--- merge base: %s ---\n' "$base"
git diff --no-ext-diff --check "$base" "$target"
printf '\n--- changed paths ---\n'
git diff --no-ext-diff --name-status "$base" "$target"
printf '\n--- full diff ---\n'
git diff --no-ext-diff --unified=80 "$base" "$target" -- \
  ':(glob)**/SalesPage.tsx' ':(glob)**/SalesPage.test.tsx'

printf '\n--- candidate files ---\n'
fd -a -t f 'SalesPage(\.test)?\.tsx$' .

printf '\n--- AST outlines ---\n'
while IFS= read -r file; do
  ast-grep outline "$file" --items all --view expanded
done < <(fd -a -t f 'SalesPage(\.test)?\.tsx$' .)

printf '\n--- liveness/key symbols with context ---\n'
rg -n -C 5 -P '\b(activeOrder|setActiveOrder|idempotency|clearKey|setPayments|setOrder|setOrders|onClose|Close)\b' \
  -g 'SalesPage.tsx' -g 'SalesPage.test.tsx' .

Length of output: 50956


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='c46ee2f53003cb6038b9331d4ed48e5963a24e5b'

printf '%s\n' '--- component outline ---'
git show "$target:web/src/routes/SalesPage.tsx" > /tmp/SalesPage.tsx
ast-grep outline /tmp/SalesPage.tsx --items all --view expanded

printf '%s\n' '--- complete active/close/effect call-site map ---'
git show "$target:web/src/routes/SalesPage.tsx" |
  rg -n -C 10 -P '\b(setActive(?:State)?|activeIdRef|onOpen|onAddItem|onUpdateItem|onRemoveItem|onConfirm|onCancel|onVoid|onVoidPayment|onRecordPayment|refreshPayments|onClose)\b'

printf '%s\n' '--- source: effects through handlers ---'
git show "$target:web/src/routes/SalesPage.tsx" | sed -n '330,680p'

printf '%s\n' '--- source: panel rendering and Close handler ---'
git show "$target:web/src/routes/SalesPage.tsx" | sed -n '680,1040p'

printf '%s\n' '--- test outline and test infrastructure ---'
git show "$target:web/src/routes/SalesPage.test.tsx" > /tmp/SalesPage.test.tsx
ast-grep outline /tmp/SalesPage.test.tsx --items all --view expanded
git show "$target:web/src/routes/SalesPage.test.tsx" | sed -n '1,330p'

Length of output: 82130


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='c46ee2f53003cb6038b9331d4ed48e5963a24e5b'
base='778faab93951300be38cfe13f2f0be5d90585e6a'

git show "$target:web/src/routes/SalesPage.tsx" > /tmp/SalesPage-current.tsx

printf '%s\n' '--- direct active state mutations ---'
rg -n -C 2 -P '\bsetActiveState\s*\(|\bsetActive\s*\(' /tmp/SalesPage-current.tsx

printf '%s\n' '--- changed async state/key operations with current line numbers ---'
git diff --no-ext-diff --unified=12 "$base" "$target" -- web/src/routes/SalesPage.tsx |
  rg -n -C 8 -P '^\+.*\b(await|getOrder|listOrderPayments|setActive|setPayments|setEditItemId|setMessage|clearKey|activeIdRef)\b|^@@'

printf '%s\n' '--- all await-to-publication handler blocks ---'
sed -n '480,705p' /tmp/SalesPage-current.tsx

printf '%s\n' '--- scope/key behavior versus parent ---'
git diff --no-ext-diff --word-diff=plain "$base" "$target" -- web/src/routes/SalesPage.tsx |
  rg -n -C 3 -P '\b(keyFor|clearKey|scope)\b'

printf '%s\n' '--- test selectors added by this commit ---'
git diff --no-ext-diff --unified=2 "$base" "$target" -- web/src/routes/SalesPage.test.tsx |
  rg -n -C 2 -P '^\+\s*(it|describe|for \(|it\.each)|^\+\s*expect\('

Length of output: 41300


@mforce No blocking findings.

I reviewed commit c46ee2f53003cb6038b9331d4ed48e5963a24e5b.

The panel Close path now clears the active-order reference synchronously. Late item, order, and payment refresh results publish panel state only when their captured order ID is still active. The patch also retains the existing idempotency-key ordering for item/order writes and void-payment retries.

The added tests cover Close during both write and refresh phases, stale status messages, key reuse and rotation, payment behavior, and line-editor preservation.

Runtime execution was not available in this review sandbox. The static review found no issue.

✅ 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.

@mforce

mforce commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

New head f56fc12 fixes a stale line editor after Close: dismissal resets the editor synchronously, and reopening tests now use freshly changed server values. Please review this head; the earlier approval covers c46ee2f.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='c46ee2f53003cb6038b9331d4ed48e5963a24e5b'
head='f56fc12ee246e8779d0b1e0b82e9e7a742a563d5'

git status --short
git merge-base --is-ancestor "$base" "$head"
printf '%s\n' '--- commits ---'
git show --no-ext-diff --format=fuller --stat "$head"
printf '%s\n' '--- diff check ---'
git diff --no-ext-diff --check "$base" "$head"
printf '%s\n' '--- changed paths ---'
git diff --no-ext-diff --name-status "$base" "$head"
printf '%s\n' '--- complete incremental diff ---'
git diff --no-ext-diff --unified=100 "$base" "$head" -- \
  web/src/routes/SalesPage.tsx web/src/routes/SalesPage.test.tsx

printf '%s\n' '--- component and test outlines ---'
ast-grep outline web/src/routes/SalesPage.tsx --items all --view expanded
ast-grep outline web/src/routes/SalesPage.test.tsx --items all --view expanded

printf '%s\n' '--- close/editor/reopen state paths ---'
rg -n -C 12 -P '\b(onClose|setEditItemId|editItemId|setActiveState|activeIdRef|onOpen|onUpdateItem|onAddItem|onRemoveItem)\b' \
  web/src/routes/SalesPage.tsx web/src/routes/SalesPage.test.tsx

printf '%s\n' '--- current close and panel rendering sections ---'
sed -n '430,820p' web/src/routes/SalesPage.tsx
printf '%s\n' '--- changed test sections ---'
git diff --no-ext-diff --unified=20 "$base" "$head" -- web/src/routes/SalesPage.test.tsx

Length of output: 100987


@mforce No blocking findings.

I reviewed the incremental diff from c46ee2f5 to f56fc12e.

closeOrderPanel now clears active and editItemId in the same Close path. Both Draft and non-Draft panel Close buttons use this path.

A late onUpdateItem result cannot restore the dismissed editor. The existing activeIdRef.current === id guards prevent publication after Close.

The revised test covers both idle and pending writes. It also verifies that reopening uses the changed server line values and starts outside edit mode.

git diff --check found no whitespace errors. Runtime test execution was unavailable in this sandbox.

⚠️ 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 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 8, 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 8, 2026

Copy link
Copy Markdown
Owner Author

PR5 verification is complete at f56fc12ee246e8779d0b1e0b82e9e7a742a563d5: 2,664 tests, typecheck, coverage, build, service-worker verification, and all CI checks passed. The two modal action bodies and 94 original Sales tests are unchanged. Round1's stale-editor regression was fixed and reviewed on this head.

Two review rounds ran. Opus, Sonnet, Qwen, and CodeRabbit's current-head comment report no merge-blocking defect. The workflow's file-based count is honestly NOT-CLEAN, 0/2: the reviews found pre-existing behavior in the same touched file. #712 tracks Close losing to a primary Open read; #713 tracks live-editor reconciliation on refetch/status changes. Both were reproduced with base source as well as the reviewed source (the #712 establishing-read body is unchanged). They remain separate from PR5's abandoned-write/follow-up-read fix.

No further source changes or repeated unchanged-head review round are underway. Scoped acceptance with those follow-ups is the owner's decision; no merge or auto-merge is armed. The formal CodeRabbit approval must also name this head before the merge ask; a current comment/green skipped status does not substitute for that record.

@mforce

mforce commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 8, 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.

@mforce

mforce commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

PR5 is at the owner merge gate on f56fc12ee246e8779d0b1e0b82e9e7a742a563d5. The owner independently reviewed this head and approved the scoped fix; #712/#713 remain separate pre-existing follow-ups. The historical file-based review count remains NOT-CLEAN 0/2 rather than being relabelled.

I retriggered CodeRabbit with the exact standalone command. It accepted the request and selected c46ee2f5..f56fc12e, but after the ten-minute bound its formal approval still names the parent and the new review remains pending. Under the owner's explicit authorization, I accept the existing current-head seat coverage: Opus, Sonnet, Qwen, CodeRabbit's incremental static review, independent driver verification, and the owner's independent review. This is an explicit coverage decision, not a claim that the old approval covers the final head. Read any new bot findings before actual merge.

All non-bot checks pass; commit-image publication is intentionally skipped. Driver-verified: 2,664 tests, coverage floors, typecheck/build/service worker, causal base-red and mutation checks, clean trees/markers, preserved original tests and modal bodies. Backend/security execution is CI-attested; local browser evidence is route component tests plus the exact-component CDP probe.

The completed merge packet is recorded locally in 55-merge-ask-pr5.md and passes its checker. No merge or auto-merge has been performed. #703 stays open for PR6's fail-open decision.

@mforce
mforce merged commit f0f7492 into main Sep 8, 2026
11 checks passed
@mforce
mforce deleted the feat/703-sales-panel-liveness branch September 8, 2026 07:05
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.

1 participant