Turns the MVP loop into the fuller system originally scoped as Phase 1. Slices created when 1.0 ships.
Spec: §6 (Phase 1.1).
Sales: prevent decimal quantities from surfacing JSON-binding errors (Sales: prevent decimal quantities from surfacing JSON-binding errors #398 )
Daily entry: require grading to reconcile sellable eggs before submit (Daily entry: require grading to reconcile sellable eggs before submit #394 )
Flaky: CurrencyLockRaceTests writer-first race fails intermittently in CI (Flaky: CurrencyLockRaceTests.WriterFirst_TheChangeWaitsOutTheWriterTransaction_NotJustItsRead fails intermittently in CI #402 , PR fix(api): prove queue entry for both racers in the currency-lock FIFO test (#402) #425 , merged)
Improve daily entry: saleable Cracked and Dirty quality eggs (Improve daily entry: saleable Cracked and Dirty quality eggs #396 , PR feat(eggs): make cracked and dirty eggs sellable stock via condition grades (#396) #407 , merged)
Stock: let an admin write off lost egg stock without restating production (Stock: let an admin write off lost egg stock without restating production #406 )
Stock: lot drill-down caps at the newest 50 lots — older lots unreachable for history and write-off (Stock: lot drill-down caps at the newest 50 lots — older lots unreachable for history and write-off #465 )
SPA: audit the other load-more/filter screens for the async races hardened in feat(stock): page and date-filter the lot drill-down so older lots stay reachable (#465) #467 (SPA: audit the other load-more/filter screens for the async races hardened in #467 #469 )
Sales: a mutation error inside the new-order and payment dialogs never renders (Sales: a mutation error inside the new-order and payment dialogs never renders #474 )
SPA: apply the dialog/page error split to the other dialog screens (SPA: apply the dialog/page error split to the other dialog screens #479 )
SPA: the success message slot is shared the same way the error slot was — closed not_planned, reasoning recorded on the issue (SPA: the success message slot is shared the same way the error slot was #490 )
Sales: both dialogs can be open at once, so the untagged dialog-error slot misattributes (Sales: both dialogs can be open at once, so the untagged dialog-error slot misattributes #480 )
Dialog: two open instances brick page scrolling, Escape closes both, no background inertness (Dialog: two open instances brick page scrolling, and Escape closes both — plus no background inertness #482 )
PWA: the update banner's announcement is swallowed while a dialog is open (PWA: the update banner's announcement is swallowed while a dialog is open #485 )
Sales: add-item quantity field doesn't make clear which unit it expects (Sales: add-item quantity field doesn't make clear which unit it expects — users type egg count instead of pack-unit count #445 )
Feed usage: promote out of Inventory drill-down + wire feed/water into Daily Entry via reserved DailyEntryId link (Feed usage: promote out of the Inventory drill-down + wire feed/water into Daily Entry via the reserved DailyEntryId link #446 )
Flaky: Refresh_ConcurrentPresentationsOfSameToken_NeverForkIntoTwoSessions can hit 0 live tokens (theft response instead of grace) #468 — flaky refresh-race test (grace vs theft classification)
Flaky: IdempotencyRecordPurgeSweepTests lock-block probe times out on CI (sweep DELETE never blocked) #470 — flaky idempotency-sweep lock-block probe timeout on CI
Flaky: i18n spec's persist assertion can match the reset's PUT, so the language durability check races #486 — i18n spec's persist assertion could match the reset's PUT, racing the durability check
CI: Playwright failures are undiagnosable — trace.zip is not uploaded and the stack-log tail is too short #487 — CI: Playwright trace.zip not uploaded and stack-log tail too short to diagnose a failure (PR ci(e2e): keep the stack logs a Playwright failure needs to be diagnosable #513 , merged)
API: make process role explicit for boot guards, so a serving-only guard cannot abort a one-shot CLI verb (refactor(api): make process role explicit for boot guards #347 )
API: fail the boot on a missing or unusable JWT signing key, instead of booting green and 500ing every authenticated request (fix(api): the Jwt PEM 'not configured' guard is dead for the shipped config, and the key is only parsed per-request #510 )
SPA: entity-scoped "Audit history" link on a record's own screen, reusing the existing /audit entityId filter (Audit: entity-scoped "View history" — who created/changed a specific record, and when #493 , PR feat(web,api): entity-scoped audit history reachable from any record (#493) #516 )
Seed: seeded demo and simulation records name a real person instead of "(unresolved)", now visible on the Show "created by / when" and "last changed by / when" inline on record pages #494 History columns (fix(seed): seeded audit events carry "(unresolved)" as the actor, now visible on record History columns #500 , PR fix(api): seeded demo and simulation records name a real person #517 )
Closed 2026-08-11 — every item in specs.md §6's Phase 1.1 scope has shipped. Remaining carryover work (found while shipping 1.1, not part of its original scope) moved to #15 (Phase 1.5) — see #15's "Phase 1.1 carryover" section.
Phase 1.1 — Operational fill
Turns the MVP loop into the fuller system originally scoped as Phase 1. Slices created when 1.0 ships.
Includes
users.language+ GET /me + PUT /me/language + user-scoped idempotency (i18n infrastructure (API half): validation error codes + users.language + GET/PATCH /me #45; English-only, shipped, PR F45: i18n infrastructure (API half) — /me, language, errorCodes, idempotency user-scope #229)claims.tsrole decode (SPA test infrastructure — Vitest + Testing Library, start with claims.ts role decode #105, PR F28: SPA test infrastructure — Vitest + Testing Library, first coverage on auth/money logic #106)api/client.tsauth + transparent-refresh flow (SPA tests: api/client.ts auth + transparent-refresh flow #107, PR F28 follow-up: api/client.ts auth + transparent-refresh test suite (#107) #111)parseMoney+ money round-trip (SPA tests: extract parseMoney helper + money round-trip tests #108, PR F28 follow-up: extract parseMoneyToMinorUnits + money round-trip tests (#108) #112)dates.todayIso, ageWeeks (SPA tests: pure utils — tokenStore, dates.todayIso, ageWeeks #109, PR F28 follow-up: pure-util tests — tokenStore, dates.todayIso, ageWeeks (#109) #113)window.confirm/window.prompt→ styled confirm + void-reason dialogs (One-way actions: replace window.confirm / window.prompt with styled dialogs #135, PR F135: replace window.confirm/prompt with the app's own dialogs #136)Spec: §6 (Phase 1.1).
Migrated from Gitea issue #14 (https://gitea.mforcelabs.com/mforce/cluckwork/issues/14).
Sales: prevent decimal quantities from surfacing JSON-binding errors (Sales: prevent decimal quantities from surfacing JSON-binding errors #398)
Daily entry: require grading to reconcile sellable eggs before submit (Daily entry: require grading to reconcile sellable eggs before submit #394)
Flaky: CurrencyLockRaceTests writer-first race fails intermittently in CI (Flaky: CurrencyLockRaceTests.WriterFirst_TheChangeWaitsOutTheWriterTransaction_NotJustItsRead fails intermittently in CI #402, PR fix(api): prove queue entry for both racers in the currency-lock FIFO test (#402) #425, merged)
Improve daily entry: saleable Cracked and Dirty quality eggs (Improve daily entry: saleable Cracked and Dirty quality eggs #396, PR feat(eggs): make cracked and dirty eggs sellable stock via condition grades (#396) #407, merged)
Stock: let an admin write off lost egg stock without restating production (Stock: let an admin write off lost egg stock without restating production #406)
Stock: lot drill-down caps at the newest 50 lots — older lots unreachable for history and write-off (Stock: lot drill-down caps at the newest 50 lots — older lots unreachable for history and write-off #465)
SPA: audit the other load-more/filter screens for the async races hardened in feat(stock): page and date-filter the lot drill-down so older lots stay reachable (#465) #467 (SPA: audit the other load-more/filter screens for the async races hardened in #467 #469)
Sales: a mutation error inside the new-order and payment dialogs never renders (Sales: a mutation error inside the new-order and payment dialogs never renders #474)
SPA: apply the dialog/page error split to the other dialog screens (SPA: apply the dialog/page error split to the other dialog screens #479)
SPA: the success message slot is shared the same way the error slot was — closed not_planned, reasoning recorded on the issue (SPA: the success message slot is shared the same way the error slot was #490)
Sales: both dialogs can be open at once, so the untagged dialog-error slot misattributes (Sales: both dialogs can be open at once, so the untagged dialog-error slot misattributes #480)
Dialog: two open instances brick page scrolling, Escape closes both, no background inertness (Dialog: two open instances brick page scrolling, and Escape closes both — plus no background inertness #482)
PWA: the update banner's announcement is swallowed while a dialog is open (PWA: the update banner's announcement is swallowed while a dialog is open #485)
Sales: add-item quantity field doesn't make clear which unit it expects (Sales: add-item quantity field doesn't make clear which unit it expects — users type egg count instead of pack-unit count #445)
Feed usage: promote out of Inventory drill-down + wire feed/water into Daily Entry via reserved DailyEntryId link (Feed usage: promote out of the Inventory drill-down + wire feed/water into Daily Entry via the reserved DailyEntryId link #446)
Flaky: Refresh_ConcurrentPresentationsOfSameToken_NeverForkIntoTwoSessions can hit 0 live tokens (theft response instead of grace) #468 — flaky refresh-race test (grace vs theft classification)
Flaky: IdempotencyRecordPurgeSweepTests lock-block probe times out on CI (sweep DELETE never blocked) #470 — flaky idempotency-sweep lock-block probe timeout on CI
Flaky: i18n spec's persist assertion can match the reset's PUT, so the language durability check races #486 — i18n spec's persist assertion could match the reset's PUT, racing the durability check
CI: Playwright failures are undiagnosable — trace.zip is not uploaded and the stack-log tail is too short #487 — CI: Playwright trace.zip not uploaded and stack-log tail too short to diagnose a failure (PR ci(e2e): keep the stack logs a Playwright failure needs to be diagnosable #513, merged)
API: make process role explicit for boot guards, so a serving-only guard cannot abort a one-shot CLI verb (refactor(api): make process role explicit for boot guards #347)
API: fail the boot on a missing or unusable JWT signing key, instead of booting green and 500ing every authenticated request (fix(api): the Jwt PEM 'not configured' guard is dead for the shipped config, and the key is only parsed per-request #510)
SPA: entity-scoped "Audit history" link on a record's own screen, reusing the existing /audit entityId filter (Audit: entity-scoped "View history" — who created/changed a specific record, and when #493, PR feat(web,api): entity-scoped audit history reachable from any record (#493) #516)
Seed: seeded demo and simulation records name a real person instead of "(unresolved)", now visible on the Show "created by / when" and "last changed by / when" inline on record pages #494 History columns (fix(seed): seeded audit events carry "(unresolved)" as the actor, now visible on record History columns #500, PR fix(api): seeded demo and simulation records name a real person #517)