internal/service/ops.go header says these bulk ops live on *Service so that "agent tools and any future REST surface inherit the same ACL enforcement". The future REST surface never arrived:
Exposed only as fill_region / clear_object in internal/agent/tools.go:36,44
The objects group in internal/api/api.go:118-121 has PATCH, DELETE, and POST-plantings. No fill, no clear.
On an instance with no OLLAMA_CLOUD_API_KEY, hex-packed bed filling does not exist at all. It's the most valuable bulk operation in a garden planner, it has the most carefully reasoned geometry in the codebase (hexCenters, the half-spacing edge rule from #75), and it's gated behind an optional LLM that a self-hoster may well not configure.
2. The UI's "clear bed" fires N PATCHes and produces N undo steps
Every service mutation auto-scopes its own change set, so clearing a 40-plop bed writes 40 change sets. Undoing it means pressing Undo 40 times.
This directly violates a stated rule in CLAUDE.md:
Multi-row operations pass all their changes to a single record call so they undo as one unit.
ClearObject (ops.go:331) does exactly that — it snapshots the rows, clears them in one UPDATE, and records one change set. So the agent's clear-bed undoes in one click and the UI's clear-bed, for the identical user-facing action, does not. It's also N round trips and a partial-failure surface (objects.ts:346-348) that the service method doesn't have.
Fix
POST /objects/:id/fill and POST /objects/:id/clear, thin handlers over the existing service methods. Point useClearObject at the new endpoint and delete the client-side loop.
Fill needs a region in the body — either a named compass region (NamedRegion already parses "ne", "south half", "all") or an explicit rect. The named form is the ergonomic one and is already written.
The UI affordance for region-fill is the larger half of the work and could be a follow-up: the endpoint alone fixes the agent-only gap and the clear-bed undo bug, and a "fill this bed with…" button in the inspector is a natural next step.
Tests
Per CLAUDE.md, both new routes need API-level tests through the router — this is precisely the case the rule exists for. Worth asserting specifically that clear-bed produces one change set, since that's the behaviour being restored.
Two problems with one fix.
## 1. `FillRegion` has no REST route
`internal/service/ops.go` header says these bulk ops live on `*Service` so that "agent tools and any future REST surface inherit the same ACL enforcement". The future REST surface never arrived:
- `FillRegion` (`ops.go:108`), `FillNamedRegion` (`ops.go:314`), `ClearObject` (`ops.go:331`)
- Exposed only as `fill_region` / `clear_object` in `internal/agent/tools.go:36,44`
- The `objects` group in `internal/api/api.go:118-121` has PATCH, DELETE, and POST-plantings. No fill, no clear.
**On an instance with no `OLLAMA_CLOUD_API_KEY`, hex-packed bed filling does not exist at all.** It's the most valuable bulk operation in a garden planner, it has the most carefully reasoned geometry in the codebase (`hexCenters`, the half-spacing edge rule from #75), and it's gated behind an optional LLM that a self-hoster may well not configure.
## 2. The UI's "clear bed" fires N PATCHes and produces N undo steps
`web/src/lib/objects.ts:334-354`:
```ts
const results = await Promise.allSettled(
plops.map((p) => api.patch(`/plantings/${p.id}`, { removedAt: today, version: p.version })),
)
```
Every service mutation auto-scopes its own change set, so clearing a 40-plop bed writes **40 change sets**. Undoing it means pressing Undo 40 times.
This directly violates a stated rule in CLAUDE.md:
> Multi-row operations pass all their changes to a single `record` call so they undo as one unit.
`ClearObject` (`ops.go:331`) does exactly that — it snapshots the rows, clears them in one UPDATE, and records one change set. So the **agent's** clear-bed undoes in one click and the **UI's** clear-bed, for the identical user-facing action, does not. It's also N round trips and a partial-failure surface (`objects.ts:346-348`) that the service method doesn't have.
## Fix
`POST /objects/:id/fill` and `POST /objects/:id/clear`, thin handlers over the existing service methods. Point `useClearObject` at the new endpoint and delete the client-side loop.
Fill needs a region in the body — either a named compass region (`NamedRegion` already parses "ne", "south half", "all") or an explicit rect. The named form is the ergonomic one and is already written.
The UI affordance for region-fill is the larger half of the work and could be a follow-up: the endpoint alone fixes the agent-only gap and the clear-bed undo bug, and a "fill this bed with…" button in the inspector is a natural next step.
## Tests
Per CLAUDE.md, both new routes need API-level tests through the router — this is precisely the case the rule exists for. Worth asserting specifically that clear-bed produces **one** change set, since that's the behaviour being restored.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two problems with one fix.
1.
FillRegionhas no REST routeinternal/service/ops.goheader says these bulk ops live on*Serviceso that "agent tools and any future REST surface inherit the same ACL enforcement". The future REST surface never arrived:FillRegion(ops.go:108),FillNamedRegion(ops.go:314),ClearObject(ops.go:331)fill_region/clear_objectininternal/agent/tools.go:36,44objectsgroup ininternal/api/api.go:118-121has PATCH, DELETE, and POST-plantings. No fill, no clear.On an instance with no
OLLAMA_CLOUD_API_KEY, hex-packed bed filling does not exist at all. It's the most valuable bulk operation in a garden planner, it has the most carefully reasoned geometry in the codebase (hexCenters, the half-spacing edge rule from #75), and it's gated behind an optional LLM that a self-hoster may well not configure.2. The UI's "clear bed" fires N PATCHes and produces N undo steps
web/src/lib/objects.ts:334-354:Every service mutation auto-scopes its own change set, so clearing a 40-plop bed writes 40 change sets. Undoing it means pressing Undo 40 times.
This directly violates a stated rule in CLAUDE.md:
ClearObject(ops.go:331) does exactly that — it snapshots the rows, clears them in one UPDATE, and records one change set. So the agent's clear-bed undoes in one click and the UI's clear-bed, for the identical user-facing action, does not. It's also N round trips and a partial-failure surface (objects.ts:346-348) that the service method doesn't have.Fix
POST /objects/:id/fillandPOST /objects/:id/clear, thin handlers over the existing service methods. PointuseClearObjectat the new endpoint and delete the client-side loop.Fill needs a region in the body — either a named compass region (
NamedRegionalready parses "ne", "south half", "all") or an explicit rect. The named form is the ergonomic one and is already written.The UI affordance for region-fill is the larger half of the work and could be a follow-up: the endpoint alone fixes the agent-only gap and the clear-bed undo bug, and a "fill this bed with…" button in the inspector is a natural next step.
Tests
Per CLAUDE.md, both new routes need API-level tests through the router — this is precisely the case the rule exists for. Worth asserting specifically that clear-bed produces one change set, since that's the behaviour being restored.