Smoke-sweep fixes: exact saves, local dates, safer remove, readable markers
- Garden and plant dialogs keep centimeters as the source of truth (LengthField in lib/units.ts): a no-change Save no longer rewrites 900 cm as 899.922 or a 45 cm spacing as 44.958, bumping versions and writing bogus history entries on the way. - The UI stamps every date with the browser's local day (lib/dates.ts). Journal notes already did; plop placement, fill and removal now do too, so a 9 pm placement isn't "planted tomorrow". The fill endpoint gained an optional plantedAt; API and agent callers still default to UTC today. - Removing an object that holds plants asks first and says how many go with it. An empty one still goes straight away (one Undo restores it). - The expanded plant card's action row wraps instead of clipping "Delete". - Monogram lettering switches to a dark ink on pale marker colors (garlic, cabbage, marigold) instead of near-white on near-white. - Copy-as-plan proposes the next free year and warns when the typed name already exists, so two gardens can't both read as "the 2027 plan". - Plan cards show the base name with a "2027 plan" tag, so the year — the point of the name — survives truncation. - A rejected model spec now says which model and why: a wrapped ErrInvalidInput's reason reaches the client as the 400's message, and the Settings field shows it inline instead of toasting "invalid input". Also defuses a clock bomb in TestRemainingReturnsWhenAPlantingIsRemoved, which only passed while the real date was before 2026-08-01. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -129,6 +129,21 @@ Conventions that follow from it:
|
||||
a constraint between neighbouring plants; a bed edge is nobody's neighbour.
|
||||
- **Soft removal**: "clear bed" sets `removed_at`; the editor reads
|
||||
`removed_at IS NULL`. Hard delete is a different operation.
|
||||
- **Length fields keep centimeters as the source of truth.** A dialog field
|
||||
that takes a length is a `LengthField` (`web/src/lib/units.ts`): the text is
|
||||
a view, `cm` changes only when the person types. Never re-parse the display
|
||||
string on save — "29′ 6.3″" is the nearest tenth of an inch, and parsing it
|
||||
back is how a no-change Save turned 900 cm into 899.922 (and bumped the
|
||||
version, and wrote a bogus history entry). The inspector guards the same way.
|
||||
- **"Today" is the browser's local day**, from `today()` in
|
||||
`web/src/lib/dates.ts`, and the UI always sends it: journal `observedAt`,
|
||||
plop/fill `plantedAt`, `removedAt`. The server's UTC default is only for
|
||||
API callers and the agent. A gardener placing at 9 pm in Ohio planted today,
|
||||
not tomorrow — don't add a UI path that leaves the date to the server.
|
||||
- **A wrapped `ErrInvalidInput` is shown to the person verbatim.**
|
||||
`fmt.Errorf("%w: chat model %q: unknown provider", domain.ErrInvalidInput, spec)`
|
||||
reaches the client as the 400's message (minus the sentinel prefix); the bare
|
||||
sentinel reads "invalid input". Write the reason for the keyboard, not the log.
|
||||
- **Migrations** are numbered `.sql` files in `internal/store/migrations/`, run
|
||||
at startup, embedded. Never edit one that has shipped.
|
||||
- **Every service mutation lands in history** (#48). If you add one, record it —
|
||||
|
||||
Reference in New Issue
Block a user