Skip to content

Survey + ADR: the platform customization model for packaged metadata — regimes per type, one activation ledger (chartered on #11665) #12049

Description

@os-support-ai

Chartered by maintainer, 2026-08-25, live PM chat (the #11665 fork discussion), verbatim: 「要整体评估除了流程还有哪些需要自定义的也有类似的问题,开始写adr」 — following 「很多所有的元数据都有类似的个性化需求,要统一考虑。」. Direct-dispatch channel (standing authorization 2026-08-10); dispatched immediately by the chartering PM session session_01KWRU3s15AJz7PGW7a7wdCh.

Why this card exists

The platform already runs two and a half customization regimes, each with its own ledger: org overlays for exactly five presentation types (pinned by identity in protocol.org-scoped-write-refused.test.ts); clone-to-customize for permission sets (#11513, its own row-state + projection machinery); and the #11665 flow design about to invent a third. The maintainer ruled: stop per-type invention — assess every metadata type holistically and write the model down as an ADR, so this class of decision never returns to the inbox per type.

Deliverable 1 — the survey (measured, identities not counts, positive controls)

Enumerate every authorable/packaged metadata type (the registry in packages/spec/src/kernel/metadata-plugin.zod.ts is the starting census). Per type, measure:

⚠️ Do not assume the two-regime model is complete: the managed-extension-fields mechanism on packaged objects may be a distinct third regime (extend rather than overlay or clone) — measure and let the evidence set the regime count.

Deliverable 2 — the ADR, as a governed DRAFT PR (docs/adr/**)

The platform customization model:

  1. Regimes and which types fall where (from the survey): presentation → org overlay (the existing five, unchanged); behavioral → clone + takeover, ⛔ never silent override; structural → whatever the extension-fields measurement says.
  2. One generic data-plane activation/customization ledger for the clone+takeover family — a platform object (name proposed by this ADR; sys_metadata is ⛔ NOT it): (metadata_type, name, org nullable) → active / replaced_by / cloned_from {package, name, version} / package_id. Definitions stay in sys_metadata — sole definition ledger, untouched; the org 作用域的 flow overlay 只在「本进程内发布后」绑定触发器,重启后静默失绑——冷启动两条读路径都把 organization_id 非空的行滤掉了 #6190 phantom-overlay wall stands (any design needing allowOrgOverride flipped on a behavioral type has drifted back into org 作用域的 flow overlay 只在「本进程内发布后」绑定触发器,重启后静默失绑——冷启动两条读路径都把 organization_id 非空的行滤掉了 #6190).
  3. Scope + write authority: install-level rows now; org column reserved; in multi-org postures ledger writes are operator-gated (a tenant admin must not flip an install-wide switch — the Decide whether POST /api/v1/automation/:name/toggle belongs in the manage_metadata write set — it mutates flow enablement with no authoring capability #10243 leak made durable otherwise); per-org divergence pre-charted for record-change-triggered flows only, the other trigger types refuse loudly.
  4. Convergence plan: flows = first consumer (Design: post-install customization of packaged flows — clone-to-customize + org-level takeover (supersedes the org-tunable-parameters framing) #11665); permission sets converge in a later card (their landed machinery stays valid meanwhile); overlay types untouched.

Tentative flow-instance directions from the 2026-08-25 maintainer discussion, to incorporate as the worked example (provenance: live chat; final ruling = the maintainer's merge of this ADR, ⛔ none of these is settled until then): A1 + operator-gated multi-org writes + reserved org column + A2 pre-chart · C1 + C3 (new name + provenance; whole-definition copy, never param-list assembly) · Q2(c) takeover refused while packaged callers reference the flow as a subflow, callers named · Q3 a one-line "clone based on v3, base now v5" notice (no diff machinery) · Q4 two deliberate steps with the half-done state shown loudly · Fork D leaning D2 (a Setup surface; automation UI is Studio-only today — survey the Setup permission-set page as the precedent shape).

Discipline

⛔ Draft PR only — governed surface: never ready, never queued, never auto-merged; request review from os-zhuang (author-identity 422 ⇒ assign instead); human merge is the review record and the model's acceptance. ⛔ No implementation code, no packages/spec edits, no edits to #11665's body. #11665 carries Blocked-by: on this card and unblocks when the ADR lands.

Refs: #11665 (design, comment 5404974967) · #11513 · #6190 · #10243 · #11753 (the sibling spec-declared-readonly ruling) · ADR-0087

Activity

  1. os-support-ai commented on Aug 25, 2026

    @os-support-ai
    CollaboratorAuthor

    Claim: maintainer direct-dispatch (decision-inbox PM session)
    Session: session_01KWRU3s15AJz7PGW7a7wdCh
    Branch: claude/issue-12049-customization-model-adr (for the ADR draft PR; the survey half is comment-only)
    Worktree: objectstack-issue-12049 (dev-side, per worktree-first)
    Domain: cross-cutting design (direct charter — audit quote in the card body, verbatim 「要整体评估除了流程还有哪些需要自定义的也有类似的问题,开始写adr」)
    File surface: docs/adr/** (one new ADR, DRAFT PR only — governed) + a survey comment on this card; ⛔ no product code, no packages/spec, no edits to #11665
    Container & model: L design, mode:subagent, model: claude-fable-5 — a platform-wide contract/protocol document is the heaviest class (PM discretion under the 2026-08-10 three-tier ruling; measured-unavailability fallback: opus, never lower)
    Clause-②: no — a direction document; the implementation cards it spawns will carry their own declarations
    Serial constraints cleared: none — docs/adr/** new file, no in-flight claim touches the customization model; the #11665 implementation is deliberately not started (it is Blocked-by this card)


    Generated by Claude Code

  2. claude commented on Aug 25, 2026

    @claude
    Contributor

    Survey: post-install customization across every authorable metadata type

    Deliverable 1 of this card. Dev seat, session 561db75b-4090-5fd3-bb06-accf7af9f8eb, branch
    claude/issue-12049-customization-model-adr. Every claim names the file/line it was read from,
    pinned to origin/main at 0b048393faa98151600598bcebf2ad905ee7021f. Static reads only, with a
    positive control through the same instrument wherever a zero is asserted. The ADR (Deliverable 2)
    cites this comment.

    NOT MEASURED, declared rather than assumed: objectstack-ai/hotcrm (out of this card's scope
    — its opportunity_approval exemplar is cited from #11665, not re-measured). What the objectui
    React designer renders (one text-level zero over the local ../objectui checkout at 2ecc9c7cb2
    is stated in §3.5 as a text zero, not a render fact). No runtime experiment was run.


    1. The census — 27 declared types, plus one dynamic-kind channel

    DEFAULT_METADATA_TYPE_REGISTRY (packages/spec/src/kernel/metadata-plugin.zod.ts:654) is the
    authoritative census, by its own header: "This array is the TOTAL universe of declared metadata
    types (#8586): the only production writer of the manager's type registry is setTypeRegistry(...),
    and no channel extends it."
    Mechanical count over the array body: 27 entries —

    object · field · hook · seed · mapping | view · page · dashboard · app · action · report ·
    dataset | flow · job | datasource · external_catalog · api · translation · email_template ·
    doc · book | permission · position · capability | agent · tool · skill

    One channel sits beside the census, not inside it: a manifest's contributes.kinds registers kind
    descriptors as ITEMS of the kind type (same header, metadata-plugin.zod.ts:648-652). Measured
    specimen: connector — the stack's connectors: entries are registered under kind 'connector'
    (packages/metadata/src/plugin.ts:98; consumed by
    packages/services/service-automation/src/plugin.ts:290-299). Dynamic-kind items ride the same
    storage and the same package provenance; they are not separately flagged for overlay/create, so the
    model the ADR writes must state what regime an undeclared kind defaults to.

    The five-type org-overlay pin holds: protocol.org-scoped-write-refused.test.ts:539-543 derives
    orgOverridable from the registry and pins it toEqual(['view', 'dashboard', 'report', 'translation', 'email_template']) — by identity, not count.

    2. What a customer can actually do today — the write door, derived mechanically

    The runtime write gate judges type flags × item provenance
    (packages/metadata-protocol/src/protocol.ts:13227-13260): an item that exists as a packaged
    artifact requires allowOrgOverride to be written at all ("writing here would overlay code-shipped
    behaviour"); a brand-new name requires allowRuntimeCreate; a type with both flags false is
    refused on every kernel (NOT_CREATABLE / NOT_OVERRIDABLE, #5086 — protocol.ts:11355-11388);
    an org-scoped write on a non-allowOrgOverride type is refused separately (#6190 family,
    protocol.ts:11471-11487). Partitioning the 27 rows by their flags (5 + 17 + 5 = 27):

    Tier Types (by identity) A packaged item can be…
    A — org-overlayable (allowOrgOverride: true) view, dashboard, report, translation, email_template overlaid env-wide AND per-org (ADR-0005)
    B — runtime-create only (allowRuntimeCreate: true, org false) object, hook, seed, mapping, page, app, action, dataset, flow, datasource, external_catalog, doc, book, permission, position, tool, skill not customized at all — any write against the packaged item answers 403 NOT_OVERRIDABLE; only brand-NEW items can be authored
    C — code-only (both false) field, job, api, capability, agent nothing — no runtime write channel exists (OS_METADATA_WRITABLE operator hatch aside)

    Tier B is where the whole problem lives: seventeen types where the platform simultaneously ships
    packaged items and refuses every post-install change to them, with authoring of a sibling item
    fully open — the exact double-fire / silent-shadow setup #11665 measured for flow.

    3. The mechanisms — four real, one paper

    3.1 Org overlay (ADR-0005) — five presentation types, live

    The read path exists only for tier A (loadMetaFromDb hydrates organization_id IS NULL; per-org
    overlays load on demand for allowOrgOverride types only —
    packages/metadata-core/src/meta-write-org-scope.ts:16-24, verified verbatim). For every other
    type an org-scoped row is a phantom write — works until restart, then silently gone; measured
    specimens flow (triggers stop firing) and object (every record 404s). This is the #6190 wall
    the card restates: no behavioral type may be "fixed" by flipping allowOrgOverride.

    3.2 Clone-to-customize + lock — permission sets only, landed (#11513)

    The one shipped instance of the behavioral regime, all identities verified present on this pin:

    • Lock: a Studio/API save targeting a package-declared set refuses 403 naming the clone path
      (packages/plugins/plugin-security/src/packaged-permission-set-lock.ts — header: "The clone is
      an ordinary org-owned set with no upgrade linkage, so upgrades keep flowing to the base
      untouched. No silent overlay row is ever minted again."
      ).
    • Clone: a row action on the data door demanding a new name
      (sys-permission-set.object.ts:115-152); whole-definition-copy is the lesson of The clone_permission_set action copies only 2 of the 6 definition facets, so a clone silently drops system permissions, RLS and tab permissions #11703 (a
      param-list clone silently dropped three of six facets).
    • The activation split: deactivating a packaged set is row state, not a definition write —
      the { active } patch is carved out of the lock by name
      (permission-set-projection.ts:1128-1145, [#4669]). This carve-out is the precedent the
      generic ledger generalizes: "switched off" is operational data-plane state per artifact, never a
      metadata write.
    • Drift surface: permission-set-drift.ts covers only the enforced copy vs its own artifact
      (overlay_shadow / provenance_skip); clone-vs-base drift is deliberately absent (the ServiceNow
      layer, recorded-not-chartered).

    3.3 Extend — objectExtensions + navigation contributions: real, general, package-grain

    This is the mechanism the dispatch asked to measure for a third-regime slot, and it is real —
    but it is not the auth one (§3.4):

    • ObjectExtensionSchema (packages/spec/src/data/object.zod.ts:2920-2980): a package declares
      objectExtensions: [{ extend: '<target>', fields, validations, indexes, label… }] against an
      object owned by another package; the loader registers it as contributor kind 'extend'
      and merges at boot (packages/objectql/src/engine.ts:4529-4547; merge carries exactly
      fields/label/pluralLabel/description/validations/indexes — the schema's own guidance refuses
      actions/hooks/listViews through this door and names the top-level alternative).
    • The UI analog: NavigationContributionSchema (ADR-0029 D7, packages/spec/src/ui/app.zod.ts)
      injects nav items into an app the contributor does not own.
    • Reach: the mechanism rides the package-install door — POST /packages routes through
      protocol.installPackage → registry.installPackage(manifest)
      (packages/runtime/src/domains/packages.ts:281-313), the same primitive that registers
      extensions — so post-install extension of a packaged object is possible by installing/authoring
      another package
      , at install grain. No per-org dimension, no ledger, no upgrade linkage
      (the extension package survives base upgrades untouched).
    • Exemplar pull: showcase ships them (objectExtensions: allObjectExtensions,
      examples/app-showcase/objectstack.config.ts:192) and the mechanism has a published hand-written
      doc page (content/docs/data-modeling/object-extensions.mdx).

    3.4 Managed extension fields (plugin-auth) — measured: NOT a customization regime

    MANAGED_EXTENSION_FIELDS (packages/plugins/plugin-auth/src/managed-extension-fields.ts:57-125)
    is a build-time constant answering a different question: which columns on the four
    better-auth-managed objects (sys_user, sys_organization, sys_api_key, sys_invitation)
    belong to ObjectStack rather than to the embedded library (ADR-0105 D7 / ADR-0092 D2), plus a
    hard-coded editable subset. No runtime write channel mints entries, no org dimension exists, and
    it faces exactly four sys objects. The PM assumption that this might be the third regime is
    falsified — the extend slot belongs to §3.3.
    (The name collision discipline in its header is
    still worth citing in the ADR as prior art for "extension must never collide with base surface".)

    3.5 The paper mechanism — a three-layer customization protocol with zero consumers

    packages/spec/src/kernel/metadata-customization.zod.ts declares a full
    system/platform/user three-layer overlay protocol — MetadataOverlaySchema, per-field
    FieldChangeSchema (originalValue/currentValue/changedBy), lockedFields/customizableFields,
    3-way merge on package upgrade. It is exported (packages/spec/api-surface/kernel.json:213) and
    published as reference documentation
    (content/docs/references/kernel/metadata-customization.mdx). Consumers:

    POSITIVE CONTROL  grep MetadataOverlaySchema|CustomizationOriginSchema|FieldChangeSchema
                      → finds the definitions in packages/spec/src and the api-surface row
    ZERO              the same grep across packages/** excluding spec (framework runtime):  0 matches
    ZERO              the same grep across the local ../objectui checkout @ 2ecc9c7cb2:     0 matches
    

    The real overlay implementation (sys_metadata + organization_id, ADR-0005) shares no code with
    it. A declared-but-unenforced protocol surface publishing the exact ServiceNow-style layer the
    #11513 ruling left unchartered is ADR-0049 territory; filed unassigned as #12057 (finding),
    and the ADR must state its disposition so the paper protocol cannot be mistaken for the model.

    4. Per-type survey

    Class: B behavioral (runs with side effects) · P presentational (render-time) ·
    S structural (schema/wiring) · C content. "Packaged today" cites in-repo deliverables
    (platform packages + examples/); file-pattern counts under-measure inline stack declarations, so
    stack keys are cited where patterns miss. "Pull" = authoring pull in the in-repo exemplars
    (showcase/CRM/todo); hotcrm NOT MEASURED.

    Type Class Packaged today (specimen) Post-install customization of a packaged item, today Exemplar pull Doc promise touching it
    object S 78 *.object.ts in packages (sys_permission_set…), 26 in examples ⛔ 403; extend via §3.3 package grain high (26 + extensions) F6 "objects … then customize in Studio" — unkeepable for fields-on-packaged
    field S inside objects ⛔ code-only spelling retired (#7893); via owning object only, which is ⛔ for packaged n/a —
    hook B 2 example *.hook.ts (opportunity.hook.ts…) + showcase hooks: allHooks; runtime hooks use sandboxed bodies (packages/runtime/src/sandbox/body-runner.ts:303) ⛔ 403 on packaged; new hooks open yes —
    seed C all three examples ship stack data: seeds (ShowcaseSeedData, CrmSeedData) moot — applied once; post-install the rows are ordinary records yes F6 "seed data included" — true
    mapping S showcase mappings: allMappings ⛔ overlay; clone-by-authoring free (new name) yes import-mappings.mdx:85 documents the negative honestly
    view P 10 example *.view.ts + platform ✅ org overlay (tier A) high metadata-service.mdx:328 — true
    page P 21 example pages + 4 platform ⛔ 403; new pages open; nav repoint needs app edit (⛔ on packaged apps) high F6 "customize in Studio" — false for packaged pages
    dashboard P 5 examples + 1 platform ✅ org overlay yes F6 — true
    app P platform Studio/Setup apps; 2 examples ⛔ 403 on packaged; nav contributions open (§3.3) yes —
    action B 2 platform + 2 example ⛔ 403 (was the #6283 phantom shape); new actions open (sandbox bodies) yes —
    report P 1 example + 1 platform ✅ org overlay low —
    dataset P/S 4 example datasets ⛔ 403; new datasets open yes —
    flow B 4 example *.flow.ts (showcase + CRM); hotcrm cited, NOT re-measured ⛔ 403 (F1/F3 verified below); no clone action exists (#11665 §4.2) yes F6 "flows … then customize in Studio" — false, the motivating breach
    job B showcase jobs: allJobs ⛔ code-only (#4509 — handler resolves only through the compiled bundle) yes —
    datasource S 2 example datasources ⛔ on code-defined (origin gating); runtime-created via wizard open (ADR-0015) yes —
    external_catalog S never packaged by design (derived snapshot, registry comment) moot n/a —
    api B showcase apis: allApis ⛔ code-only (#5488 — a saved runtime row was never served) yes —
    translation C showcase translations ✅ org overlay yes —
    email_template C showcase emailTemplates: allEmails ✅ org overlay yes email-templates.mdx:213 documents overlay-wins
    doc C packages ship docs/*.md items ⛔ overlay; new docs open low —
    book C/P showcase books: allBooks ⛔ 403 (rolled back #6483 — Studio drag-edit refused) yes —
    permission B stack permissions: (showcase permission-sets.ts); plugin-materialized sets ✅ clone + lock + row-state deactivate (§3.2) — the landed regime instance yes —
    position B showcase positions.ts ⛔ 403 on packaged; new positions open yes —
    capability B showcase capabilities.ts + plugin bootstrap ⛔ code-only (ADR-0066 D1 — minting grant targets at runtime is the defect) yes —
    agent B platform ships exactly two (ADR-0063 §2) ⛔ code-only, deliberate ("the code is the record") none (closed) —
    tool B (runtime-creatable; no in-repo packaged *.tool.ts specimen) ⛔ 403 on packaged; new tools open none found —
    skill B skills/ catalog ships to customer projects ⛔ 403 on packaged; new skills open catalog —

    5. The published-promise measurements (the F6 family)

    Two shipped pages promise post-install customization of packaged apps generically:

    Against §2: the promise is keepable today for views/dashboards (tier A), for no tier-B type,
    and the flows half is the measured breach #11665 chartered. The honest negatives exist too
    (import-mappings.mdx:85), so the doc corpus knows how to say "no" — the promise pages just don't.

    6. Spot-checks of #11665's load-bearing facts on this pin

    Fact Verdict Evidence
    F1 — the on/off switch is a field of the packaged artifact holds flow.zod.ts:641 status: z.enum(['draft','active','obsolete','invalid'])
    F3 — org-scoped rows of non-overlay types are phantoms holds meta-write-org-scope.ts:16-24 read verbatim
    F4 — only record-change triggers carry an org holds grep `tenantId
    F5 — single posture = one logical tenant; multi-org is entitlement-gated holds tenancy-posture.ts:12-16, 26-30
    five-overlay identity pin holds test line 543, §1
    F2 (env-wide flowEnabled map = the #10243 leak) cited, not re-measured runtime state, no static read adds anything
    §2.2 same-name shadow (listItems no precedence) holds, fix in flight draft PR #12026 (claude/issue-11997-packaged-flow-name-shadow) — not landed on this pin; the ADR's C1 (new name mandatory) must not depend on the shadow persisting

    7. What the evidence sets the regime count at

    Three live regimes plus a code-only tier, and one paper protocol to retire or converge:

    1. Overlay (presentational): the five tier-A types — live, unchanged.
    2. Clone + takeover (behavioral): landed for permission; chartered for flow (Design: post-install customization of packaged flows — clone-to-customize + org-level takeover (supersedes the org-tunable-parameters framing) #11665);
      structurally applicable to every tier-B behavioral type (hook, action, tool, skill, position)
      as pull appears. This is the family the generic activation ledger serves.
    3. Extend (structural): objectExtensions + navigation contributions — live, package-grain,
      additive, no ledger required (the customization IS a package; provenance and upgrade isolation
      come from package identity).
    4. Code-only tier (field, job, api, capability, agent): customization = ship a new package
      version; deliberately outside the model.

    The managed-extension-fields mechanism is not a regime (§3.4). The three-layer spec protocol is
    not a regime either — it is paper (§3.5) and needs an explicit disposition in the ADR.


    Generated by Claude Code

  3. claude commented on Aug 25, 2026

    @claude
    Contributor
    {
      "issue": 12049,
      "status": "done",
      "branch": "claude/issue-12049-customization-model-adr",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12063",
      "premise_still_valid": true,
      "summary": "Survey posted as issue comment 5406807988 (27-type census, write-door tier partition, four real mechanisms + one paper one, per-type table, doc-promise measurements, F1/F3/F4/F5 spot-checks at origin/main 0b048393). ADR-0126 drafted as docs/adr/0126-packaged-metadata-customization-model.md on draft PR #12063 (Part of #12049): three regimes by artifact behavior, sys_metadata_activation generic ledger, install-level operator-gated write authority with reserved org column, the #6190/sole-definition-ledger/upgrade-vs-choice walls, flow worked example carrying the 2026-08-25 tentative directions marked settled-only-by-merge. PM-assumption verdicts: registry census confirmed authoritative (plus dynamic-kind channel); managed-extension-fields FALSIFIED as a regime (build-time auth ownership declaration) - the extend regime slot belongs to objectExtensions/ADR-0029-D7 nav contributions, which are real, general, packaged-object-facing, package-grain; #11665 F1/F3/F4/F5 and the five-type pin all hold on this pin.",
      "files_changed": [
        "docs/adr/0126-packaged-metadata-customization-model.md (added, 363 lines; the only file in the diff)"
      ],
      "gates_run": [
        "node scripts/pm/dispatch-gates.mjs (no paths; stderr header: objectstack-ai/objectstack at 7ca6f94) -> derived 5 families",
        "pnpm check:adr-anchors -> exit 0 (self-test + scan)",
        "pnpm check:doc-authoring -> 'doc authoring guard: 390 files clean - no bare metadata literals.'",
        "pnpm --filter @objectstack/lint run check:doc-formula-expressions -> '22 record-scoped formula example(s) across 422 files / 1449 TS blocks judged clean' (verify-lock VERDICT command-exit 0)",
        "pnpm check:pm-governed-merges -> 'check-governed-merges --self-test: 129 assertions'",
        "node scripts/check-adr-links.mjs -> 'check-adr-links: 554 relative link destination(s) under docs/adr/ resolve'",
        "pnpm check:nul-bytes -> 'check-nul-bytes: OK (scanned 6696 text file(s))'",
        "all gate runs at HEAD 7ca6f94 (the final and only commit); heavy steps (install, builds) serialized through scripts/pm/os-verify-lock.sh, max hold 4m57s, waited 0s"
      ],
      "tests": "Docs-only diff; no package tests owed. Gate evidence above; exits captured before any pipe (cmd > log 2>&1; EXIT=$?), verdict lines quoted from the gates' own output.",
      "deviations": [
        "Global GitHub search API is unavailable in this session (repository-scoped endpoints only); dedup for the finding ran as repo-scoped REST listing of all 377 open issues + local keyword grep instead of search - declared narrowing: closed issues were not swept.",
        "Review request to os-zhuang succeeded (HTTP 201, requested_reviewers read back) - the author-identity 422 fallback was not needed.",
        "No changeset; skip-changeset applied via additive POST and read back AFTER the size-labeler wrote (labels: size/m, skip-changeset) - the ADR-0124/PR #9856 precedent.",
        "Branch commit carries no card-relationship trailer (squash rule); the Part of declaration lives in the PR body only."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #12057: spec ships a three-layer metadata-customization protocol (MetadataOverlaySchema, field-level change tracking) with zero consumers; published reference docs describe an unimplemented customization architecture (finding label, unassigned, Blocked-by #12049)"
      ]
    }

    Generated by Claude Code

  4. os-support-ai commented on Aug 25, 2026

    @os-support-ai
    CollaboratorAuthor

    PM acceptance — both deliverables verified; awaiting human merge of the ADR.

    Session: session_01KWRU3s15AJz7PGW7a7wdCh (the chartering PM session). Terminal report = comment 5406965521, read back intact.

    Deliverable 1 (survey) — comment 5406807988, accepted: 27-type census from DEFAULT_METADATA_TYPE_REGISTRY with claims pinned to origin/main @ 0b048393; write-door tier partition (5 org-overlayable / 17 runtime-create-only / 5 code-only); four real mechanisms + one paper one; per-type table; doc-promise measurements (the F6 family, plus a second specimen build-without-code.mdx:37 not previously recorded); #11665's F1/F3/F4/F5 and the five-type overlay pin all hold on this pin. The card's ⚠️ was answered by measurement, not assumption: managed-extension-fields is falsified as a regime (a build-time auth ownership declaration, ADR-0105 D7 / ADR-0092 D2); the extend-regime slot belongs to objectExtensions + ADR-0029 D7 navigation contributions — regime count stays three, plus the code-only tier.

    Deliverable 2 (ADR) — draft PR #12063, single file docs/adr/0126-packaged-metadata-customization-model.md (+363), accepted as the decision vehicle: three regimes by artifact behavior; one generic sys_metadata_activation data-plane ledger (definitions stay in sys_metadata, sole definition ledger); install-level operator-gated write authority with the org column reserved; the #6190 phantom-overlay, sole-definition-ledger, and upgrade-vs-choice walls restated; the flow worked example carries the 2026-08-25 maintainer directions each marked settled only by this ADR's acceptance.

    Discipline held: PR stays DRAFT (governed surface), review requested from os-zhuang (HTTP 201, no fallback needed), skip-changeset per the ADR-0124/PR #9856 precedent, no product code, no packages/spec edits, no #11665 body edits. Out-of-scope finding filed as #12057 (finding, unassigned, Blocked-by #12049); the ADR states its disposition so the paper protocol cannot be mistaken for the model.

    State: pm:dispatched → pm:awaiting-maintainer. ⛔ Next action is the maintainer's alone: review and hand-merge PR #12063 — the merge is the review record and the model's acceptance. After merge + verification on origin/main, this card closes and #11665 (Blocked-by: #12049) unlocks for its implementation legs.


    Generated by Claude Code

  5. os-support-ai commented on Aug 25, 2026

    @os-support-ai
    CollaboratorAuthor

    Maintainer rulings — 2026-08-25, live PM chat, after reading the ADR draft on PR #12063. Recorded by the chartering PM session session_01KWRU3s15AJz7PGW7a7wdCh; an amendment implementing both is dispatched to the same draft PR. ⛔ As with the rest of the ADR, nothing is settled until the maintainer's hand-merge.

    Ruling 1 — hook leaves the model (code-only posture). Verbatim, untranslated: 「hook 也算代码类吧,我觉得也不需要修改。」 Interpretation: hook is removed from the Regime C pre-chart list and joins the "outside" tier. A hook body is imperative package code maintaining the package's own data invariants — the managed-package trigger analogy (Salesforce locks managed Apex triggers; customization = the vendor ships a new version). Packaged hooks get no ledger rows and no disable switch — the job posture. Runtime authoring of brand-new sibling hooks stays open, unchanged. Cost stated to the maintainer on the record: a misbehaving packaged hook has no customer-side off-switch; the remedy is a vendor fix, as with jobs today.

    Ruling 2 — Regime C simplifies to disable + clone, no takeover ceremony. Verbatim, untranslated: 「行为类 能否搞一个启用停用的功能,我不想要可以停用,然后克隆一个。」 Interpretation: the behavioral regime becomes two independent primitives — an enable/disable switch per packaged artifact (the activation-ledger row flip, operator-gated per D3) and free sibling cloning (new name mandatory, whole-definition copy — both stand). No designated-replacement linkage: replaced_by and cloned_from leave the §4 ledger, which shrinks to a pure on/off registry (metadata_type, name, package_id, organization_id nullable-reserved, active). The clone is an ordinary artifact with no recorded linkage to its base — exactly the landed #11513 permission-set shape, generalized. Consequences: the §7.3 referenced-while-packaged guard attaches to disable (refused, callers named); the §7.4 "clone based on v3, base now v5" notice is dropped (no data source without provenance; recorded as deliberately not tracked, consistent with the deferred ServiceNow-style layer); the "half-done state" concept dissolves into ordinary displayed states; permission-set convergence (§8) gets simpler since semantics are now identical.

    Still open with the maintainer in chat: whether action follows hook out of the model or stays in the Regime C pre-chart (PM proposal on the table: internal package plumbing → code-only; customer-facing business behavior → disable + clone, so action stays).


    Generated by Claude Code

  6. os-support-ai commented on Aug 25, 2026

    @os-support-ai
    CollaboratorAuthor

    Maintainer ruling — 2026-08-25, live PM chat (follow-up to the amendment rulings). Recorded by the chartering PM session session_01KWRU3s15AJz7PGW7a7wdCh.

    Ruling 3 — action stays in Regime C, and the disable half now has measured pull. Verbatim, untranslated: 「动作 可能是需要开关的,因为有的 action 我不想启用。」

    Interpretation: this settles the question left open after ruling 1 (whether action should follow hook out of the model) — it does not: action remains in the disable+clone family, per the boundary the PM proposed and the maintainer has now confirmed by supplying the pull themselves (internal package plumbing → code-only; customer-facing business behavior → disable + clone). The quote is itself first-party pull for the disable half of actions: when the post-merge implementation legs are cut for #11665, an action-disable leg (same sys_metadata_activation ledger, a consult point at action dispatch, a surface row) is chartered to follow the flow legs; the clone half of actions still waits for real pull, per §3's pre-chart discipline. No ADR text change required — action already sits in Regime C's pre-chart list; this ruling starts the clock on its switch, and the implementation card will cite this comment as provenance.


    Generated by Claude Code

  7. os-support-ai commented on Aug 25, 2026

    @os-support-ai
    CollaboratorAuthor

    Closing — both deliverables complete and the ADR is accepted on origin/main.

    Verification (session session_01KWRU3s15AJz7PGW7a7wdCh): PR #12063 merged as 28b47a93 on main; docs/adr/0126-packaged-metadata-customization-model.md blob on main reads 6ca6dd1d — byte-identical to the final pushed revision (head a09d035a, three maintainer rulings folded in).

    Merge provenance, for the record: the merge was executed by PM session session_01H9StxQgG2DPA26XzZZqnJB (os-trump) via the merge queue, citing the maintainer's batch instruction on its governed-PR review, verbatim: 「同意,帮我合并,然后继续」 (audit comment on PR #12063, comment 5409315015). Per the ADR's own terms the merge is the model's acceptance — the §7 flow-instance directions (A1 install-level disable · C1 whole-definition clone, no ancestry · Q2(c) disable refused while packaged subflow callers exist · Fork D Setup surface, on/off only) are now settled.

    Downstream: #11665 unblocks now (label swap to pm:queue, comment posted there); its implementation legs consume ADR-0126 §7. The action-disable leg follows the flow legs (ruling 3). Release-target (v17 minor vs v18) is pending a maintainer ruling in chat and will be recorded on the implementation cards when cut. #12057 (paper protocol) proceeds separately per §6.4/D5.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions