Repository navigation
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
Activity
🔒 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
死认领问询(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
死认领回收(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
5 remaining items
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
applyObjectRegistryMutationstill claims'sys_metadata'and the ownership clash is swallowed into aconsole.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.tsdeliberately 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
认领(执行席 PM,engine-core 车道,第 10 轮 —— 复席后首轮)
- 会话:
session_019Q7oc7ASjh8yxyS3Yz78We - 分支:
claude/issue-4636-writepath-real-package-id(本轮只做裁 B 的 PR1 写路径半边;boot 半边 PR2 待 PR1 合入后下轮单独派发,规避堆叠 PR squash 税 —— 座位贴教训 ⑧) - 工作树:
../objectstack-issue-4636-writepath - 文件面:
packages/metadata-protocol/src/protocol.ts(applyObjectRegistryMutation:7220 一带 + rollback :9939 的 packageId 管线)+ 服务端强制_provenance:'org'盖章点 + 测试 + changeset;⛔ 本 PR 不触loadMetaFromDb(PR2 的面)、不触saveMetaItem的 api 门区域(api注册表条目声明 allowRuntimeCreate: true,但运行时创建的端点匹配器永远看不见 —— 声明的能力运行时不兑现(真实 boot 实测) #5488/saveMetaItem的 direct-active 写入是api的第三条门外通路 —— 命名空间门(ADR-0121 D1/D2)与去重门在这条路上无人跑 #5311 下轮的面)
按 2026-08-07 维护者裁决 B(写路径先行)执行,历史认领(第 3 轮,同会话)已随 needs_decision 收官,本条为新认领。
Generated by Claude Code
- 会话:
认领延续(执行席 PM,第 11 轮 —— 裁 B PR2 boot 半边)
- 会话:
session_019Q7oc7ASjh8yxyS3Yz78We - 分支:
claude/issue-4636-boot-package-binding - 工作树:
../objectstack-issue-4636-boot - 文件面:
packages/metadata-protocol/src/protocol.ts(仅loadMetaFromDbobject 分支一行,: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
- 会话:
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
- added a commit that references this issue
on Aug 8, 2026 - added a commit that references this issue
on Aug 17, 2026
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 currentmain) registers each hydrated object row with:But
engine.find('sys_metadata', ...)returns rows keyed by the object's snake_case field names —package_id, notpackageId:sys_metadataobject declarespackage_id(packages/metadata-core/src/objects/sys-metadata.object.ts);SysMetadataRepositorywritesparentRowData.package_id = ...(sys-metadata-repository.ts~386-388) and mapsrow.package_id → packageIdexplicitly when it wants camelCase (~784);getMetaItemsin the same file reads the same query's rows asr.package_id(~2588, ~2606).So
record.packageIdis alwaysundefinedand 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'sentinelsourcePackageat 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 surfacingrecord.package_id, ~2596-2609), andregistry.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 withpackage_idset; assert the registry records the real package binding, and that cloud#970's_provenance: 'org'+ editability still hold). CheckregisterObject'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#970not_overridabletrap — 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.