Adds POST /gardens/:id/copy: duplicates a garden the actor owns, along
with its objects and their currently-planted plops, into a new garden
owned by the actor. A blank name derives "<source> (copy)".
Deliberately not carried over:
* public_token — the share link is a capability granted to the
original; a copy must not silently inherit a live public URL
* garden_shares — a copy is private to whoever made it
* removed plantings — the copy is a fresh layout, not a history
Owner-only for now: a copy's plops keep pointing at the SOURCE's
plant_ids, so letting a viewer copy someone else's garden would hand
them plants outside their catalog and pin the original owner's plants
against deletion (plantings.plant_id is ON DELETE RESTRICT). Copying a
shared garden needs a plant-cloning policy first.
The whole copy runs in one transaction, so a failure part-way leaves no
half-populated garden. The object/planting list scans are factored into
queryObjects/queryPlantings so the same code serves a plain read and a
read inside that transaction.
UI: a Copy action on the owner's garden card opens a modal prefilled
with the derived name, then lands in the new garden.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Capture the v1 architecture: domain model, geometry, SVG editor with
semantic zoom, REST API with version-guarded sync, service-layer seam
for future majordomo/executus agent tools, OIDC-first auth (Authentik),
and the phased roadmap.
Co-Authored-By: Claude Fable 5 <[email protected]>