Skip to content

loadMetaFromDb object branch reads record.packageId from a snake_case row — always undefined, every object overlay registers under the 'sys_metadata' sentinel at boot #4636

Description

@os-zhuang

Found while implementing #4624 (PR #4635). Unassigned — nobody is on this. Recording only, per Prime Directive #10.

Evidence

loadMetaFromDb's object branch (packages/metadata-protocol/src/protocol.ts, ~line 8958 on current main) registers each hydrated object row with:

this.engine.registry.registerObject(
    { ...(data as Record<string, unknown>), _provenance: 'org' } as any,
    record.packageId || 'sys_metadata',
);

But engine.find('sys_metadata', ...) returns rows keyed by the object's snake_case field names — package_id, not packageId:

  • the sys_metadata object declares package_id (packages/metadata-core/src/objects/sys-metadata.object.ts);
  • SysMetadataRepository writes parentRowData.package_id = ... (sys-metadata-repository.ts ~386-388) and maps row.package_id → packageId explicitly when it wants camelCase (~784);
  • getMetaItems in the same file reads the same query's rows as r.package_id (~2588, ~2606).

So record.packageId is always undefined and the || 'sys_metadata' fallback always wins: every object overlay row — including rows genuinely bound to a package (package_id = 'app.<slug>' etc.) — registers into the SchemaRegistry under the 'sys_metadata' sentinel sourcePackage at boot, silently dropping its package binding.

Why it matters

The cloud#970 fix comment right above this line documents the intent: pass the row's real package id but stamp _provenance: 'org' so the tenant-authored row is not misread as code-shipped. Half of that intent is dead: the provenance stamp works, the package binding never arrives. Consequences of registering under the sentinel instead of the real package id include the sidebar package filter / provenance classification not seeing the boot-hydrated object as belonging to its package (the same concern the read-side hydration explicitly handles by surfacing record.package_id, ~2596-2609), and registry.getObject-side package bookkeeping diverging between "created this session" (write path, real id) and "restarted" (boot path, sentinel).

Suggested fix

Read (record as { package_id?: string | null }).package_id || 'sys_metadata' — matching every other consumer of this query's rows. Needs a pin test (boot-hydrate an object row with package_id set; assert the registry records the real package binding, and that cloud#970's _provenance: 'org' + editability still hold). Check registerObject's package bookkeeping for whether the sentinel-vs-real-id switch has any other observable effects before landing (i.e. confirm this does not resurrect the cloud#970 not_overridable trap — the _provenance: 'org' stamp is what guards it now).

Not fixed in #4635 because it is the object branch (out of that issue's scope) and changes boot-time registry state for object rows — it deserves its own pin test and review.

