Skip to content

docs: split the README into audience-scoped docs and adopt repo-template scaffolding - #548

Merged
mforce merged 3 commits into
mainfrom
docs/readme-split
Aug 16, 2026
Merged

mforce merged 3 commits into
mainfrom
docs/readme-split

Conversation

@mforce

@mforce mforce commented Aug 16, 2026

Copy link
Copy Markdown
Owner

What and why

README.md was 477 lines serving four audiences at once — evaluator, developer, release engineer, operator. 226 of those lines (47%) were release engineering. It is now 102 lines: what Cluckwork is, how to run it, and where everything else lives.

Nothing is deleted. Every block moved to a file whose audience matches it:

Moved To
Release how-to, deploy-by-digest, repair paths docs/releasing.md
Production + compose + IDE bootstrap-admin docs/runbooks/first-admin-provisioning.md
pg_dump / pg_restore docs/runbooks/backup-and-restore.md
Hooks, dev env, DB reset, schema-change workflow, commit messages CONTRIBUTING.md

Adopted from mforce/repo-template

Notes on the three config files

  • .gitattributes — * text=auto eol=lf. Confirmed zero CRLF files tracked, so it causes no renormalisation churn. It protects .githooks/* and tools/simulation/*.sh from a CRLF checkout, where the kernel looks for /bin/sh\r and fails naming neither the hook nor the cause.
  • CODEOWNERS ships inert. main currently has required_approving_review_count: 0 with a single owner holding write access, so "Require review from Code Owners" would be a deadlock or self-approval theatre. The file records the gap it would close — a merge to main rewriting ci.yml still produces a genuinely valid attestation — and the three steps to activate.
  • SECURITY.md states plainly that no private reporting channel is enabled, rather than pointing at GitHub private vulnerability reporting, which is off. Owner call this session. It carries a maintainer note on how to turn it on.

Cross-references updated

AGENTS.md (release how-to pointer ×2, plus human entry points at the top), docs/decisions/README.md, docs/decisions/351-releases.md, deploy/README.md, and a stale (README) comment in SeedCommandTests.cs. The two existing runbooks gained a Last drilled header, honestly set to not recorded — no drill was performed, so none is dated.

How it was verified

  • Link check — every relative link in the 21 touched files resolves. The checker was proven first against an injected broken link and an anchored link, so a clean run means something.
  • Content-loss check — a list of distinctive strings from the old README (42P07, Address already in use, cluckwork-dev_cluckwork-dev-pg18, --bundle-from-oci, --signer-workflow, --source-ref, the word(( parser trap, Release-As: 1.0.0, [skip ci], …) still resolves somewhere in the tree. First run reported three false misses because grep -F ate the leading -- as options; fixed with -e and re-run clean.
  • Anchor targets checked by hand against the headings they name.
  • Docs-only plus one comment line in a test file — no build or test behaviour changes.

Checklist

  • The PR title is a conventional commit, and is the release note I want.
  • Docs updated in this PR.
  • No hardcoded credential.
  • No hardcoded hosting-provider name.
  • No new Action, no dependency change.

…ate scaffolding

README was 477 lines serving four audiences at once; 226 of them were release
engineering. It is now 102 lines: what Cluckwork is, how to run it, where
everything else lives.

Moved verbatim, nothing dropped:

- release how-to, deploy-by-digest and the repair paths to docs/releasing.md
- production and dev bootstrap-admin to docs/runbooks/first-admin-provisioning.md
- pg_dump/pg_restore to docs/runbooks/backup-and-restore.md
- hooks, dev env, DB reset, schema-change workflow and commit messages to
  CONTRIBUTING.md

Adopted from mforce/repo-template:

- CONTRIBUTING.md, SECURITY.md, a PR template with this repo's own checks
- runbook and decision-record templates, plus a runbooks index
- .editorconfig, .gitattributes, an inert CODEOWNERS

Also: docs/README.md maps the tree and states which file a rule belongs in.
Cross-references in AGENTS.md, deploy/README.md and the decision records now
point at docs/releasing.md. The two existing runbooks gained a Last drilled
header, honestly set to "not recorded".

Verified: every relative link in the touched files resolves, with the checker
proven against an injected broken link first; every distinctive string from the
old README still resolves somewhere in the tree.
@mforce

mforce commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

@codex please review this PR. It is documentation-only apart from one comment line in SeedCommandTests.cs, so the interesting failure modes are: a moved paragraph that now contradicts its source, a link or anchor that does not resolve, a claim in the new files that is not true of this repo (particularly .gitattributes, CODEOWNERS and SECURITY.md), and any fact from the old 477-line README that was lost in the split rather than moved.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1cdd2a1d02

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/releasing.md Outdated
Comment on lines +103 to +105
— if someone pushed those bytes by hand, the digest is still a perfectly valid,
perfectly immutable digest. The attestation is a signed claim by this repo's CI
workflow, so anything pushed by hand has no such claim and fails the check.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the attested-byte substitution limitation

When an attacker can write to the registry and select an image digest that this workflow attested previously, manually substituting those bytes still presents a valid provenance claim, so “anything pushed by hand has no such claim” overstates the guarantee. The canonical boundary explicitly leaves substitution of other attested bytes open; qualify this as rejecting newly pushed, unattested bytes under a leaked registry credential.

AGENTS.md reference: AGENTS.md:L230-L235

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed and fixed in d51744b. "Anything pushed by hand has no such claim" now reads: bytes CI never built carry no such claim and fail the check; bytes CI did build still verify whoever pushed them, so this does not stop an older attested image being substituted — which is what step 3 is for. That matches the canonical boundary in AGENTS.md, which this file summarises rather than restates. Also inherited verbatim from the old README.

Comment thread docs/releasing.md Outdated
Comment on lines +98 to +99
All three flags matter and none is the default — each narrows *whose* claim is
accepted (the registry copy, one workflow, one branch). Copy the command as-is.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Describe each attestation flag by its actual role

--bundle-from-oci does not narrow whose claim is accepted; it selects the registry copy of the attestation bundle rather than the GitHub API copy. Only --signer-workflow and --source-ref constrain signer identity, so grouping the registry source with “whose claim” contradicts the canonical explanation and gives deployers the wrong security model.

AGENTS.md reference: AGENTS.md:L227-L227

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed and fixed in d51744b. The three flags are now described by role, matching the canonical bullet in AGENTS.md: --bundle-from-oci reads the attestation from the registry copy rather than the GitHub API, and only --signer-workflow and --source-ref narrow whose claim is accepted. The sentence was inherited verbatim from the old README, not introduced by the split.

Comment thread .github/CODEOWNERS
# * @OWNER

# Paths where a self-merge rewrites what every other check asserts.
# /.github/ @OWNER

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use recursive patterns before activating CODEOWNERS

If the documented activation steps are followed, /.github/, /.githooks/, and /docs/decisions/ do not recursively own files under those directories in CODEOWNERS syntax; directory contents require patterns such as /.github/**. Consequently workflow changes such as .github/workflows/ci.yml would remain ownerless even though this file says activation closes the self-merge control gap.

AGENTS.md reference: AGENTS.md:L236-L242

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Rejected, with evidence — a trailing-slash CODEOWNERS pattern is recursive.

GitHub's own CODEOWNERS syntax examples state it twice:

In this example, @doctocat owns any files in the build/logs directory at the root of the repository and any of its subdirectories. — /build/logs/ @doctocat

In this example, @doctocat owns any file in the /docs directory in the root of your repository and any of its subdirectories. — /docs/ @doctocat

So /.github/ would own .github/workflows/ci.yml once activated; /.github/** is not required. Leaving the patterns as they are.

(Restating for the record: the file ships inert — every pattern is commented out — because main currently has required_approving_review_count: 0 and one owner with write access, so activating it would be a deadlock or self-approval. The claim in the header is about what activation would close, not about anything this PR turns on.)

Comment thread .gitattributes Outdated
# regenerating, never by hand-editing (#417 for the schema docs).
docs/schema/** linguist-generated=true
**/packages.lock.json linguist-generated=true
web/package-lock.json linguist-generated=true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Mark every generated npm lock file consistently

