Skip to content

feat: suspend-account / reactivate-account operator verbs (#534) - #573

Merged
mforce merged 3 commits into
mainfrom
feat/534-suspend-reactivate-account
Aug 21, 2026
Merged

mforce merged 3 commits into
mainfrom
feat/534-suspend-reactivate-account

Conversation

@mforce

@mforce mforce commented Aug 20, 2026

Copy link
Copy Markdown
Owner

What changed

  • Added production-capable suspend-account and reactivate-account one-shot CLI verbs with optional reasons.
  • Added lifecycle audit actions/system actors and writes audit rows in the suspension transaction only on real transitions.
  • Added CLI, lifecycle, suspended-farm lock-sweep, and minimal-config coverage; translated audit labels and updated the glossary.

Verification

  • dotnet restore Cluckwork.sln --locked-mode: All projects are up-to-date for restore.
  • dotnet build Cluckwork.sln --configuration Release --no-restore: Time Elapsed 00:00:04.67
  • dotnet test Cluckwork.sln --configuration Release --no-build --verbosity normal: Time Elapsed 00:01:48.34 (1390 tests passed)
  • tools/schema-docs/generate.sh --check: docs/schema/ is up to date.
  • cd web && npm run typecheck && npm run test:coverage && npm run build && npm run verify:sw: [service worker] dist/sw.js: /api and /health excluded from the navigation fallback, no runtime caching strategy, 22 shell entries precached and no API path among them. (1814 tests passed)

Mutation checks

Mutant Observed result
M1 duplicate audit write RED: Assert.Equal expected 1, actual 2 (AccountLifecycleCommandTests.cs:88); restored GREEN.
M2 revoke on active reactivation RED: Assert.Equal expected 1, actual 0 (AccountLifecycleCommandTests.cs:124); restored GREEN.
M3 unknown suspend slug exits 0 RED: Assert.Equal expected 1, actual 0 (AccountLifecycleCommandTests.cs:140); restored GREEN.
M4 discard audit reason RED: Assert.Equal expected non-payment drill, actual null (AccountLifecycleCommandTests.cs:69); restored GREEN.
M5 audit after commit RED: Sequence contains no elements at the audit query (AccountLifecycleCommandTests.cs:64); restored GREEN. This proves the write must precede SaveChanges, and does not prove crash atomicity.
M6 skip suspended farms in lock sweep RED: Assert.Equal expected Locked, actual Submitted (DailyEntryAdjustTests.cs:605); restored GREEN.

grep -rn "MUTANT" src/ tests/ printed two unrelated third-party DLL matches under ignored bin/; excluding bin and obj produced no source markers.

Closes #534

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@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: 39346d7d55

ℹ️ 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 on lines +142 to +145
if (suspending)
account.Suspend();
else
account.Reactivate();

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 Skip the aggregate mutation for lifecycle no-ops

When reactivate-account is retried against an already-active farm, stateChanged is false but this branch still calls Reactivate(), which increments the account's concurrency-token Version before SaveChangesAsync. Consequently, despite reporting that nothing changed, the command invalidates versions held by active users and can make a subsequent Farm Settings save fail with Account.VersionMismatch; an already-suspended retry similarly increments the token. Gate the aggregate call on stateChanged while retaining the independent suspend revocation sweep.

AGENTS.md reference: AGENTS.md:L71-L71

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

You are right about the mechanism: Account.Suspend()/Reactivate() bump Version unconditionally by design (#531, pinned by AccountSlugTests), and Version is the token UpdateFarmSettingsHandler.cs:22 compares a Farm Settings save against — so a no-op operator retry advanced it and the next save from anyone holding that form open failed with Account.VersionMismatch.

Fixed at the service rather than the aggregate, so "every aggregate mutation bumps Version" stays true — on a no-op there is now no mutation:

if (stateChanged)
{
    if (suspending)
        account.Suspend();
    else
        account.Reactivate();
}

The revoke sweep is deliberately not gated by this: a suspend-account re-run still re-revokes (owner decision for this slice), and it touches user and token rows, never the account row.

Verification, driver-run rather than quoted from the implementer:

  • dotnet build Cluckwork.sln -c Release — 0 Warning(s), 0 Error(s).
  • dotnet test Cluckwork.sln -c Release --no-build — 1886 passed / 0 failed (integration 1390 -> 1394).
  • Mutation: removing the new gate turns ReactivateVerb_OnAnAlreadyActiveFarm_DoesNotAdvanceTheFarmSettingsVersion red (Assert.Equal() Failure: Values differ); restored, rebuilt, green.
  • Mutation: revokeSessions = suspending || stateChanged -> = stateChanged now kills exactly one test of 26 across both suspension suites. Before this round it killed none — that gap was found by mutation, not by review, and SuspendVerb_RunAgainstAnAlreadySuspendedFarm_StillRevokesALiveSession closes it.

@mforce

mforce commented Aug 21, 2026

Copy link
Copy Markdown
Owner Author

Review round 1 pushed: a9e4d50bbbde728799614cccd6cda4c94f9ea650

  • Gate account aggregate lifecycle calls on stateChanged, preserving Account.Version for no-op suspend/reactivate retries while keeping the suspend re-revoke sweep unconditional.
  • Add lifecycle CLI coverage for re-revocation, no-op version preservation, both missing-slug verbs, reactivation audit actor/reason, unknown-slug state/version preservation, and invariant uppercase slug lookup.

Verification: Release build clean; full suite 1394/1394 passed. All four prescribed mutants compiled and were killed.

@mforce

mforce commented Aug 21, 2026

Copy link
Copy Markdown
Owner Author

@codex the Account.Version finding is addressed in a9e4d50b — the aggregate mutation is now gated on the pre-mutation transition check, so a no-op suspend-account / reactivate-account retry no longer advances the token a Farm Settings save is compared against. Details and the mutation evidence are on the original thread.

The same commit closes five test gaps found in the same round: the suspend re-run’s re-revoke, the reactivate audit row’s system actor and --reason, the unknown-slug test’s previously unfalsifiable "changed nothing" assertions, the uppercase-slug fold, and the missing---slug guard on the reactivate verb.

Driver-run verification at a9e4d50b: build 0 warnings / 0 errors, 1886 tests passed / 0 failed, and every mutation for this round red on its named test then green after restore.

Please re-review at a9e4d50b — the previous round hit your security-review usage limit, so that pass was partial.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: a9e4d50bbb

ℹ️ 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 commented Aug 21, 2026

Copy link
Copy Markdown
Owner Author

Review round 2 test-only increment: 124fe78

Verification (dotnet build Cluckwork.sln --configuration Release --no-restore):

Build succeeded.
    0 Warning(s)
    0 Error(s)
Time Elapsed 00:00:01.56

Verification (dotnet test Cluckwork.sln --configuration Release --no-build --verbosity normal):

Test Run Successful.
Total tests: 1394
     Passed: 1394
 Total time: 1.7594 Minutes
Build succeeded.
    0 Warning(s)
    0 Error(s)
Time Elapsed 00:01:46.02

Mutation R2-M1 killed: changing if (stateChanged) to if (stateChanged || suspending) compiled, then SuspendVerb_RunTwice_ExitsZero_AndWritesNoSecondAuditRow failed its new version assertion (Expected: 1, Actual: 2, line 106). Restored the production file, touched it, rebuilt cleanly, re-ran the focused test green, and grep -rn "MUTANT" src/ tests/ --include='*.cs' returned no matches.

@mforce

mforce commented Aug 21, 2026

Copy link
Copy Markdown
Owner Author

@codex your last pass was clean against a9e4d50b, but the head has moved to 124fe78a since — please re-review at the current head.

124fe78a is test-only. Round 2 found that the round-1 fix gates the aggregate mutation symmetrically for suspend and reactivate, while only the reactivate side was pinned: a stateChanged || suspending mutant around that gate passed the entire suite. SuspendVerb_RunTwice_ExitsZero_AndWritesNoSecondAuditRow now also asserts Account.Version is unchanged across the second run, and that mutant dies on the new assertion (Expected: 1, Actual: 2).

Driver-run verification at 124fe78a: build 0 warnings / 0 errors, 1886 tests passed / 0 failed.

Also for the record, since it is visible in this diff and is deliberate: the check-then-mint window in login (a suspension committing between the IsActive check and token issuance) is NOT closed here. It was declined in #532 on login-hot-path cost, the credential it produces is inert, and it is now tracked as #579 rather than being silently dropped from #534.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 124fe78a3f

ℹ️ 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 commented Aug 21, 2026

Copy link
Copy Markdown
Owner Author

Review loop stopped deliberately at 3 rounds. Recording that here so the quiet is not read as an unfinished response.

Count, by confirmed defects in product code:

Round Head Product defects What it found
1 39346d7d 1 Account.Version bumped on a no-op run, breaking open Farm Settings saves (codex). Plus 5 test gaps, and 2 findings rejected with evidence.
2 a9e4d50b 0 The round-1 fix was symmetric but only pinned on the reactivate side — a stateChanged || suspending mutant passed the whole suite. Test-only fix.
3 124fe78a 0 Codex clean at the current head; no line-level findings.

Two consecutive rounds yielding no defect in shipped code is the stop point, so I am not tagging for further rounds. Four reviewers ran across the loop: the Codex connector, two independent agents briefed on distinct defect classes (invariants; false-green/deletable-guard), and a local review model.

Final state, driver-verified rather than quoted from the implementer:

  • dotnet build Cluckwork.sln -c Release — 0 Warning(s), 0 Error(s).
  • dotnet test Cluckwork.sln -c Release --no-build — 1886 passed / 0 failed.
  • 9 mutations run and re-run by the driver; each died on its named test and went green after restore, with the tree verified clean afterwards. The one mutation that survived (round 1: revokeSessions weakened so a suspend re-run stops revoking) is what produced SuspendVerb_RunAgainstAnAlreadySuspendedFarm_StillRevokesALiveSession, and it now dies.
  • Deliberately not delivered here and tracked instead: the login check-then-mint window, now Suspension: login can still mint an inert credential in the check-then-mint window #579.

Not merging this myself — that is the owner’s call.

@mforce
mforce merged commit d0be26c into main Aug 21, 2026
10 checks passed
@mforce
mforce deleted the feat/534-suspend-reactivate-account branch August 21, 2026 05:00
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.

CLI: suspend-account / reactivate-account operator verbs

1 participant