Repository navigation
refactor(metadata-protocol): move the artifact registry's lookups out of protocol.ts into protocol-artifact-lookup.ts (#25467 cut 15) - #26104
Conversation
… of protocol.ts (#25467 cut 15) Move-only. The bodies of lookupArtifactItem, artifactLockLayerAt, shippedArtifactsOf and shippingPackagesOf move into the new protocol-artifact-lookup.ts (host shape B: a live engine getter, and protocol-bound closures for lookupArtifactItem, shippingPackagesOf and shippedArtifactsOf, so the moved bodies reach each other through the class methods). All four members keep their name, docblock and signature in the class as stubs that delegate. resolveArtifactLockLayer leaves protocol.ts' import with its last reader. Nothing re-exports the new module, so the published surface does not move. The #22024 call-site pin scans the new module beside protocol.ts: the lookup's body and shippedArtifactsOf's two calls of it are recorded there, the stub's host closure is recorded as a new site, and a non-vacuity case checks that the seam module's sites are found. Claude-Session: https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 17 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 11 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 97c4e61fd69154cf52f4774bab4aac89304ca25c && git checkout 97c4e61fd69154cf52f4774bab4aac89304ca25c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0a6948210cc96a95cc568b30193895e157968c01 9840a879dab62b2e1edc5d4070e199a79c4e7b22 && git checkout -B drift-repro 0a6948210cc96a95cc568b30193895e157968c01 && git merge --no-ff 9840a879dab62b2e1edc5d4070e199a79c4e7b22
node scripts/docs-audit/affected-docs.mjs --json 0a6948210cc96a95cc568b30193895e157968c01
|
席位核对 · ACCEPT ·
|
| item | reading |
|---|---|
| Shape | draft, base main, Part of #25467 (cut 15), skip-changeset (no published behaviour moves), mergeable clean. |
| Files | 3 files, +231/−87: lookupArtifactItem, artifactLockLayerAt, shippedArtifactsOf and shippingPackagesOf move into protocol-artifact-lookup.ts. The private members stay as one-line stubs, so the call sites and instance spies inside protocol.ts are unchanged. The host is an engine getter plus closures. Not governed. |
| Fix | a pure move. Seat probe: all 70 removed protocol.ts lines (less the dropped resolveArtifactLockLayer import) reappear verbatim in the seam module, with 0 missing. Dev: AST compare 795 tokens, 0 differ (negative control 1); .d.ts raw identical at both ends (positive control moves it); export tables equal. |
| Pin | #22024 protocol.lookup-artifact-item-call-sites.test.ts now scans the seam file. shippedArtifactsOf's two rows move unchanged; new rows for the seam definition and the stub's closure (disposition no); a found-equals-rows case for the seam. Both on-disk controls go red (2/5 and 2/4). The pin-free fallback was not needed. |
| Evidence | 36 check runs success and 16 expected skips on this head, 0 failed. Suites 0 lost, 0 changed, 1 added; 59/59 derived gates exit 0, --ran reconciles. |
| Carried | the cut 16 proposal in the report (readActiveOverlayRows, storedOverlayEntries, namesWithOwnStoredRow, readItemActiveRows; no by-path pin; the durability --list is re-measured) follows from the same thread once this lands. |
| Landing | 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay now that every check on this head is green or an expected skip. |
Generated by Claude Code
Requested by Leehom · project thread
Part of #25467
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6270097840
Cut 15 of the
protocol.tssplit (#24586 ruling A, recorded in6270097840). Cuts 1–14 landed as #25494, #25518, #25538, #25589, #25601, #25623, #25642, #25719, #25731, #25740, #25797, #25939, #25972 and #26014. The claim is6414543503; the scope is the cut 15 proposal of the cut 14 report (6412300878), as the server seat approved it in its dispatch order: the whole family, with the #22024 pin widened (the pin edit held, so the pin-free fallback was not needed).Four
ObjectStackProtocolImplementationmembers move unedited into a new module,packages/metadata-protocol/src/protocol-artifact-lookup.ts(#23050 shape B). They are the artifact registry's lookups from the ADR-0010 section:lookupArtifactItem: an item's artifact.shippedArtifactsOf: every package's artifact for an item.shippingPackagesOf: the packages that can ship an item of a type.artifactLockLayerAt: the item-lock resolution's artifact layer.protocol.tsloses 48 net lines (29,488 to 29,440). The cut is move-only, and the card stays open for later cuts.Branch
claude/project-thread-ybjm3his the harness-pinned branch for this thread, restarted frommain3117289002after cut 14 merged. It names no issue, so the claim comment is the identity record. It holds the commitdd85dd1b4(the move and the pin edit together: the move alone turns the pin red, as the control below shows) and two merges ofmain:be7897799(59e0b7e0f) andaa2f8e31f(9840a879d). Neither touchespackages/metadata-protocol/: the package'ssrc/tree isaf425909at3117289002and at both mergedmainheads.Serial with the file's in-flight PRs. Measured just before this PR was opened: of the 39 open PRs, one touches
packages/metadata-protocol/src/. #26005 editsprotocol.meta-types-degenerate-derivation.test.ts, which does not overlap this cut. No open PR editsprotocol.ts, the new module or the #22024 pin.What moves
lookupArtifactItem:16493artifactLockLayerAt:16533shippedArtifactsOf:16558shippingPackagesOf:1659480 body lines (the proposal said about 80).
The shape (#23050 shape B, as cuts 1–14 apply it)
.call, marked[#25467 cut 15].ArtifactLookupHosttypes every member as the class member itself (an indexed access onObjectStackProtocolImplementation). Each moved function types itsthisas thePickof the host over exactly the members its body reads. Each stub annotates its literal asThisParameterTypeoftypeof fn. No member changes visibility.this, withascasts, parentheses and!stripped; barethischecked too):lookupArtifactItemandshippingPackagesOfreadengineonly, through a live getter.shippedArtifactsOfreadslookupArtifactItemandshippingPackagesOf, andartifactLockLayerAtreadsshippedArtifactsOf; each goes as a closure.lookupArtifactItem) is still the oneshippedArtifactsOfreaches, as before the move.ObjectStackProtocolImplementationidentifier and none onthis.constructor, in any of the four bodies. 0new Error((and no other error constructor), so noPRODUCERSrow.lookupArtifactItem(sites inprotocol.ts(the finding(metadata-protocol): getMetaItem naming no package grafts the first-registered package's artifact envelope onto another package's served view expansion, so a no-package by-name read can carry the wrong _packageId #22024 table),getEffectiveLock, the read doors and the list forartifactLockLayerAt, andreadFlattenedMetaItemsand the others forshippingPackagesOf.Module-level declarations, by reader
export, none moves. The bodies readPLURAL_TO_SINGULAR(from@objectstack/spec/shared),resolveArtifactLockLayerand the typeItemAddress(from./item-lock.js), which the new module imports from whereprotocol.tsimports them, andShippingPackages,protocol.ts's own exported type, imported as a type. The module's only import from./protocol.jsis a type import, so it adds no runtime cycle.protocol.ts.resolveArtifactLockLayerloses its last reader and leaves the./item-lock.jsimport (its two{@link}mentions in docblocks stay as prose). The one new statement is the import of the new module.The #22024 call-site pin, widened
protocol.lookup-artifact-item-call-sites.test.tsrecords a disposition for everylookupArtifactItem(in the source. The move takes three of them into the new module (the function the class member delegates to, andshippedArtifactsOf's two calls) and adds one inprotocol.ts(the stub's host closure,lookupArtifactItem: (...a) => this.lookupArtifactItem(...a)). The edit:protocol-artifact-lookup.tsbesideprotocol.ts. It scans the seam with a module-function header (export [async] function NAME(), and keys each site by file as well as member and argument text.shippedArtifactsOf's two rows move to the seam file, unchanged: same arguments, same disposition, same reason.(...a): dispositionno, recorded as passing no package because it forwards the package-less call too, and its disposition is the two forwarded calls'.Controls (each on disk, restored from
HEADunder a trap, hash equal toHEAD's after):this.lookupArtifactItem(type, name, packageId)was changed tothis.lookupArtifactItem(type, name)in the module. On-disk counts read first: 0 scoped calls, 2 package-less. Result: 2 failed, 3 passed:expected 18 to be 19.With the widened pin, 5/5 pass.
The published
.d.tsThe series bar is ruling
6348891160, sorted-union identity, measured first. Base3117289002and headdd85dd1b4were built withpnpm --filter @objectstack/metadata-protocol buildagainst one dependency tree. The closure was built at head by a fullpnpm build(119/119). For the base build, the package'ssrc/was swapped tree-only to base and then restored fromHEADunder a trap;git diff HEADwas empty,git statusclean, and all three touched blobs equal toHEAD's after. Measured twice: on the pre-merge tree (base3117289002, headdd85dd1b4) and after mergingmain(head59e0b7e0f, closure rebuilt by a fullpnpm build, 119/119; the later merge9840a879dchanged only CI workflow files and scripts underui/andscripts/, no package source). The readings below hold for both.08914e65f936a2b031c3f3f215481a97f53d132eefd869e4f42d928947ebe173at base and at head, on bothdist/index.d.tsanddist/index.d.cts, 775,874 bytes each. Raw identity holds, so sorted-union identity holds too. All four members stayprivate NAME;lines.46ccd7e5c4db673652513d1801ec9c9f06bf34a0a7fb6ba97fb2531ef0cc544bat both ends (80 string-literal union runs sorted; the cut 12 normaliser).index.jshashes13ff6c73088affd6…at base and5671669a82bc3837…at head; each of the four functions'.call(hostsites occurs 0 times in the base bundles and once in the head bundles (ESM and CJS alike).privatewas dropped fromlookupArtifactItem(on disk, restored fromHEADunder a trap) and the package rebuilt. Both.d.tsfiles move to4290e8428fa3c8f5…and printlookupArtifactItem(type: string, name: string, currentPackageId?: string, member?: string): unknown;, so the bar can see a change on these members.9f18a0177ac736ed…on all four. No moved function, the host type orresolveArtifactLockLayerappears.No changeset:
skip-changeset, per ruling6348891160.Proof of move-only
Bodies, by AST token (the TypeScript compiler's AST leaf tokens, JSDoc skipped, over parameters less the added
this, return type and body), baseprotocol.tsat3117289002against the head module:Negative control, one token changed in an in-memory copy of the head module (
id !== 'sys_metadata'toid === 'sys_metadata', one occurrence):protocol.ts. 242 of the 243 base statements are byte-identical, leading comments included. The other one is the./item-lock.jsimport, which losesresolveArtifactLockLayer. Head adds one statement, the new module's import.protocol-artifact-lookup.tshas 158 lines; the pin edit is +50/−16.Readers of the moved lines
Every test and script that reads
protocol.tsby path, every test file inpackages/andui/, anddocs/,scripts/andcontent/were grepped for the four member names.protocol.register-item-call-sites.test.tsnamesshippedArtifactsOfonly inside a docblock; its by-path regex is onanotherPackageShips, which stays. The other by-path readers ofprotocol.ts(protocol.hydrate-overlay-canonical-type,protocol.read-verb-canonical-fold,protocol.object-registry-write-through-spelling,protocol.recovery-doors-emit-mutation,protocol.recovery-doors-mutation-projector) do not read these bodies; all ran in the package suite and pass at both ends.protocol.lock-gate-canonical-type.test.tsdrives the lookup through the gate (behaviour).protocol.declines-stored-row-publishedandprotocol.i18n-bundle-list-mergename it in comments. All pass at both ends.protocol-registry-shadow,protocol-boot-hydration-scopedandenvironment-overlay-email-locale-restart(comments; behaviour through the class): 3 files, 61/61.meta-published-overlay: 16/16.meta-published-runtime-publish: 12/12.PermissionMatrixEditor.artifactTier.test.tsxandResourceEditPage.resetVerdict.test.tsxname it in comments only.docs/,scripts/orcontent/;docs/qa/cites none of the four.check-adr-anchorsexits 0 at both ends: 88 anchored files, citations 59,047 → 59,048 (the new module's header cites ADR-0010), files 8,289 → 8,290, decision-letter citations 1,864 at both ends. The moved comments citeADR-0010 §3.3,ADR-0048andADR-0029 D3. TheD3one sits inshippedArtifactsOf's docblock, which stays inprotocol.ts, so no decision-letter citation leaves the anchored file.check-durability-degradation-log-levelexits 0 at both ends with identical output, and its--listis identical once[@:][0-9]+is stripped (159 lines).shippingPackagesOf'stry { … } catch { return []; }around a registry listing is not on the list (it reads no store).check-engine-double-contract): exits 0 at both ends. TheupdateData/deleteDataseams keep their entries at new line numbers (14086 → 14087, 14111 → 14112).productionFilesis 3,718 at base and 3,720 at head: +1 is the new file, +1 is the gitignoredpackages/spec/src/migrations/registry.tsthe spec build writes in the head tree.check-adr-symbol-anchors,check-system-context-census,check-tenant-audit-census,check-query-options-erasure-ratchet,check-filter-alias-parity.check-nul-bytesdiffers only in the scanned file count, +1. The base-end readings were taken in a clean detached worktree at3117289002.check-governed-merges --branch claude/project-thread-ybjm3h: 0 of 3 paths hit the register, NOT governed; 318 changed lines, under the 3,000-line human-merge bar.Verification
tsc --noEmitinpackages/metadata-protocolexits 0 before and after the merge (after the full build; the first run, on a stale dependency tree, failed only inprotocol-package-install*.tson@objectstack/metadata-coreexports that the build then supplied).pnpm exec eslint --no-inline-config packages/metadata-protocol(the whole touched package; it has nolintscript, these are the rootlintscript's flags) exits 0, no output.packages/metadata-protocolsuite, base and head, compared per test name and status (vitest--maxWorkers=2, JSON reporter, each run under the shared verify lock,VERDICT command-exit 0both legs), on the pre-merge tree (the merge changed no file of the package):3117289002: 21,557 tests (21,538 passed, 0 failed, 19 skipped), 269 files.dd85dd1b4: 21,558 tests (21,539 passed, 0 failed, 19 skipped), 269 files.src/swapped tree-only from3117289002(the new module removed, the pin at its base blob) and restored fromHEADunder a trap.check-platform-checklist --changed-from origin/main(merged tree): OK, 1 anchored file in the change set, 29 anchors judged, 0 lost.dispatch-gates --commands --repo objectstack-ai/objectstackderives 59 gates on 3 changed paths from the merged tree9840a879d, with no stale-tree warning (the first derivation, at59e0b7e0f, flaggedci.ymlandcheck-closing-keyword-parity.mjsas changed onmain, somainwas merged once more before running). All 59 exit 0 after the fullpnpm build.dispatch-gates --ran: 59 derived, 59 run, 0 NOT-MEASURED, 0 UNRUN, exit 0.Acceptance notes
lookupArtifactItemis the family's hot member (18 class call sites, some in list loops). Its stub now builds a one-getter host literal per call, as every shape-B stub in this series does. No behaviour or result changes; this is noted only because the lookup runs per listed item.shippedArtifactsOfandartifactLockLayerAtcall their siblings through host closures on the protocol, not through the module's functions. That keeps instance spies and doubles effective, as above. A later redesign that wants the module to stand alone would be a behaviour change for such doubles, not a move.Carry-forward
Unchanged from cut 14: these stay in
protocol.ts:probeRecord,getMetaItemCached,findReferencesToMeta,packagelessAddress,refuseUnmintableMetaType,metaTypeNamespaceExists,viewContainerNameCollisionRefusalwithoverlaidShippedContainerViewNames,reportPageRequiresAbsentAtLoad, the 7applyRegistryWriteThroughcallers, andreadCodeLayerForCarryForward.refusePackagedBaseRemoval,packagedBaseRefusal,runPublishSideEffects,promoteDraftForPublish,publishPackageDraftsanddiscardPackageDrafts.warnClosureReadUnavailable.🤖 Generated with Claude Code
https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6