Repository navigation
Description
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
Filing gate: ④ child of epic #28236, filed on the maintainer's word in direct chat with the epic PM seat, 2026-11-03: 「发记录并立卡」 and 「相关卡 p1, 让车道处理」. ⛔ Not a claim.
QA-source: #29648 · automation.package-flow-override · off-clause
Part of #28236 · governs: ADR-0146 D2, D4, D9
Reader: domain:server (the clone door in packages/runtime).
Dedupe (open and closed): clone override, New similar, NOT_OVERRIDABLE clone. #28567 and #28149 built the door, and both predate a live override as the clone's source. New.
reach: any packaged flow customized in Studio. Studio and the API both meet it.
type: Bug
Business reason. An administrator who customized a flow and wants a second variant presses New similar and gets an English sentence about a declaration they never wrote. The only way forward in the product today is to delete their customization first.
What happens
Measured on main at 322cf9068f, twice in Studio and once over the API.
| source state | POST /api/v1/automation/showcase_budget_approval/clone |
|---|---|
| no override | 200, status obsolete, packageId com.example.showcase.custom |
| published override | 403 NOT_OVERRIDABLE: "'flow/showcase_budget_approval_copy' declares an override of 'com.example.showcase', which ships no flow named 'showcase_budget_approval_copy'. Remove overrides to keep it as 'com.example.showcase.custom''s own item, or correct the name (ADR-0146 D2)." |
The Studio dialog (auto-new-similar-dialog) shows that sentence and stays open. No flow is written.
Reproduce
- Publish an override of
showcase_budget_approval(Customize, edit a node label, Publish). POST /api/v1/automation/showcase_budget_approval/clonewith{name:'showcase_budget_approval_copy', label:'x'}. Expected: a switched-off copy in the pair (ADR-0146 D4). Actual: the 403 above.- Restore package original, and repeat step 2: 200.
Why (read from source, the dev confirms)
cloneFlowDefinition (packages/runtime/src/flow-clone.ts:255) copies the whole definition and removes only FLOW_CLONE_DROPPED_KEYS (:159), which do not include overrides. So the copy declares it overrides the base under a name the base does not ship, and the write gate is right to refuse that.
Wanted
- The copy never carries the source's
overridesblock: it is the pair's own item, under a new name (D2, D4). - It copies what the source serves: with a live override, the override's definition. The dev states this in the PR, and asks if the code says otherwise.
- A pin at the clone door with a live override as the source, and a clause added to
automation.package-flow-override, whose steps today clone only after the restore, so they cannot see this.
Activity
objectstack-fleet commentedon Nov 3, 2026
domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4
Claim: PM loop round 1 · 2026-11-03T00:26Z
Session:
session_01JQwxbZUR3xvHyDPCjNL5P4(the seat); the dev is the card threadsession_01JQwxbYmYA3GtqMUGHGQkR8Account:
os-warrenBranch:
claude/project-thread-yzu3d4(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own
Domain:
domain:server(sub-issue of epic #28236, domain inherited; handed to the lane by the epic PM's filing line)Seat:
domain:server#5Ruling-ref: none on the card. Governing: ADR-0146 D2 and D4.
File surface:
packages/runtime/src/flow-clone.ts(FLOW_CLONE_DROPPED_KEYS,cloneFlowDefinition) and the clone door's tests; the dogfood/QA caseautomation.package-flow-override(a clause that clones while the override is live); one.changeset/29650-*.md. ⛔ Nopackages/spec/**; ⛔ nopackages/metadata-protocol/src/protocol.tsunless measured necessary (then stop and report).Container & model:
M,mode:thread,model: default(--tier: no path mandate),effort: high(p1)Clause-②: no
Metadata-protocol: none
Responsibility: the code is the flow clone door's copy · New similar on a customized packaged flow answers 403 and writes nothing · every administrator who customized a packaged flow in Studio, or the API
Thread-read: none
Premise: holds (read on
origin/mainb391feb1:FLOW_CLONE_DROPPED_KEYSdoes not nameoverrides; the dev reproduces red first)Serial constraints cleared: no other in-flight claim on
packages/runtime/src/flow-clone.ts(seat 4's #29649 is indomains/packages.ts).objectstack-fleet commentedon Nov 3, 2026
domain:server#5 · os-warren · session_01JQwxbYmYA3GtqMUGHGQkR8
os-dev-report
{ "issue": 29650, "status": "done", "branch": "claude/project-thread-yzu3d4", "pr": "https://github.com/objectstack-ai/objectstack/pull/29689", "session": "session_01JQwxbYmYA3GtqMUGHGQkR8", "premise_still_valid": true, "summary": "Premise held: cloneFlowDefinition dropped only the read decorations, the protection envelope and protection, so with a live override the copy kept the source's overrides block under its new name and the save gate answered 403 NOT_OVERRIDABLE. FLOW_CLONE_DROPPED_KEYS now also derives Object.keys(OverrideDeclarationFields) from @objectstack/spec/kernel, so the copy is the pair's own item. The door's source is automationService.getFlow(name), the engine map entry, which with a published override is the override's body (ADR-0146 D9 item 1), so the copy carries the override's definition; the PR states this and asks a reviewer to say if they read it differently. QA case automation.package-flow-override goes to revision 3 with step 10 and acceptance[6] (clone while the override is live), appended so existing indices do not move; one runtime patch changeset.", "tests": "Red first at db4ce28f (pin only): new describe in packages/runtime/src/domains/automation-flow-clone-custom-package.test.ts (real better-sqlite3 + ObjectQL + ObjectStackProtocolImplementation; override saved into the pair declaring overrides, armed in the engine map; POST /automation/:name/clone through HttpDispatcher) -> 2 failed / 17 passed, AssertionError 403 NOT_OVERRIDABLE with the card's sentence; the no-override control cases in the same file stayed 200. After the fix: both clone suites Test Files 2 passed, Tests 38 passed. Ablation via scripts/ablation-replace.mjs --delete of the OverrideDeclarationFields spread (anchor 1 to 0, blob 9864c4f16874 to 60975c62ef63): 2 failed / 17 passed; restored, blob == HEAD and git diff HEAD empty. The suite imports flow-clone.ts from src, so there is no dist leg. Runtime pnpm test at head 14f1ec09: Test Files 529 passed, Tests 8514 passed, 19 skipped; typecheck exit 0.", "gates": "Local at 14f1ec09: dispatch-gates --commands derived 63 families; 62 exit 0. check-plugin-teardown-shape --self-test exited 3 on the shallow clone, then 0 after git fetch --unshallow origin main. NOT MEASURED: pnpm check:dual-build-cjs-loads, PREREQUISITE NOT MET (reads every package's dist; only the runtime closure was built), left to CI. --ran reconciliation: 63 derived, 62 run, 1 NOT-MEASURED, 0 UNRUN. Outside the derivation: runtime test exit 0, runtime typecheck exit 0, pnpm lint exit 0. CI check-runs: in_progress when this was written, not awaited.", "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2: POST pulls via the fleet-write relay pr_create (#29689, 6814 bytes sent and stored, identical); this os-dev-report comment via post-stamped.mjs. git push is not a REST write.", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [] }Clause-②: no and Metadata-protocol: none, confirmed from the diff (no packages/spec, no packages/metadata-protocol). No packages/metadata-protocol/src/protocol.ts change was needed. Report written after local verification, before CI converged.
Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4
Landed · 2026-11-03T02:14Z
c9636f1e50, through the merge queueFixes #29650Seat records: claim 6592058258, ACCEPT 6593411405 on head
14f1ec09(TIER_DEFAULT, not governed).pm:dispatchedcomes off in the same round. Follow-ups: none.