The repository also tracks tools/simulation/ui/package-lock.json, but this rule marks only web/package-lock.json as generated. Changes to the simulation UI lock file therefore remain in language statistics and expanded PR diffs despite the surrounding section describing generated lock files; use a repository-wide **/package-lock.json pattern or list the second lock explicitly.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed and fixed in d51744b. tools/simulation/ui/package-lock.json is indeed tracked; the rule is now the repository-wide **/package-lock.json. Verified: git check-attr linguist-generated returns true for both lock files, both packages.lock.json files and docs/schema/.

…les generated

Codex review round 1 on PR 548:

- P2, docs/releasing.md: the summary said all three verify flags narrow whose
  claim is accepted. Only --signer-workflow and --source-ref do that;
  --bundle-from-oci selects the registry copy of the bundle. Now split by role,
  matching the canonical bullet in AGENTS.md.
- P2, docs/releasing.md: "anything pushed by hand has no such claim" overstated
  the guarantee — bytes CI did build still verify whoever pushes them. Narrowed
  to bytes CI never built, and pointed at step 3 for the substitution case.
- P3, .gitattributes: tools/simulation/ui/package-lock.json was not marked
  generated. Both lock files now match a repository-wide pattern.

Both prose defects were inherited verbatim from the old README rather than
introduced by the split.
@mforce

mforce commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

@codex round 1 addressed in d51744b.

  • Attestation flags describe their actual roles (P2) — fixed. Only --signer-workflow and --source-ref narrow whose claim is accepted; --bundle-from-oci selects the registry copy.
  • Attested-byte substitution (P2) — fixed. Narrowed to "bytes CI never built", with the substitution case pointed at step 3.
  • Both npm lock files marked generated (P3) — fixed with a repository-wide **/package-lock.json; verified via git check-attr.
  • CODEOWNERS recursive patterns (P2) — rejected with evidence. GitHub's CODEOWNERS syntax docs state twice that a trailing-slash pattern owns the directory and any of its subdirectories (/build/logs/, /docs/), so /.github/ already covers .github/workflows/ci.yml. Details on the thread.

Both prose defects were inherited verbatim from the old 477-line README rather than introduced by the split — worth knowing, since the same two sentences were live on main before this PR.

Please re-review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d51744b05c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/runbooks/backup-and-restore.md Outdated
Comment on lines +53 to +55
Start the API and confirm `/health/ready` returns 2xx — it 503s while any
migration is pending (#263), which is also how a restore of an older schema
announces itself.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Run migrations before restarting after an older restore

When the restored dump predates the current schema, starting the API does not bring it forward: Production sets Database:MigrateOnStartup=false, and the already-completed Compose migrate service is not rerun by restarting app. Readiness will therefore remain 503 rather than merely “announce” the stale schema, leaving the recovery incomplete; add an explicit one-shot migrate invocation before restarting the API.

AGENTS.md reference: AGENTS.md:L63-L63

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed and fixed in 60fd07d. Verified against deploy/docker-compose.yml: migrate is a one-shot service with command: ["migrate"] that app waits on via service_completed_successfully, so a completed job plus Database:MigrateOnStartup=false means restarting app applies nothing and readiness stays 503 indefinitely.

The restore section now runs the job explicitly before starting the API:

docker compose -f deploy/docker-compose.yml --env-file deploy/.env run --rm migrate

and the wording no longer implies the 503 clears on its own. The drill gained the older-dump case: restore a pre-migration dump, confirm readiness stays 503 until migrate has run.

…tore

Codex review round 2 on PR 548, P2: the restore procedure said to start the API
and treat a 503 as the schema announcing itself. It does not self-heal.
Production sets Database:MigrateOnStartup=false and the Compose migrate job has
already completed, so restarting app never applies the pending migration and
readiness stays 503.

The runbook now runs the one-shot migrate service before starting the API, and
the drill covers the older-dump case.
@mforce

mforce commented Aug 16, 2026

Copy link
Copy Markdown
Owner Author

@codex round 2 addressed in 60fd07d.

Migrate before restarting after an older restore (P2) — confirmed real, fixed. Verified against deploy/docker-compose.yml: migrate is a one-shot service app waits on with service_completed_successfully, and Production sets Database:MigrateOnStartup=false, so restarting app applies nothing and /health/ready stays 503 rather than announcing-then-clearing. The runbook now runs run --rm migrate explicitly before starting the API, and the drill covers the pre-migration-dump case.

Running total on this PR: 4 confirmed product/doc defects, 1 finding rejected with evidence. Please re-review.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 60fd07d9d6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mforce
mforce merged commit b3f3fcf into main Aug 16, 2026
10 checks passed
@mforce
mforce deleted the docs/readme-split branch August 16, 2026 19: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