Address #127 review: quote the garden name, validate rectangles, require plantId
Build image / build-and-push (push) Successful in 17s

- The plan-name line of the system prompt interpolates the garden's name
  with %q like the rest of the prompt: any editor can rename a garden, and a
  name with a newline in it must not read as an instruction.
- fill_region refuses an inverted rectangle with its corners named, and a
  rectangle that misses the bed (or only touches its edge) is an error from
  the service rather than a successful fill of nothing.
- remove_plantings requires plantId; omitted it would remove plant 0 and
  report success.
- historyEntry.Undo → UndoOf (it holds the reverted change set's id).
- remove_planting's description names list_plantings as an id source.
- RemovePlanting takes the removal date itself; the dateless wrapper had no
  callers left.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-08-23 00:27:48 -04:00
co-authored by Claude Fable 5
parent a1baf4b871
commit d3d7238259
6 changed files with 53 additions and 21 deletions
+10 -1
View File
@@ -555,6 +555,15 @@ func TestToolsFromTheLiveSweep(t *testing.T) {
if r := call("fill_region", map[string]any{"objectId": bed.ID, "plantId": beet.ID}); !r.IsError {
t.Error("fill_region with nowhere to fill succeeded")
}
// An inverted rectangle is refused with its corners named, before the service
// sees it — the mistake a model makes is swapping which way is north.
if r := call("fill_region", map[string]any{"objectId": bed.ID, "plantId": beet.ID, "x0Cm": 40.0, "y0Cm": -60.0, "x1Cm": -40.0, "y1Cm": 60.0}); !r.IsError || !strings.Contains(r.Content, "west of") {
t.Errorf("inverted rectangle: %+v, want a refusal naming the corners", r)
}
// remove_plantings without a plant would "remove" plant 0 — nothing.
if r := call("remove_plantings", map[string]any{"objectId": bed.ID}); !r.IsError || !strings.Contains(r.Content, "plantId") {
t.Errorf("remove_plantings with no plant: %+v, want a refusal asking which plant", r)
}
// place_planting without a radius → one plant at half the spacing; two garlic
// cloves along the north edge, dated today.
ok("place_planting", map[string]any{"objectId": bed.ID, "plantId": garlic.ID, "xCm": -100, "yCm": -50})
@@ -666,7 +675,7 @@ func TestToolsFromTheLiveSweep(t *testing.T) {
t.Fatalf("undo: err=%v conflicts=%+v", err, conflicts)
}
decode(ok("read_history", map[string]any{"gardenId": g.ID, "limit": 3}), &hist)
if hist.Entries[0].Undo == nil || !hist.Entries[2].Undone {
if hist.Entries[0].UndoOf == nil || !hist.Entries[2].Undone {
t.Errorf("after an undo: newest = %+v, undone = %+v; want the revert to point at the removal, and the removal marked undone", hist.Entries[0], hist.Entries[2])
}