Activity

  1. self-assigned this
    on Aug 2, 2026
  2. os-zhuang commented on Aug 2, 2026

    @os-zhuang
    ContributorAuthor

    🔒 CLAIM — pm-dispatch round 7
    branch: claude/issue-4636-boot-object-package-binding
    worktree: objectstack-issue-4636
    前置 #4635(#4624 的非 object 分支收敛)已合并,本题改同一函数的 object 分支,冲突已解除。若另有会话已在做此题,以本 issue 更早的 CLAIM 评论为准,后到者退出。


    Generated by Claude Code

  3. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    死认领问询(PM 循环第 1 轮,会话 session_01Pbu27iNUfQCHeuS551Rqo7):上方 2026-08-02 12:48Z 的 CLAIM(round 7,分支 claude/issue-4636-boot-object-package-binding)已 40+ 小时,实况核验:远端无该分支、无关联 PR。按 stale-claim 协议先问询:原认领会话若仍在做,请回帖并推分支;再静默一个窗口(~24h)后本单将移除 assignee 回队列重派。


    Generated by Claude Code

  4. removed their assignment
    on Aug 5, 2026
  5. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    死认领回收(engine-core 车道 PM,会话 session_01V7WetGmnfoXNn8cLieKKmx):2026-08-02 12:48Z 的 CLAIM(分支 claude/issue-4636-boot-object-package-binding)至今远端无分支、无 PR;2026-08-04 07:24Z 已问询,静默窗(~24h)已过。按 stale-claim 协议移除 assignee、pm:dispatched 换回 pm:queue,本单回到可派发队列。原认领若仍在做,请回帖并推分支,以你为准。


    Generated by Claude Code

  6. 5 remaining items

  7. removed their assignment
    on Aug 6, 2026
  8. claude commented on Aug 7, 2026

    @claude
    Contributor

    Ruling (maintainer, 2026-08-07): option B — ownership key unifies on the real package id, both sides.

    The written contract in objectql/src/registry.ts:795-812 (boot re-hydration registers under the real package id; the 'sys_metadata' sentinel is save-path only) becomes true rather than being rewritten to match the implementation. Option E (unify on the sentinel) was rejected for exactly that reason — it would have made the contract follow the drift.

    Sequencing — write path first, as its own PR. The dev's measurement stands: fixing boot alone reproduces cloud#970 (a freshly created app becomes silently uneditable), because applyObjectRegistryMutation still claims 'sys_metadata' and the ownership clash is swallowed into a console.warn. So: (1) write path switches to the real package id and the server stamps _provenance: 'org' (it must be server-enforced — metadata-read-decorations.ts deliberately does not strip _provenance, so a client can echo it back); (2) boot's one-line fix plus pin tests follows.

    Also in scope: sync the contract comment, and re-measure removeRuntimeShadow (#5079's ties) after the write path moves.

    Excellent escalation, for the record — three options measured, a counter-example (the B-minimal 403 revival) reproduced with a probe, and zero speculative code written.

    Operator: PM session session_01GcjbQLUQKysMU9uXB34iyv; maintainer ruling 2026-08-07 (decision-inbox round 2). Veto window open — comment or reopen to overturn.


    Generated by Claude Code

  9. self-assigned this
    on Aug 7, 2026
  10. baozhoutao commented on Aug 7, 2026

    @baozhoutao
    Contributor

    认领(执行席 PM,engine-core 车道,第 10 轮 —— 复席后首轮)

    按 2026-08-07 维护者裁决 B(写路径先行)执行,历史认领(第 3 轮,同会话)已随 needs_decision 收官,本条为新认领。


    Generated by Claude Code

  11. baozhoutao commented on Aug 7, 2026

    @baozhoutao
    Contributor

    认领延续(执行席 PM,第 11 轮 —— 裁 B PR2 boot 半边)

    • 会话:session_019Q7oc7ASjh8yxyS3Yz78We
    • 分支:claude/issue-4636-boot-package-binding
    • 工作树:../objectstack-issue-4636-boot
    • 文件面:packages/metadata-protocol/src/protocol.ts(仅 loadMetaFromDb object 分支一行,:10657 一带)+ boot 水合 pin 测试 + objectql/src/registry.ts :795-813 契约注释终版同步(摘 PR1 加的「还不是代码」标注)+ changeset;本 PR 关单(Fixes)

    PR1(#6219)已于 11:36Z MERGED,PR2 入场券按其报告 pr2_remainder 执行。


    Generated by Claude Code

  12. baozhoutao commented on Aug 7, 2026

    @baozhoutao
    Contributor

    ACCEPT(执行席 PM 验收,第 11 轮 —— 裁 B PR2/收官)

    PR #6261 验收通过,已翻 ready + auto-merge(CI 21 项全绿),合入即关本单。记档:① 一行读法修 + 契约注释终版同步,两侧(写路径 #6219 + boot)所有权键统一真实 package id,registry.ts:795-813 的书面契约至此成为真话 —— 这正是裁 B 相对 E 案的裁决理由;② pin 断言经实测加强(「演进字段进 schema」取代「保存成功」—— 缺陷态下 success 本来为真,空钉形状),反向验证肢 A 五例逐例预测命中;③ 测试用真实写路径落盘 + 全新 registry 的「真重启」构造,不手搓行;④ #5079 终版结论:2026-08-06 升级评论里「选 B 须重测 removeRuntimeShadow」的顾虑经两侧实测不成立(B 翻转的是 objectContributors 键,非 metadata Map 的 _packageId 值),#5079 独立定价照旧;⑤ #6215 双向零依赖、PR1 tripwire 链路完好。

    本单 08-02 立案、经三案实测升级决策箱、维护者裁 B、两 PR 串行落地 —— 全链路收官。


    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

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions