Repository navigation
Description
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
- Admin-gate corrective/destructive actions — stepping stone to full RBAC (F19: Admin-gate corrective/destructive actions — stepping stone to full RBAC #73)
- Roles + scoped permissions (house/flock-level RBAC UI; builds on F19: Admin-gate corrective/destructive actions — stepping stone to full RBAC #73) (F27: Roles + scoped permissions — role model, flock-scoped workers, RBAC UI (builds on #73) #103, batches Tech debt: magic strings — duplicated role/claim constants, untyped SPA status strings #84; PR F27: Roles + scoped permissions — role model, flock-scoped workers, RBAC UI #104)
- Product catalog with egg products (replace raw grade strings) — part 1 catalog (F25 (part 1): Product catalog — egg products, grade mappings, packed-unit conversions #97), part 2 sales rewiring (F25 (part 2): Sales lines sell products — unit factor snapshots, quantity_base, grade-mapped allocation #99)
- Egg grade management — CRUD UI + endpoints (F6: Egg grade management — CRUD UI + endpoints #42; pulled forward from 1.0 QA)
- Egg inventory movement ledger (explicit rows + cached balances) (F26: Egg inventory movement ledger — explicit movement rows, rebuildable cached balances #101)
- Feed inventory + feed usage (F15: Feed inventory + feed usage #66; PRs F15 (1/2): Inventory foundation — items, lots, purchases, movement ledger (#66) #68 + F15 (2/2): Feed usage + stock corrections (#66) #70)
- Water usage (F16: Water usage tracking #67, PR F16: Water usage tracking (#67) #76)
- Flock management — edit/deplete/archive + Flocks screen (F7: Flock management — edit/deplete/archive + Flocks screen #47; pulled in during 1.1, PR F7: Flock management — edit/deplete/archive + Flocks screen (#47) #48)
- Mortality/culling — bird movement ledger + current flock counts (F10: Mortality/culling — bird movement ledger + current flock counts #54, PR F10: Mortality/culling — bird movement ledger + current flock counts (#54) #55)
- Reactivate flock — undo deplete/archive (F11: Reactivate flock — undo deplete/archive #57, PR F11: Reactivate flock — undo deplete/archive (#57) #62)
- Demo seed data — Seed:Demo flag for dev/demo environments (F12: Demo seed data — Seed:Demo flag for dev/demo environments #58)
- Destructive-action guards — confirms + prefill-failure overwrite fix (F13: Destructive-action guards — confirms + prefill-failure overwrite fix #59, PR F13: Destructive-action guards + prefill-failure overwrite fix (#59) #61)
- Void confirmed sales order — return allocated stock (F14: Void confirmed sales order — return allocated stock #60, PR F14: Void confirmed sales order — return allocated stock (#60) #64)
- Hardening: DurableJobWorker crash kills the API on a transient DB outage (Hardening: DurableJobWorker crash takes down the whole API on a transient DB outage #65)
- Edit submitted daily entries — auto-lock + manager adjust with lot/movement reconciliation (F17: Edit submitted daily entries — lock job + manager adjust with lot/movement reconciliation #69)
- F17 follow-up: voided entry frees its day for re-entry (F17 follow-up: voided daily entry permanently blocks re-entering that day #82)
- Tech debt: magic strings — role/claim constants, typed SPA statuses (Tech debt: magic strings — duplicated role/claim constants, untyped SPA status strings #84 — rode with F27: Roles + scoped permissions — role model, flock-scoped workers, RBAC UI (builds on #73) #103, PR F27: Roles + scoped permissions — role model, flock-scoped workers, RBAC UI #104)
- History: Draft rows link to editing on the Daily entry screen (History: Draft rows need an edit affordance pointing at the Daily entry screen #85)
- In-app help — user guide + glossary in the SPA (F18: In-app help — user guide + glossary in the SPA #71, PR F18: In-app help — user guide + glossary (#71) #74)
- Expenses — basic categories + recording, admin-only (F20: Basic expenses — categories + expense recording with monthly view (admin-only) #87)
- Customer payments + balances — order-attached, outstanding per order/customer, admin-only (F21: Customer payments + balances — order-attached payments, outstanding per order/customer (admin-only) #89)
- Dashboard — landing screen with today's numbers (F5: Dashboard — landing screen with today's numbers #41)
- Core reports — production (worker-open) + sales/expense/profit summaries, admin (F22: Core reports — production (worker-open) + sales/expense/profit summaries (admin) #91)
- Audit log for critical changes — same-transaction events + admin viewer (F23: Audit log for critical changes — same-transaction append-only events + admin viewer #93)
- Audit filter/entityType drift — client list had fallen 3 action codes + FarmLogo behind the server; brought back to 1:1 (Audit log: action filter + entityType labels drift from server (3 actions + FarmLogo missing) #247, PR fix(web): add 3 missing audit actions + FarmLogo entityType to filter/labels (#247) #255, merged)
- Prevent client↔server audit-vocabulary drift long-term — derive/validate the SPA action/entityType list against the server (Tech debt: magic strings — duplicated role/claim constants, untyped SPA status strings #84 magic-strings; Audit vocabulary: validate the client action/entityType list against the server so it can't silently drift (#247 root cause) #258, PR fix(api): guard the audit action/entityType vocabulary against drift #439, merged)
- Audit page: cascading record-type filter to narrow the action dropdown (Audit page: cascading record-type filter to narrow the action dropdown #520)
- Show "created by / when" and "last changed by / when" inline on record pages (Show "created by / when" and "last changed by / when" inline on record pages #494, PR feat(api,web): show who created and last changed a record, inline on its own page (#494) #503, merged)
- CSV export / manual backup — per-dataset CSVs + full account zip + dump instructions (F24: CSV export / manual backup — per-dataset CSVs, full account export (zip), self-host dump instructions #95)
- Responsive/mobile pass — spec KD-6/wireframes (F9: Responsive/mobile pass — usable on phones and tablets #49, PR F9: Responsive pass — phones and tablets (#49) #51; offline capture split to Offline data capture (PWA) — tech spec KD-4/§6 #50, Phase 1.5)
- Mobile: bottom nav bar hidden on History, Flocks, Inventory, Grades, Products, Help (Mobile: bottom navigation bar hidden on History, Flocks, Inventory, Grades, Products, Help #441)
- i18n infrastructure (API half) — additive errorCodes +
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)- i18n: explicit errorCodes on the remaining validators (i18n: assign explicit errorCodes to the remaining 30 validators #231, PR feat(api): assign explicit errorCodes to all validators + coverage guard (#231) #257, merged) — ~220 Feature.Field.Rule codes across all 34, additive, + a reflection guard that fails the build on any uncoded rule
- i18n infrastructure (SPA foundation + full string sweep) — react-i18next + resolution + the complete B0–B6b string sweep, code-complete and merged (i18n infrastructure (SPA foundation): react-i18next + resolution + string sweep (tracker) #182; final PRs F182 B6b: i18n sweep — Help glossary + final sweep audit (sweep complete) #252 sweep audit + fix(web): remember last UI language on this device (login screen + F5 no longer revert to English) (#182) #253 login/refresh language memory). Every client-authored SPA string is externalized (843→3 false positives) and machine-drafted es/tl render app-wide. Only the native-speaker es/tl review remains → Phase 1.5 (EPIC: Phase 1.5 — Egg product hardening #15); i18n infrastructure (SPA foundation): react-i18next + resolution + string sweep (tracker) #182 stays open as its tracker.
- Farm settings — profile (name/logo) + locale/timezone/currency editing + §4.6 currency-change guard, API + SPA (Farm settings — profile (name/logo) + locale/timezone/currency editing + §4.6 currency-change guard (API + SPA) #123)
- Per-farm theme — curated accent palettes in farm settings, follows Farm settings — profile (name/logo) + locale/timezone/currency editing + §4.6 currency-change guard (API + SPA) #123 (Per-farm theme — curated accent palettes in farm settings (API + SPA) #149)
- Baseline seeder: pin that a freshly seeded farm can still change its currency (Baseline seeder: nothing pins that a freshly seeded farm can still change its currency #178; found while exercising Farm settings — profile (name/logo) + locale/timezone/currency editing + §4.6 currency-change guard (API + SPA) #123's settings screen — the §4.6 rule is well covered, the seeder's starting state is not)
- Close the §4.6 currency-lock race properly — shared account-row locks across all money-writing handlers (Close the §4.6 currency-lock race properly (shared lock on the account row across money-writing handlers) #162, PR Close the §4.6 currency-lock race: shared account-row locks across money-writing handlers (#162) #235, merged)
- SPA: pending/busy state on every save — lock waits are invisible to the user (SPA: pending/busy state on every save — lock waits (#162) are invisible to the user #236; found while testing Close the §4.6 currency-lock race properly (shared lock on the account row across money-writing handlers) #162: a save waiting on the account lock looks frozen)
- Farm branding surfaces: compact sidebar mark + a home for the detailed logo (post-login splash) (Farm branding surfaces: a compact sidebar mark + a home for the detailed logo (post-login splash) #179; follows Farm settings — profile (name/logo) + locale/timezone/currency editing + §4.6 currency-change guard (API + SPA) #123)
- Date/Time format override fields accept bogus-looking free text — offer common-preset dropdown + tighten server validation (Farm settings: Date/Time format override fields accept bogus-looking free text #452)
- Users: Name settable at creation + editable afterwards (Users — Name can be set at creation and edited afterwards (API + SPA) #163; absorbed Users — edit a user's Name after creation (API + SPA) #164)
- Users: password management — admin set + self-service change (Users — password management: admin sets a user's password + self-service change (API + SPA) #165; absorbed Users — self-service password change for the signed-in user (API + SPA) #166)
- Credential epoch: per-request revocation check, deployed inert first (Credential epoch: per-request revocation check, deployed inert ahead of the user-admin mutations #364; spec PR spec: design user disable/enable and email change (#356, #357) #358) — blocks the two below
- Users: disable / re-enable a user (Users: disable / re-enable a user #356; spec PR spec: design user disable/enable and email change (#356, #357) #358; PR feat(users): disable and re-enable a user without deleting them (#356) #492, merged)
- Users: change a user's role after creation (promote/demote) (Users: change a user's role after creation (promote/demote) #355)
- SPA test infrastructure — Vitest + Testing Library, first coverage on
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)- SPA tests:
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) - SPA tests: extract
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) - SPA tests: pure utils — tokenStore,
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) - SPA tests: component-render suite, mocked API (SPA tests: component-render suite (mocked API) #110, PR F28 follow-up: component-render tests — StockPage + real AuthContext (#110) #114)
- SPA tests: StockPage drill-down + AuthProvider lifecycle (SPA tests: StockPage lot/movement drill-down + AuthProvider lifecycle #115, PR F28 follow-up: StockPage drill-down + AuthProvider lifecycle tests (#115) #118)
- SPA tests: Login + ProtectedRoute gating + renderWithProviders helper (SPA tests: Login + ProtectedRoute auth gating (+ renderWithProviders helper) #116, PR F28 follow-up: renderWithProviders helper + Login + ProtectedRoute tests (#116) #119)
- SPA tests: business-logic screens — DailyEntry (F28 follow-up: DailyEntryPage accuracy-gating tests (part of #117) #120), Sales + History (SPA tests: business-logic screens - DailyEntry, Sales, History #117, PR F28: SPA tests for Sales money-parse + History sellable guard (#117) #122)
- CI: SPA coverage gate — Vitest thresholds + ratchet (CI: enforce SPA coverage gate (Vitest thresholds + ratchet) #121, PR F29: CI SPA coverage gate — Vitest thresholds + ratchet (#121) #124; floor re-baselined Fix red main: re-baseline SPA coverage floor after #122 #125)
- UI design revamp — distinctive visual identity: aubergine design system, sidebar + icons, light/night, status badges (UI design revamp — distinctive visual identity #52, PR F52: SPA visual identity — aubergine design system, sidebar, badges, light/night #130)
- Inline add/edit forms → modal dialogs — accessible Dialog component, screen-by-screen migration (Inline add/edit forms → modal dialogs (less-intrusive capture) #131, PR F131: move add/edit capture into accessible modal dialogs #132)
- Capture screens: Users flock-assign moved to dialog; Water/expense/Sales stay inline by decision (Capture screens: move Water / expense-add / Sales add-line forms into dialogs? #133, PR Users: move flock-assignment into the shared dialog; keep capture forms inline (#133) #154, merged)
- One-way actions:
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)
- Daily entry UX: two-pane workspace, steppers, live remainder, draft badge (Daily entry UX: numbered sections, sticky summary footer, draft-edit badge (wizard rejected) #134, PR F134: numbered steps, pinned footer and a draft badge on Daily entry #137)
- Mobile nav: bottom tab bar + More sheet (Mobile nav: bottom tab bar + More sheet (the top bar eats a third of the screen) #147, PR Mobile nav: bottom tab bar + More sheet #148, merged)
- Mobile data tables: scroll-cue for the clipped last column (Mobile data tables scroll sideways with no cue — the last column is silently clipped #150, PR Mobile: scroll-shadow cue on horizontally-scrolling data tables (#150) #151, merged)
- SPA tests:
- Observability: activate the telemetry pipeline — OTLP trace export + request logging + TraceId↔log correlation (Observability: activate the telemetry pipeline — OTLP trace export, request logging, TraceId↔log correlation #214, PR feat(api): activate the telemetry pipeline — OTLP export, request logs, TraceId correlation #226, merged)
- Observability: metrics — runtime/HTTP/DB meters over OTLP (Observability: metrics — runtime, HTTP, and DB meters over OTLP #215, PR feat(api): export metrics over OTLP beside traces #227, merged)
- Observability: structured logs at domain state transitions (Observability: structured logs at domain state transitions #216, PR feat(app): structured logs at domain state transitions #228, merged)
- Observability: frontend error reporting — ErrorBoundary → API + production sourcemaps (Observability: frontend error reporting — ErrorBoundary → API + production sourcemaps #217, PR Observability: frontend error reporting — ErrorBoundary → API + traceparent + sourcemaps (#217) #232, merged)
- Display app version in the SPA, sourced from release-please's version.txt (Display app version in the SPA, sourced from release-please's version.txt #458)
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)
Activity
Mark Phase 1.0 shipped in AGENTS.md and spec (#40)
mforce commentedon Jul 16, 2026
First 1.1 slices filed: #41 (dashboard), #42 (egg grade management UI). #42 partially covers the "product catalog" item — full product catalog (egg products beyond grades) remains unsliced.
Spec: multi-language support phasing — i18n infra in 1.1, first trans…
F28: SPA test infrastructure — Vitest + Testing Library, first covera…
F29: CI SPA coverage gate — Vitest thresholds + ratchet (#121) (#124)
F52: SPA visual identity — aubergine design system, sidebar, badges, …
docs(agents): update phase context now that epic #14 is closed (#515)