Skip to content

Commit c68c670

Browse files
claude[bot]os-support-aiclaude
authored
feat(runtime): flow clone action — whole-definition copy under a new name (#12190)
`POST /api/v1/automation/:name/clone` (ADR-0126 §7.1): copy a flow's parsed definition to an ordinary org-authored sibling an admin can edit, for the case where the packaged original cannot be edited in place. The copy is whole-definition and never an enumerated facet list — #11703 measured an enumerated clone dropping three of six facets in silence, and a flow has far more facets than a permission set. Exactly `name`, `label` and `status` are mutated; the acceptance test asserts deep equality of the clone against its source minus those three fields, so a dropped facet fails a test rather than shipping. The new machine name is mandatory and a same-name clone is refused 409 `RESOURCE_CONFLICT`, naming the reason and the remedy: the engine keys flows by bare name, so a second definition under one name silently shadows the other and the survivor depends on registration order. No ancestry is recorded (amendment ruling 2, §9) — no provenance field on the definition, none on the response — and the source's ADR-0010 protection envelope is dropped rather than carried across, so the clone is org-owned and editable instead of a second copy of the package's locked artifact. References are not re-pointed; the response says so, along with the fact that `status: 'draft'` is a lifecycle label and not an off-switch. The route joins the `manage_metadata` authoring-write set — it registers flow metadata at environment scope, exactly as `POST /automation` does. Co-authored-by: Claude Code <support@objectstack.ai> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 428f9b2 commit c68c670

4 files changed

Lines changed: 924 additions & 0 deletions

File tree

.changeset/olive-crabs-clone.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
"@objectstack/runtime": minor
3+
---
4+
5+
Add `POST /api/v1/automation/:name/clone` — whole-definition flow clone (ADR-0126 §7.1)
6+
7+
An admin who cannot edit a packaged flow in place can now copy it to an ordinary
8+
org-authored sibling and edit that instead. `POST /automation/:name/clone` takes
9+
`{ name, label }` — both mandatory — and registers a copy of the source flow's
10+
parsed definition under the new machine name.
11+
12+
**The copy is whole-definition, never an enumerated facet list.** Every key the
13+
source definition carries comes across; exactly `name`, `label` and `status` are
14+
mutated. This is the shape ADR-0126 §7.1 rules for, and the reason is measured:
15+
a clone assembled from an enumerated facet list silently dropped three of six
16+
facets (#11703) — the record was created, the success toast fired, and the
17+
difference was discoverable only by diffing the two rows. A flow has far more
18+
facets than a permission set, so the acceptance test asserts deep equality of
19+
the cloned definition against its source minus those three fields; a dropped
20+
facet fails the test rather than shipping.
21+
22+
**The new machine name is mandatory and a same-name clone is refused** with a
23+
409 `RESOURCE_CONFLICT` naming both the reason and the remedy. Not because
24+
storage rejects it — storage legitimately holds both rows — but because the
25+
automation engine keys flows by bare name, so a second definition under one name
26+
silently shadows the other and which of the two dispatches depends on
27+
registration order.
28+
29+
**No ancestry is recorded.** Nothing tracks what a clone was copied from — no
30+
provenance field on the definition, none on the response (ADR-0126 amendment
31+
ruling 2, §9). The source's own ADR-0010 protection envelope (`_packageId`,
32+
`_provenance`, `_lock`, …) is dropped rather than carried across, so the clone is
33+
an org-owned flow the admin can actually edit rather than a second copy of the
34+
package's locked artifact.
35+
36+
**References are not re-pointed** — no reference index exists, so the clone calls
37+
exactly what the original called. The response says so, along with the fact that
38+
`status: 'draft'` is a lifecycle label and not an off-switch: the engine only
39+
disables on `obsolete`/`invalid`, so a cloned record-change or schedule flow is
40+
bound to its trigger and runs alongside the flow it was copied from.
41+
42+
The route joins the `manage_metadata` authoring-write set (#10145/#10243) — it
43+
registers flow metadata at environment scope, exactly as `POST /automation` does.

0 commit comments

Comments
 (0)