Repository navigation
fix(objectql): an object extension whose extend folds to a legacy ns__name key is filed under that key, not a second owner-less entry (#28858) - #28887
Conversation
📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 20 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1a29198d0e348ebbc81e5d4d5f0892cddb4b0b5a && git checkout 1a29198d0e348ebbc81e5d4d5f0892cddb4b0b5a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7fa9873f38f7a0158e3ce17338dcc0f446b113e3 6db3a09fa9cbb7973ae5af4bdfdf8e3d4b5e0be1 && git checkout -B drift-repro 7fa9873f38f7a0158e3ce17338dcc0f446b113e3 && git merge --no-ff 6db3a09fa9cbb7973ae5af4bdfdf8e3d4b5e0be1
node scripts/docs-audit/affected-docs.mjs --json 7fa9873f38f7a0158e3ce17338dcc0f446b113e3
|
|
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC 席位核对 · ACCEPT ·
|
| item | reading |
|---|---|
| Shape | Draft, base main, Fixes #28858. 4 files (+211 / −1): extensionTargetKey and its one call in registerObject's extend arm (objectql/src/registry.ts), the one place the manifest objectExtensions loop and a stored row's registerObjectExtension both reach. Plus a new objectql test, a runtime hydration case, and an objectql patch changeset. Metadata-protocol: none. No export, no packages/spec/**, no gate, no new error code. |
| Rule | An extend that getObject folds to a different (legacy ns__name) key is filed under the resolved key, and the spelling is named once at warn. It is never refused at boot (Startup registry reads). An exact-name entry or nothing to fold to keeps the written spelling, as before. withdrawObjectExtension already resolves through resolveObjectKey, so the delete of such a row now finds the contribution it filed. The #28853 item gate, computeFQN and parseFQN are unchanged. |
| Premise | Count 0 in this repo and in hotcrm, but authoring reaches it: the published ObjectSchema accepts an ns__name object name (spec-dist probe). Residual: a legacy owner registering after the extension is not yet there to fold to, so the extension is filed as written, as on main. |
| Evidence | CI green on head 6db3a09f (filter=latest: 35 success, 13 skipped, 0 failed). The report shows red on main (objectql 2 of 4; the runtime hydration case 1 of 6, a phantom case contributor) and green on head with #28853's pins, an ablation restored clean, the objectql and metadata-protocol suites, typecheck in full and 63/63 derived gates. |
| Landing | 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay, with every check green or an expected skip. |
Generated by Claude Code
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC
Fixes #28858
Clause-②: no
Metadata-protocol: none
维护者速读
extend写成短名,而getObject把它折叠到旧式ns__name键时,注册表把这条扩展登记到折叠后的那个键上,字段落到真正的对象,不再在旁边开一个没有所有者的第二个对象条目;同一写法每个包在日志里warn一次,并给出应写的键。object_extension保存口拒绝这种写法,但清单的objectExtensions循环和启动时回灌存储行仍照原样登记,会多出一个幽灵条目,扩展的字段也到不了任何读取。extend折叠到别的键的扩展;本仓库和 hotcrm 中这样的写法为 0。写对键、或对象恰好以该名字注册时行为不变。启动时不拒绝。回滚即还原本 PR。packages/objectql/src/registry.ts的registerObject中extend分支新增的两行,以及extensionTargetKey。pnpm --filter @objectstack/objectql exec vitest run src/registry-extension-legacy-key-fold.test.ts,预期 4/4。What changes
registerObject'sextendarm is the one place both entries reach: the manifestobjectExtensionsloop (engine.ts) and a storedobject_extensionrow (registerObjectExtension). Right after the extend refusals, it now files the contribution underextensionTargetKey(extend). That isextenditself when an entry is registered under it or nothing is registered that it folds to. It isgetObject's fold (resolveObjectKey) only when the fold names a different key. The folded spelling is named once atwarn, keyed by spelling, resolved key and package (deduped likereportFieldPatchMisses), with the key to write.registerObjectExtensionanswers the resolved key, and the row delete'swithdrawObjectExtensionalready resolves the same way.There are no refusals at boot, no change to #28853's item gate, and
computeFQN/parseFQNare untouched. There is nopackages/spec/**, no new export, gate or error code.Premise, measured (step 1). The count is 0 everywhere:
extendvalues are each a declared object's exact name, and no shipped object name contains__;The case is still reachable by authoring: the spec dist's
ObjectSchemaacceptscrm__accountandsurvey__c, and a stored object row from the FQN era reaches it too. The seat kept the card on that ground.Clause-② measured: no. No accept set moves and nothing is exported. For a package with the folded spelling (0 measured), the extension's fields now reach the object they name, where on main they reached no read.
Residual. A legacy owner that registers after the extension is not yet there to fold to, so that extension is filed as written, as on main.
Tests
Red first, ablation, controls
objectqlregistry-extension-legacy-key-fold.test.ts(4 cases, from source):extend: 'case'beside the ownercrm__case: the field lands oncrm__case, nocasecontributors, one object listed, one warn across a re-read and a re-register;registerObjectExtensionanswerscrm__case, warns once, and withdrawing by the short name removes it;casebesidecrm__casekeeps its extension, with no warn.runtimemeta-object-extension-item-door-fit.test.ts, new case: a stored row respelled at rest toextend: 'case', thenloadMetaFromDb. The field lands oncrm__case, nocaseentry, one warn.pnpm --filter, head-only string count 0 in dist): 1 failed | 5 passed, with acasecontributor present. On head (rebuilt, count 2): 6/6.ablation-replace.mjsgave both objectql pins red (2 failed | 2 passed). Restore:ok restored: blob == HEAD,git diff HEADempty.Suites, typecheck, lint, gates
Head
6db3a09f, under the verify lock:pnpm typecheckin full (tsc plus check:test-typecheck) exits 0 for objectql and runtime, and both ledgers hold. eslint--no-inline-configon the 3 touched files: exit 0.Gates:
dispatch-gates.mjs --commandsderived 63, all run, every exit 0.--ran: 63 derived, 63 run, 0 NOT-MEASURED.Dogfood battery: not owed (no
!, no narrowing arm).🤖 Generated with Claude Code
https://claude.ai/code/session_01FEkQ5oTRCUNQ4EA3YV84sZ