Description
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
Filing gate: ④ child of epic #28236 — re-scoped from ADR-0146 child 2c on the maintainer's rulings of 2026-10-30, verbatim: 「方向定为包级覆盖:代码和 Studio 用同一机制,定制都进 .custom。」 · 「等我拍板的 8 个问题,全部同意」 (Q6: clone is kept only as "new similar", landing in <base>.custom). The original body (2c, optional packageId, <base>.customize) is in this card's edit history. ⛔ Not a claim.
Blocked-by: #28407
Part of #28236 (P2, child N-21) · governs: ADR-0146 as rewritten by N-01, ADR-0070 D5/D6 · lane: domain:server · size: S–M.
Why it waits. The door's default target is the paired <base>.custom package, created on demand by the .custom ensure door (epic child N-13, #28407 — itself waiting on N-08 #28404 for the pair constants). ⛔ A dev dispatched while no ensure door exists reports premise_still_valid: false instead of building a second creation path.
Business reason. Cloning a packaged flow today writes a copy that belongs to no package — the scope ADR-0070 D5/D6 retired — and, because the original keeps running, a record change fires both (measured 2026-10-30 on showcase: showcase_urgent_task_alert and its _copy both ran). The customer's work sits outside every package Studio can open and outside every export. Under the ruled direction, a "new flow like this one" is a new item of the paired .custom package.
Measured (d6c0a75f1a).
packages/runtime/src/flow-clone.tsstrips the ADR-0010 envelope (_packageId,_packageVersion,_provenance: 'package',_lock) and sets no package, so the copy is package-less.POST /automation/<name>/clonetakes a mandatory new name and nothing else;PackagedAutomationPage.tsxis its only caller.
Wanted.
- The door takes a target package. Given: it must be an installed, writable package inside whose dependency closure the flow's objects lie (else a worded refusal in the
DUPLICATE_SOURCE_NOT_A_BASEfamily). Omitted: the target is the source package's<base>.custom, ensured through N-13's door. - ⛔ No package-less clone after this card: the omission no longer writes a package-less row.
- The copy is written inactive (definition status draft), so a clone never double-runs beside its original; the response says so in the server's words.
- Contract: request schema in
packages/spec(Zod first), the route ledger'srequestSchema, the SDK client and the OpenAPI output regenerated;Clause-②/Metadata-protocollines filled honestly on the PR. - Tests: default lands in
<base>.customwith no base envelope; explicit writable target lands there; read-only, unknown and out-of-closure targets refused; the copy is inactive.
Not here. Creating .custom (N-13); the Studio "new similar" button (N-22); overriding a flow in place (N-11 / N-18); clone lineage (ADR-0126 §9 second bullet).
Dedupe. objectui#13270 (clone result offers, console side); objectui#11553 (package-less scope). No other card changes the clone door's target.
Links back: #28236, #28130 (superseded), ADR-0146, ADR-0070 D3/D5/D6, ADR-0010.
Generated by Claude Code
Activity
<base>.customizepackage, the metadata catalog moves into Studio #28130objectstack-fleet commentedon Oct 30, 2026
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
Recorded 2026-10-30T15:21Z (desktop session
040dfd80-c4c9-411b-895c-698e785a6848).Label transition:
pm:queue→pm:epic(one replace write, read back:enhancement,priority:p2,pm:epic,area:studio,domain:server); linked as a sub-issue of #28130.Instruction — the maintainer, in a direct desktop session, verbatim: 「相关的issue 你都应该标记 epic,避免被车队认领」. The card is reserved to the epic subtree; no domain seat takes it.
Why it waits: in the same session the maintainer ruled the package-level override direction — 「方向定为包级覆盖:代码和 Studio 用同一机制,定制都进 .custom。」 — and, on the follow-up questions, 「等我拍板的 8 个问题,全部同意」, which includes keeping clone only as a "new flow like this one" that lands in the paired
<base>.custompackage. This card is re-scoped (or replaced) in the epic's re-plan; nothing is claimed until then.The label write and the sub-issue link above were made with the maintainer's own account (
hotlong) from the desktop session before this seat switched to the fleet write tools; this record is the provenance the transition owes.<base>.customizepackage, the metadata catalog moves into Studio #28130[-]Flow clone door takes a target `packageId` (a writable package) so a clone of a packaged flow no longer lands in the retired package-less scope (ADR-0146 D3, ADR-0070 D5; #28130 child 2c)[/-][+]Flow clone door → "new similar": the copy lands in a writable target package, by default the paired <base>.custom, never package-less[/+]objectstack-fleet commentedon Oct 30, 2026
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
Recorded 2026-10-30T18:38Z (desktop session
040dfd80-c4c9-411b-895c-698e785a6848).Re-scoped as N-21 and parked behind the ADR rewrite.
<base>.customizepackage, the metadata catalog moves into Studio #28130 (closed as superseded) to the new epic [Epic] Package-level override: every customization of an installed package lives in its paired <base>.custom package, one declared override shared by code and Studio (supersedes #28130) #28236 as its child N-21; title and body rewritten for the ruled direction (clone kept only as "new similar", landing in the paired<base>.custom, never package-less). The original 2c body is in the edit history.pm:epic→pm:blocked(one replace write, read back), withBlocked-by: #28237(the ADR-0146 rewrite) in the body. When the.customensure door (epic child N-13) is cut after docs(adr): rewrite ADR-0146 for package-level override — customizations live in <base>.custom through a declared override (folds #28145) #28237 merges,Blocked-by:moves to that card.11 remaining items
objectstack-fleet commentedon Nov 1, 2026
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
2026-11-01T06:08Z
Notify: rider · #28149 (N-21) · Setup's "Open the clone in Studio" link
Found by the P2 drafting review (epic #28236).
ui/packages/app-shell/src/views/setup/PackagedAutomationPage.tsx:573–578draws "Open the clone in Studio" withstudioOrgScopePath({ type: 'flow', name }), which is the package-less scope. After this card the copy lives in the target package (Wanted 1–2), so that link lands nowhere.Asked of this card, in the same PR: the clone response names the package the copy landed in, and that one link reads it. The link opens the copy in Studio's automations surface of that package, in the shape the dev settles. A release cut right after this card must not ship a dead link. N-22 (#28567, Studio "New similar") only checks the link and re-points it only if this PR did not. This is a rider on the card's own scope (its console surface), not a new card; it is one
ui/line declared beside the card'sdomain:serverlane.objectstack-fleet commentedon Nov 1, 2026
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
2026-11-01T06:51Z
Unblocked · p1 ·
pm:queue(epic stall patrol)⛔ Not a claim.
Blocked-by: #28407(N-13, the.customensure door). metadata-protocol: the <base>.custom ensure door — the one idempotent creation path for the paired package, explicit <base namespace>_custom, walled 403 #28407 is closed as completed (closed 2026-11-01T06:07Z).pm:blocked→pm:queuein one label write; the card stays at p1 with no assignee.The dev builds on N-13's door on
mainas the only way to create<base>.custom; ⛔ no second creation path.Also on this card: rider 6554259755 (Setup's "Open the clone in Studio" link is fixed in this PR).
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo
Claim: PM loop round 4 · card thread · 2026-11-01T07:21Z
Session:
session_0186vfjHHMsRXC9EUT9hTFMo(the seat); the card thread issession_01FEkQ5pRVPsxnFxPBAQLUMhAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-sdqbrt(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#2Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. Also the maintainer, 2026-11-01T06:40Z: 「任务很多啊 is:issue state:open label:pm:queue label:domain:server」. Epic #28236 direction, the maintainer 2026-10-30T15:21Z (quoted on this card): 「方向定为包级覆盖:代码和 Studio 用同一机制,定制都进 .custom。」 · 「等我拍板的 8 个问题,全部同意」 (Q6: clone kept only as "new similar", landing in
<base>.custom). Unblock6554634959: the dev builds on N-13's door onmainas the only way to create<base>.custom; ⛔ no second creation path. Rider6554259755rides in the same PR.File surface:
packages/runtime/src/flow-clone.tsand thePOST /automation/<name>/clonehandler and route-ledger entry; a request schema inpackages/spec; the SDK client method inpackages/client; regenerated OpenAPI; the rider's link inui/packages/app-shell/src/views/setup/PackagedAutomationPage.tsx; tests and changesets. Calls N-13'sensureCustomizationPackage(packages/metadata-protocol) without re-implementing it.Container & model:
M,mode:thread,model: opus(--tiernames no path mandate: PM's call, default),effort: highClause-②: yes (widening — the clone door gains an optional target
packageand its response names the landed package; the default target moves from package-less to<base>.customand bad targets are refused, so the dev declares a narrowing arm too ifCLAUSE2_ARMSreads it that way; a contract review is owed before the queue, atCONTRACT_REVIEW_TIERsince a spec schema moves)Metadata-protocol: none (expected; the dev measures and declares)
Responsibility: the code is the flow clone door · the platform path is ADR-0146 D3's paired
.custompackage via N-13's ensure door, under ADR-0070 D5/D6 (no package-less scope) · reached by an administrator cloning a packaged flow from Setup's packaged automation pageThread-read: none
Premise: holds (read 2026-11-01T07:21Z at
origin/main:flow-clone.tsstrips the ADR-0010 envelope and sets no package; N-13's ensure door is onmainsince #28407 closed)Serial constraints cleared: #28407 (N-13) closed as completed; open PR #28554 (#28410) edits
packages/metadata-protocol/src/protocol.tsandprotocol-packaged-base-refusal.ts, so the dev reads its diff first, does not edit its hunks, and mergesorigin/mainonce if it lands firstResume-from: none — fresh from
origin/mainState: →
pm:dispatched, assigned toos-project-manager, recorded by this claim.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo
os-dev-report
{
"issue": 28149,
"status": "done",
"branch": "claude/project-thread-sdqbrt",
"pr": "#28621",
"session": "session_01FEkQ5pRVPsxnFxPBAQLUMh",
"premise_still_valid": true,
"summary": "POST /automation/:name/clone is now ADR-0146 D4's "new similar". The copy lands in a writable package: by default the source's paired .custom, ensured in process through N-13's ensureCustomizationPackage (no second creation path), or the package named by the new optional
packagemember. It is written switched off (status 'obsolete', because 'draft' arms like 'active' in the engine), with a first-write pin so an existing row is never overwritten, and the answer is { flow, packageId, notice }. The target rule (installed 422 / writable 422 WRITABLE_PACKAGE_REQUIRED / dependency closure 409 RESOURCE_CONFLICT) lives in runtime domains/new-similar-target.ts, shared with #28563. Also in this PR: CloneFlowRequestSchema/CloneFlowResponseSchema in spec, client.automation.clone, the route-ledger row moved to sdk, the Setup 'Open the clone in Studio' link reading data.packageId (rider 6554259755), and the metadata-lifecycle docs row.","tests": "Head 71dca74 (main merged at de3fafc, regenerated from the merged tree). New runtime/src/domains/automation-flow-clone-custom-package.test.ts runs on the real protocol + ObjectQL + sqlite sys_metadata; red-first on base sources gave 11 red / 2 green (packageId undefined, 'draft' not 'obsolete',
packagerefused unknown_field, a local clone 200 package-less). At 8a0ba37, under os-verify-lock: runtimevitest run --project local --project repo: Test Files 506 passed (506), Tests 9121 passed | 19 skipped, VERDICT command-exit 0. Client: tsc -p tsconfig.test.json clean; Test Files 53 passed (53), Tests 690 passed. ui: PackagedAutomationPage.test.tsx 32/32. app-shell type-check cannot resolve sibling @object-ui/* in this container (no ui build), so it is left to CI Type Check UI; no error names a touched file. Ablation via scripts/ablation-replace.mjs (committed first, restore verified against the HEAD blob): ensure call ->target = owner;gives 5 red; FLOW_CLONE_STATUS 'obsolete' -> 'draft' gives 4 red. Scoped eslint over the touched files: exit 0, 0 errors (one existing react-hooks/set-state-in-effect warning on PackagedAutomationPage.tsx:333, not a changed line).","gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 8a0ba37: 140 gates, each run with its exit code captured before any pipe; all 140 exit 0.
--ranreconciliation: '140 derived famil(ies) accounted for — 140 run, 0 NOT-MEASURED'. The first pass at c3369fd found real reds, all fixed: api-surface, export-origins, strictness-ledger and check:docs stale (regenerated); spec-parsed-alias (added CloneFlowRequestParsed); test-source-alias (aliased /^@objectstack\/lint\/runtime$/ in runtime vitest.config.ts); doc-authoring (dropped an issue id from the route-ledger note string); route-envelope (new-similar-target.ts declared handBuilt 0, byte-identical to #28563's lines). The last commit, 71dca74, touches only two dogfood test files. Declared narrowing: at that head I re-ran only the 15 test-reading gates (cross-package-test-inputs, query-options-erasure, objectql-double-limit, test-source-alias, where-matcher, doc-authoring, issue-citations, nul-bytes, engine-double-contract, registry-log-declared, rest-log-spy-declared, refd-timer-probe, closed-ceremony-text, role-word, org-identifier), all exit 0. PR check-runs were not read yet: CI was still starting at report time.","dogfood_battery": "8a0ba3770f ·
pnpm --filter @objectstack/dogfood testafterpnpm --filter '@objectstack/dogfood^...' build, under the lock · VERDICT command-exit 1 · Test Files 3 failed | 362 passed | 3 skipped (368) · Tests 3 failed | 2936 passed | 28 skipped (2967). Failures: (1) flow-credential-channel 6: the env-flow control clone answered 400, the intended narrowing; fixed in 71dca74 to create a writable package and name it. (2) flow-provenance-server-held 'a clone of a shipped flow…': the copy now lands in com.example.showcase.custom and its meta read carries the pair's _packageId (the measured META_BOUND shape); fixed in 71dca74 to assert the pair, and that the round-trip PUT saves 200. Re-run of those two files plus automation-flow-clone-door at 71dca74: Test Files 3 passed (3), Tests 29 passed, VERDICT command-exit 0. (3) route-ledger-live-mount-parity: 'GET /packages/:id/customizations — LEDGERED BUT NOT MOUNTED'. That is main's, from #28595 (handler in domains/packages.ts and the ledger row, but no mountPackagesRoute in dispatcher-plugin.ts). Not caused by this diff and not fixed here; see out_of_scope_findings.","mcp_calls": "3: mcp__github__list_pull_requests ×1, mcp__github__pull_request_read (get_files) ×2. All reads, no write tool.",
"api_writes": "Write budget spent: 2 git push (the PR head was conflicted after #28595 landed, so main was merged and pushed again), 1 relay pr_create (fleet-write run 41290018382 success, read-back 8531/8531 bytes identical, assignees none), and this report comment through post-stamped. No REST proxy writes; 1
gh apiGET (claim comment 6554908823).","open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door + wrong answer — GET /api/v1/packages/:id/customizations answers 404 on a live stack · evidence: dogfood route-ledger-live-mount-parity at 8a0ba37, 'LEDGERED BUT NOT MOUNTED'. #28595 added the domain arm (packages.ts) and the route-ledger row, but packages/runtime/src/dispatcher-plugin.ts has no mountPackagesRoute('get', '/:id/customizations', …) beside '/:id/ensure-customization' (line ~1613). Dogfood skips PR CI, so main is red on that pin · dedupe words: customizations route not mounted, mountPackagesRoute, #28411",
"carrier: seat 2 (hand-back message) · noted, not filed"
]
}
Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo
Landing record · #28149 · PR #28621
domain:serverseat 2 ·session_0186vfjHHMsRXC9EUT9hTFMo· 2026-11-01T11:37Zf23e0512git merge-base --is-ancestor f23e0512 origin/maingit ls-remote origin refs/heads/gh-readonly-queue/*matching 28621git grep -c CloneFlowRequestSchema origin/main -- packages/spec/src/api/automation-api.zod.tsstatus: 'obsolete') in a writable package, by default the source base's paired.customvia the ensure door; an explicitpackageis accepted under the shared target rule (domains/new-similar-target.ts); a source no read-only package ships, sent withoutpackage, is refused 400.CloneFlowRequestSchema/CloneFlowResponseSchemaare new in@objectstack/spec/api(Clause-② yes (narrowing), Metadata-protocol added, contract review PASS on the landed head).Release: claim
6554908823(session_01FEkQ5pRVPsxnFxPBAQLUMh, branchclaude/project-thread-sdqbrt) · landed.pm:dispatchedremoved; assignee cleared; the card closed on merge.Generated by Claude Code