Repository navigation
[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
Description
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
Filing gate: ④ coordination parent, opened on the maintainer's direct instruction (③) in a desktop session on 2026-10-30, verbatim: 「方向定为包级覆盖:代码和 Studio 用同一机制,定制都进 .custom。」 · 「现在用fable规划新的epic 任务清单。原来的你可以考虑直接关闭」 · 「等我拍板的 8 个问题,全部同意」. Supersedes #28130.
Epic (coordination parent) · no pm:epic on this parent or any child — the children are fleet work: each carries pm:queue (or its own state) and its domain:* lane, and the domain seats claim them under the normal protocol. Maintainer, 2026-10-31, verbatim: 「这些卡都可以放回车队开发吧」 · 「注意不能写epic车队才会认领」. Coordinating session: 040dfd80-c4c9-411b-895c-698e785a6848 (desktop, hotlong) · ⛔ not a claim on any child.
Territory (declared): docs/adr/0146-*.md (rewritten in place) and the status lines of ADR-0005/0009/0048/0126/0131/0141 · packages/spec (override declaration, .custom pairing constants, field-patch whitelist) · packages/metadata-protocol/src/{protocol-environment-rows.ts,protocol-packaged-base-refusal.ts,protocol-authoring-gate.ts,protocol.ts} (read/write doors) · packages/runtime/src/domains/{packages.ts,automation.ts}, packages/runtime/src/flow-clone.ts · packages/services/service-automation/src/{flow-precedence.ts,run-resume.ts,engine.ts} · packages/objectql/src/registry.ts (extension merge) · ui/packages/app-shell/src/views/{studio-design,metadata-admin,setup}/**.
维护者速读
- 改了什么: 已安装软件包(如 HotCRM)的一切定制——改原有的、加新的、扩展对象——都进配对的可写包
<base>.custom;「改原有的」= 在.custom的同名项正文里显式声明覆盖,整份替换。写代码和 Studio 用同一机制,定制随包导出、进 git、跨环境部署。 - 为什么改: 10-30 浏览器走查:Studio 改不了只读包;Setup 写的是不随包走的「无包覆盖行」;克隆即双跑;依赖包的同名视图会静默盖掉基础包(Studio 看 6 列、用户看 2 列)。ADR-0146 还没开工,直接改写。
- 风险与回滚: 需推翻 ADR-0126 D1/D4 墙 1、ADR-0131 D6、ADR-0005 五型、ADR-0048 §3.3、ADR-0009 未兑现的版本固定、ADR-0141 D3 的卸载保护推迟——都在 ADR-0146 改写里逐条写明。每张子卡独立 PR,可单独回退。
- 先看: N-01(ADR-0146 改写);P0 六张现在可并行,全部进车队(
pm:queue,各车道认领;整棵子树不挂pm:epic)。 - 两分钟验证: HotCRM 视图「定制」→ 三处读取同一列集、导出带声明、「恢复包原版」回原样;流程定制只跑新定义、挂起运行按旧版完成;对象加字段落
.custom、名为xxx__c;卸载 HotCRM 被拒并点名hotcrm.custom。 - 需要你做的: 审 N-01 的 ADR PR 并亲手合并。
Rulings this epic carries (maintainer, 2026-10-30, verbatim)
- D-1 Direction — 「方向定为包级覆盖:代码和 Studio 用同一机制,定制都进 .custom。」
- D-2 Suffix
.custom— 「同意 后缀改成 .custom」 - D-3 Auto-create, no prompt, dependency on the base declared (from the walkthrough discussion; the ADR records it).
- D-4 Studio customization waits for the override core — 「我建议先等包级覆盖开发完,再考虑这个界面的修改」
- D-5 Item toolbar in Studio, before customization — 「同意新增 2h「Studio 项工具栏」并放在 2a 之前」
- D-7 Setup's item tools belong to Studio — 「setup 右侧这套界面,包括看版本历史的,本身就应该属于 studio」
- Q1–Q8 — 「等我拍板的 8 个问题,全部同意」: undeclared same-name refused at publish · declaration in the item body · real version pinning for flows · explicit
<base_ns>_customnamespace, never derived · package-less overlay writes closed after migration · clone kept only as "new similar" into.custom· base-field changes limited to a display-property whitelist · Packaged automation retired after the ledger switch and the landing counts. - ADR — 「adr 146还没开始开发,可以直接改」 (ADR-0146 is rewritten in place; PR docs(adr): ADR-0146 — anchor the four load-bearing symbols, count two Setup pages, drop the epic label snapshot (S13–S15) #28145 folds into N-01).
- Extension fields — 「扩展字段用 __c 后缀,只有 .custom 包能用。」
- Fleet — 「这些卡都可以放回车队开发吧」 · 「注意不能写epic车队才会认领」 (2026-10-31): every child is dispatched by its domain lane; nothing in this subtree carries
pm:epic. - Ordering — 「四个小问题 同意」: flow customization waits for real version pinning (no interim publish gate); the action ledger switch stays out of this epic (meta: GET /meta/object serves which packaged actions are switched off beside the item, and the disabled-action refusal reads as a plain sentence #27842/Setup › Packaged automation: a packaged action switched off keeps its button on the record page, and clicking it shows a toast naming sys_metadata_activation and ADR-0126 #27504); no new
area:label; docs(adr): ADR-0146 — anchor the four load-bearing symbols, count two Setup pages, drop the epic label snapshot (S13–S15) #28145 folds into N-01.
P0 — start now (cut with this epic)
| # | Card | Lane | Size | Depends on |
|---|---|---|---|---|
| N-01 | Rewrite ADR-0146 in place for package-level override (+ status-line pointers on ADR-0005/0009/0048/0126/0131/0141; folds PR #28145) | domain:skills |
M | — |
| N-03 | Defect: a same-named item in a dependent package replaces the base's item for package-less reads and in the base app | domain:server |
M | — |
| N-04 | Studio: ledger switch "off for this deployment" on a read-only package's flow — #28148 re-scoped | domain:ui |
S–M | — |
| N-05 | Studio: one item toolbar (layers, references, history with rollback, audit, source) on every pillar item | domain:ui |
L | — |
| N-07 | Studio: object pickers offer only objects inside the package's dependency closure; the dependencies editor is reachable | domain:ui |
S | — |
| N-27 | Setup: position holders and permission-set assignment as people-and-organization pages | domain:ui |
M | — |
Cut after N-01 merges (plan of record, not cards yet)
Each is filed into its domain lane's pm:queue when cut, with Blocked-by: where the order below says so.
- P1 override core (filed 2026-10-31; ready now: N-06, N-08, N-12): N-06 Studio: publishing takes a change note and item history names the person — the note reaches publish-drafts, the actor is the session user, and it is shown by name, not id #28406 change note + actor name · N-08 spec: the override declaration, the
.custompairing constants, the<base_ns>_customnamespace rule, the__creservation, the overridable-type list and the base-field display whitelist #28404 spec: override declaration,.customconstants, namespace rule,__creservation · N-09 metadata-protocol: the override write gate — only the base's paired.custommay declare; undeclared same names and a dependent'soverridesare refused at publish,os validateand install #28410 write gate — only.customdeclares (docs(adr): ADR-0146 amendment — only the paired<base>.customdeclares an override; a dependent package adds under its own names (ruled A, withdraws rider 3) #28390 ruling A), refuse undeclared / out-of-closure / excluded type / a dependent'soverrides, at publish,os validateand install · N-10 metadata-protocol: one effective read — package-less, base-named, layered, the env-wide list and export serve the.customdeclared override, and a withdrawn public form stays withdrawn #28409 one effective read across package-less, base-named, layered, list and export; a withdrawn public form stays withdrawn (metadata-protocol: a same-named item in a dependent package replaces the base package's item for package-less reads and in the base app #28238 list half) · N-11 service-automation: a declared override in <base>.custom is the packaged flow — armed over the code package, rebound in one reload, one ledger switch #28412 flow override · N-12 service-automation: a suspended run resumes on the flow version it started on, not on the definition registered under its name #28405 real flow version pinning · N-13 metadata-protocol: the <base>.custom ensure door — the one idempotent creation path for the paired package, explicit <base namespace>_custom, walled 403 #28407.customensure door · N-14 objectql: uninstalling a base package is refused while its <base>.custom exists, naming the pair (409, the extender-refusal family) #28408 uninstall protection for the pair · N-15 runtime: per-package customization read — for an installed base, its.customoverrides, its object extensions and its switched-off items, served rows only, with counts #28411 per-package customization read · N-16 cli + metadata-protocol:os migratemoves package-less overlay rows into <base>.custom as declared overrides — the migration ceremony only (the close is N-16b) #28413 migrate package-less overlay rows, then close that write. - P2 Studio customization (filed 2026-11-01 on the maintainer's 「四项提速措施全部按推荐执行」, schedule and cards reviewed by Fable; ready now: N-23a, N-26a): N-16b metadata-protocol: close the package-less write onto a packaged item (PUT/DELETE, rollback, source save, reset) naming <base>.custom; Studio drops its overlay link #28576 close the package-less write + remove the overlay link (split from N-16 cli + metadata-protocol:
os migratemoves package-less overlay rows into <base>.custom as declared overrides — the migration ceremony only (the close is N-16b) #28413, which is now the migration ceremony only, so no release lands between a closed write and Studio's replacement; ← N-16a, N-17, N-25) · N-17 Studio: customize a read-only package's view, dashboard or report into <base>.custom — a declared override, its badge, and "Restore package original" (replaces #28147) #28562 customize view / dashboard / report (replaces Studio: edit a read-only package's views, dashboards and reports in place — overlay draft, "customized in this environment" chip, Reset to package default (ADR-0146 D2; #28130 child 2a) #28147) · N-18 Studio: customize a read-only package's flow into <base>.custom under its own name — one ledger switch, the declaration's badge, "Restore package original" #28570 customize a flow · N-19 Studio: a base and its <base>.custom as one surface — "customized" / "added" marks, "only customizations", the pair folded under its base, New lands in the pair #28572 base +.customas one surface · N-20a runtime: the permission-set clone door lands "new similar" in a writable target package, by default the source package's <base>.custom, never package-less #28563 permission-set "new similar" door →.custom· N-20b Studio: "New similar" on a base permission set — the copy lands where the clone door puts it (<base>.custom) and opens in Studio; Setup's clone button reads the same #28573 its Studio UI · N-21 Flow clone door → "new similar": the copy lands in a writable target package, by default the paired <base>.custom, never package-less #28149 clone door → "new similar" · N-22 Studio: "New similar" on a flow over the clone door — the copy lands inactive in <base>.custom or the open writable package; Setup's clone link follows it #28567 Studio "new similar" on a flow · N-23a spec: a per-field display patch slot on ObjectExtensionSchema, derived from BASE_FIELD_DISPLAY_WHITELIST; structural keys refused at parse #28560 spec: per-field display patch slot · N-23b metadata-protocol + cli + install-local:__cextension fields only in a.custompackage — refused at save/publish/restore,os validate, both install doors and duplicate #28564__cjudge at every door (incl. marketplace install) · N-23d objectql: an object extension's display patch is carried and applied by mergeObjectDefinitions; a patch naming a missing field or option is refused at install and validate #28565 patch merge into the base object · N-23e metadata-protocol + objectql: an object extension is an authorable item of a writable package — saved and published through the existing doors, registered as 'extend', exported as objectExtensions #28574 object-extension write path into.custom· N-23c Studio: add a field to a read-only base object — written as an extension in <base>.custom named xxx__c, base fields editable only within the display-property whitelist #28575 Studio add-field on a base object · N-24 Studio: the landing card of each installed base says "n customized, m switched off", read from the per-package customization read #28566 landing counts · N-25 Studio: email template and translation editors in the All metadata area — a read-only base's template or bundle is customized as an override in <base>.custom #28569 email template / translation editors into Studio. - P3: N-26a Studio: an All metadata area in the builder — directory, lists, generic item page, history and diagnostics, per package and across packages; pillar types open their pillar #28561 All metadata area in Studio (browse half, ready now) · N-26b Studio: All metadata lists show a base's .custom items marked "customized" or "added", with an "only customizations" filter and per-type counts #28568 its "customized / added" marks · N-28 Setup: retire Setup → Metadata behind redirects into Studio (datasource kept) and retire Packaged automation's flows half; Studio stops linking into Setup #28578 Setup → Metadata redirects, Packaged automation's flows half retired (← N-17, N-20b, N-22, N-24, N-25, N-26a; item 2 waits on decision M-1 [Decision] Setup → Metadata retirement: where the on/off switch of positions, permission sets and sharing rules lives — on the item in Studio (A) or kept in Setup (B) #28559) · N-29 QA checklist: rewrite the Setup-designer items to the Studio door and add the .custom customization journey, with one live e2e through Studio #28579 QA checklist + live e2e through the Studio door · N-30 skills/docs: the ADR-0126 §1.3 promise pages and the skills teach what ADR-0146 D2 delivers — customizations in <base>.custom,
__cfields, flow override and "new similar", no package-less overlay #28577 skills and docs (after skills/docs: teach the one effective read of a.customdeclared override (N-10's owed Skills-docs follow-up; the first slice of N-30) #28509). - Release safety (schedule review): do not cut a release after N-13 metadata-protocol: the <base>.custom ensure door — the one idempotent creation path for the paired package, explicit <base namespace>_custom, walled 403 #28407 merges and before install-local:
DELETE /api/v1/marketplace/install-local/:manifestIdremoves the ledger entry before asking the uninstall refusal, so a refused base (its<base>.custominstalled, or an ADR-0029 extender) answers 200 and is left half-removed #28511 merges; N-16b's changeset is major and its release note tells operators to run N-16a'sos migratefirst.
Old cards
#28130 closed as superseded by this epic · #28147 closed, replaced by N-17 (WIP claude/project-thread-3c7skj@cd0ae0d156 kept as reference) · #28148 re-scoped as N-04 and re-parented here — its claim (os-tesla, pm:dispatched) stands and the pause is lifted · #28149 re-scoped as N-21 and re-parented here, pm:blocked (its door needs N-13, which is cut after N-01) · PR #28145 folds into N-01.
Not in this epic
Per-organization overrides (ADR-0131 D6 environment scope stands) · upgrade diff / basedOn checking (recorded only) · clone lineage (ADR-0126 §9 second bullet) · overriding permission sets, positions, capabilities · code-only types (hook, job, field, …) · structural changes to base fields · the action ledger switch (#27842, #27504).
Dedupe
Searched open and closed issues: "package-level override" (4 hits, none on this topic; #28149 is a child here), customize installed package override (12; #28149, #27356, #11665 related), "item toolbar" Studio (6, none), "outside the closure" picker (15; #24669 closed is the lint-side refusal, related). None covers this plan.
Links back: #28130, ADR-0146 (cdbf08d1), #27356, ADR-0005/0009/0048/0070/0126/0131/0141.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 30, 2026
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
Checklist, 2026-10-30T18:41Z. Every child is lane work; nothing in this subtree carries
pm:epic(maintainer, verbatim: 「这些卡都可以放回车队开发吧」 · 「注意不能写epic车队才会认领」).P0 — in the fleet now
domain:skills·pm:queue· Tier H, maintainer hand-mergesdomain:server·pm:queuedomain:ui·pm:dispatched(os-tesla, claim stands, pause lifted)domain:ui·pm:queuedomain:ui·pm:queuedomain:ui·pm:queueWaiting
<base>.custom·domain:server·pm:blocked,Blocked-by: #28237(moves to N-13 when N-13 is cut)Old cards: #28130 closed not planned (superseded) · #28147 closed not planned, replaced by N-17 (WIP
claude/project-thread-3c7skj@cd0ae0d156kept as reference) · PR #28145 left open, folds into #28237.Next cut: P1 (N-06, N-08…N-16) after #28237 merges, each filed into its lane's
pm:queue.objectstack-fleet commentedon Oct 30, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · epic routing read · 2026-10-30T18:58Z
⛔ Not a claim.
pm:epicby design. Its open children each carry their own lane andpm:queue(or their own state): docs(adr): rewrite ADR-0146 for package-level override — customizations live in <base>.custom through a declared override (folds #28145) #28237 (skills), metadata-protocol: a same-named item in a dependent package replaces the base package's item for package-less reads and in the base app #28238 (server), Studio: one item toolbar — layers, references, history with rollback, audit, source — on every pillar item #28239, Studio: object pickers offer only objects inside the package's dependency closure; the dependencies editor is reachable from Studio #28240 and Setup: position holders and permission-set assignment as people-and-organization pages #28241 (ui), Studio: switch a read-only package's flow off for this deployment from the Automations pillar — ledger switch on the item, posture-aware, refusal verbatim #28148 (dispatched) and Flow clone door → "new similar": the copy lands in a writable target package, by default the paired <base>.custom, never package-less #28149 (blocked). Triage leaves their grades as filed, p2.Generated by Claude Code
actionoverridable in this epic #28364objectstack-fleet commentedon Oct 31, 2026
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
2026-10-31T09:36Z
N-09 card riders — recorded before the card is cut. ADR-0146 rewrite merged in #28295 (head
04aa72e7, 2026-10-31T08:28Z). The maintainer directed in the desktop PM session, verbatim: 「落卡:缺口一和三写进 N-09 的卡面(合并后切卡时),缺口二开决策卡;三处文字残留随 N-09 顺手修。」 When N-09 (write gate) is cut, its body carries all of the following; the seat that cuts it copies this list, ⛔ does not re-derive it.__cthree points (publish,os validate, the install door). A partner package installed from the marketplace, or a<base>.customdeployed from git, never publishes, so it bypasses the rule. N-09 enforces the same three points as__c: publish,os validate, andinstallPackage. Tests cover an install that carries an undeclared same-name item and an install that would be a second declarer..customitem already holds, under D8 item 2 that item stops being served by base address and is not the dependent's own item either — it silently disappears from the UI, and its next publish is refused. Only fields are protected (__c). N-09 at minimum: (a) the upgrade/install door names every such collision in its result instead of passing silently; (b) the cost is added to ADR-0146's cost list in the same PR. Surfacing the orphan in the layered read is N-10's half — N-10's card links here.<base>.custom, one declared override shared by code and Studio; dated pointers on six Status lines #28295 contract-review ACCEPT):.custom" — D2 now lets any closure package declare;actionin a dependent is refused like any other kind, and until action override opens the only fix is rename;.custompackage; align it with the body..customcustomization (and vice versa), with no chain or precedence. It goes to a separate decision card (being prepared); N-09 carriesBlocked-by:that card until it isRuled:, because the second-declarer check is shaped by the answer.Also from the ruling record (6540351677, rider 5): N-09's dispatch first counts existing closure same-name rows across examples and test deployments; zero ⇒ proceed, non-zero ⇒ report the list before migrating.
30 remaining items
__cfields, flow override and "new similar", no package-less overlay #28577objectstack-fleet commentedon Nov 1, 2026
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
2026-11-01T07:15Z
P2/P3 cut · schedule reviewed · audit
On the maintainer's instructions in the epic PM's direct session, verbatim: 「四项提速措施全部按推荐执行:现在切 P2/P3 全部卡并用 Fable 审核、N-09 由你直接派 os-dev、设置卡住巡查。」 · 「用fable 先审核一下这个计划」
Schedule review (Fable) found two release-unsafe windows: after N-13 #28407 and before #28511 (both have since merged, so this window is closed), and after a close of the package-less write and before Studio's replacement. ⇒ #28413 split. #28413 is rewritten to N-16a, the
os migrateceremony only (title and body replaced; hold note 6553667890). The close half is N-16b #28576, which waits on #28413, N-17 and N-25, and carries a major changeset.20 new children (all
priority:p1, lane-labelled, drafted by three agents, reviewed card by card by Fable, filed as sub-issues of this epic; none carriespm:epic):needs-user-decision→ ruled A (6554738464), closedpm:queuepm:queuepm:blockedpm:blocked→pm:queue(6554636923)pm:blockedpm:blockedpm:blockedpm:blockedpm:blockedpm:blockedpm:blockedpm:blockedpm:blockedpm:blocked(the object-extension write path into.custom, a gap the review found)pm:blockedpm:blockedpm:blockedpm:blocked(item 2 settled by M-1 A)pm:blockedNot done by the PM: N-09 #28410 was not dispatched by this seat.
domain:server#2(os-project-manager) had already claimed it at 2026-11-01T04:17Z, and a second dispatch would double-write the card.Riders: #28410, comment 6553666595: the marketplace install door (
marketplace-install-local-plugin.ts#handleInstall, which registers throughmanifestService.registerand never callsinstallPackage) gets the same install-time judgement. #28149, comment 6554259755: Setup's "Open the clone in Studio" link is fixed in N-21's own PR.Stall patrol: a seat-local check every 2 hours flags a
pm:dispatchedchild that has gone 4 hours with no push, no PR movement and no seat comment, and runs the unblock scan. First run: nothing stalled; #28149 and #28563 were unblocked when #28407 closed.objectstack-fleet commentedon Nov 1, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Triage: the epic seat cannot be rung · 2026-11-01T13:45Z
⛔ Not a claim. A knock to the epic seat
epic:#28130#1(hotlong), which dispatches this epic's N-cards.pm:epiccard of this epic, yet it has no seat-post row on the fleet roster and nopm:mailboxPR. Triage, the lanes and the cloud seat can only reach it with card comments, which wake nobody. Example: triage's recommendations on child cards wait until the seat's next read.node scripts/pm/mailbox-open.mjs --repo objectstack-ai/objectstack --seat <the epic seat> --login hotlong(from a cloud container use--head <a branch ahead of main>),subscribe_pr_activityon it, and one line on this card naming the mailbox number. Whether epic seats get roster rows is part of pm-dispatch(mailbox): name when a seat MUST ring another — unblocked card, main-red anchor, release blocker, a question for triage, serial order, a relayed ruling — and close the seats with no mailbox #28675.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage:
trackingadded · 2026-11-02T01:54Z⛔ Not a claim.
pm:queue, for each lane to claim, and keeps the whole subtree offpm:epic. That left the epic card itself with no state (check-half-statesH13). It stays open as the umbrella until its children land, which is thetrackingshape. The children's labels are unchanged.check-half-statesalso flagged it as a P0 suspect; that is a false reading: "P0" in this body names the epic's first phase, not a severity.Generated by Claude Code