Commit Graph
5 Commits
Author SHA1 Message Date
steveandClaude Opus 4.8 94aed26e14 Address Gadfly review on seed lots
Build image / build-and-push (push) Successful in 15s
Three real bugs, two of which would have quietly corrupted inventory numbers.

CopyGarden carried seed_lot_id into the copied plantings, so copying a garden
double-counted the original lot's usage — the copy is a plan, not a second
planting out of the same packet. It also quietly attached a private lot to a
garden that may later be shared, contradicting the "lots are never shared with a
garden" invariant this feature was built on. The copy now drops the link.

RestorePlanting restored a seed_lot_id verbatim from a history snapshot. Live
rows get their link nulled by ON DELETE SET NULL when a lot is deleted, but the
snapshot still holds the old id, so undoing a planting deletion after retiring
its lot would violate the foreign key and abort the whole revert. The revert now
drops a dangling link before restoring: the plant really was in the ground,
which is the part worth restoring.

CreateSeedLot returned a lot with Used/Remaining at their zero values, so a
packet you just bought reported "0 remaining" — the exact opposite of the truth.
The version-conflict path had the same gap, and that row is what the client
rebases on, so a 409 reporting remaining=0 is worse than no number at all. Both
fill them now.

Remaining can go negative and that is deliberate — it means more was planted
than the lot recorded buying, which happens (a miscounted packet, a lot entered
after the fact). Clamping to zero would hide the discrepancy at exactly the
moment it is worth seeing. Now documented and tested rather than incidental.

Performance and tidiness: SeedLotUsage is scoped to the lots being filled
instead of scanning every planting the owner has, so reading one lot is one
lot's work; ListSeedLotsForOwner gained the LIMIT every other list read has;
parseNullable moved out of seed_lots.go into the shared request helpers, since
every nullable field in the API needs that three-way absent/null/value
distinction; finalizePlant validates against its own maxPlantVendorLen rather
than borrowing a constant named for seed lots; seedLotForPlanting became
checkSeedLotForPlanting, since it validates rather than fetches; and the
duplicate intPtr test helper gave way to the existing ptrInt.

Declined: recording seed-lot mutations in the revision history. change_sets are
anchored to a garden and lots are user-scoped, deliberately — they belong to
you, not to any one garden, so there is no garden to record them against.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
2026-07-21 01:38:54 -04:00
steve f208da94d6 Revision history: change sets + revisions + revert — the undo substrate (#48) (#61)
Build image / build-and-push (push) Successful in 5s
Co-authored-by: Steve Dudenhoeffer <[email protected]>
2026-07-21 05:07:30 +00:00
steve 2a86f87b50 Sharing backend: shares CRUD + ACL enforcement everywhere (#16)
Build image / build-and-push (push) Successful in 4s
Co-authored-by: Steve Dudenhoeffer <[email protected]>
2026-07-19 03:49:54 +00:00
steve 293532f021 Plant catalog backend: CRUD + seeded built-ins (#12)
Build image / build-and-push (push) Successful in 5s
Co-authored-by: Steve Dudenhoeffer <[email protected]>
2026-07-19 01:48:22 +00:00
steveandClaude Opus 4.8 3fbefa4e05 Address Gadfly review on #7: garden cap, dim validation, shared errors
Build image / build-and-push (push) Successful in 5s
Fixes from the PR #26 adversarial review (graded 18 real / 0 false positive).

Correctness / security
- maxGardenCM fixed to 10_000 (100 m), matching its comment — it was
  100_000 cm (1 km), 10x too lax (5 models flagged this).
- Dimension validation now rejects NaN/Inf (which slip past naive
  comparisons) and subnormal-tiny positives, via a finite [1cm, 100m]
  check. Name (200) and notes (10_000) are length-capped so untrusted
  input can't balloon storage.
- Update version binding is `required,min=1`, so a negative/zero version
  is a 400, not a 409.

Maintainability / performance
- One unified writeServiceError (new errors.go) maps every auth + resource
  sentinel; writeResourceError removed. writeVersionConflict and
  parseIDParam moved to errors.go (shared, not in the gardens feature file).
- Request structs share an embedded gardenFields (one toInput).
- CreateGarden uses INSERT ... RETURNING (one round-trip).
- ListGardensForOwner has a defensive LIMIT (pagination is post-v1).

Tests: name/notes length, NaN/Inf/subnormal dims, dimension-at-cap valid,
negative/zero version -> 400.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
2026-07-18 18:39:27 -04:00