Repository navigation
refactor(metadata-protocol): move the record reads out of protocol.ts into protocol-record-reads.ts (#25467 cut 4) - #25589
Conversation
… into protocol-record-reads.ts Move-only: the existence check, the OCC version checks, the read made as the calling principal, the cross-object search and the write-response strip move unedited into protocol-record-reads.ts (RecordReadHost); the class keeps each method's name, docblock and signature. probeRecord and getMetaItemCached stay. The row-serving door pin follows searchAll's admission call and exposure decision to the new file. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J8pUyVs5eas1AjMdReJM7C
📓 Docs Drift CheckThis PR changes 1 package(s): 45 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 15 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 1688342ee454b6741d3eaec4929d8f194777510e && git checkout 1688342ee454b6741d3eaec4929d8f194777510e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ba9e0c37492cd65abb19651829910959219d5e01 034fbb27f886284a5ccfa453f1ce5fe6084a5ecd && git checkout -B drift-repro ba9e0c37492cd65abb19651829910959219d5e01 && git merge --no-ff 034fbb27f886284a5ccfa453f1ce5fe6084a5ecd
node scripts/docs-audit/affected-docs.mjs --json ba9e0c37492cd65abb19651829910959219d5e01
|
|
os-dev-report |
席位核对 · ACCEPT · 2026-10-24T03:08Z
Generated by Claude Code |
Requested by Sales · project thread
Part of #25467
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6270097840
Cut 4 of the
protocol.tssplit (#24586 ruling A, recorded in6270097840). Cut 1 landed as #25494, cut 2 as #25518 and cut 3 as #25538. The claim is6376061670. The seat agreed scope C and its five conditions before the claim, and agreed the narrowing below after it.Six
ObjectStackProtocolImplementationmethods, the record reads, move unedited into a new module,packages/metadata-protocol/src/protocol-record-reads.ts, together with the four declarations of the OCC version-token reading: 568 lines, 142 of them from the last 30 days (measured at798d480269; the lines are unchanged on this PR's base).It is move-only, and the card stays open for the later cuts. Branch:
claude/project-thread-uasfag, the harness-pinned branch for this session, restarted fromorigin/mainat798d480269after cut 3 merged, and rebuilt once18e73483after #25550 and #25560 landed (the move re-run from scratch on that base, see below). The branch names no issue, so the claim comment is the identity record.Serial with the file's in-flight PRs. Measured just before this PR was opened, on main
ba9e0c3749. None of the 11 open PRs touchesprotocol.ts,protocol-record-reads.tsor the row-serving pin (each PR's diff read against its merge base).git merge-treeof this head with each of them, and with main, is clean. At claim, #25533 was the one open PR onprotocol.tsand was disjoint; it merged as35d942ed58. Beforepr_create, #25550 (#25234) added a line directly after the@objectstack/spec/contractsimport this cut narrows, andmerge-treeconflicted, so this cut yielded until it landed (b531016f6d), then was rebuilt once18e73483and re-measured in full.origin/mainhas not touched these files sincece18e73483.What moves
omitInternalWriteFields:14526: voidassertRecordExists:14600assertVersionOf:14662assertVersionMatch:14713readAsCaller:14731searchAll:14849–:15322: Promise<{…}>None is on the series' skip list of public members with inferred return types. Four are private. The two public ones declare their return type, so the published
.d.tsprints the declared type from the stub, unchanged.Narrowed from scope C during the trial, before the claim. Both members below stay in the class exactly as they are.
probeRecordstays. Its system-contextengine.findOneis one ofprotocol.ts's 6 sites inscripts/query-options-erasure-baseline.json. That baseline is keyed by file and only shrinks, so the site cannot change files unedited:query-options/no-any-erasurerefuses it in the new file. The moved bodies call it through the protocol, as a host closure. ItsisSystemsite therefore stays put too.getMetaItemCachedstays. The Three read-side/metaverbs (history, audit, references) never reachcanonicalizeMetaRequestType— they still derive their key from the MANIFEST map #9157 pin,protocol.read-verb-canonical-fold.test.ts, derives the/metarequest-boundary verbs from the method bodies inprotocol.tsitself, and requires each verb to callcanonicalizeMetaRequestType. With the body moved, the stub no longer folds and the pin goes red (measured). Teaching the pin to follow a stub into another module would be a change to the pin, not a move.simpleHashandmetadataItemNotFoundErrorstay with it, because only it reads them.canonicalizeMetaRequestTypeneeds noexport.The shape (#23050 shape B, as cuts 1–3 apply it)
asyncon the five async stubs. Each body is now one host literal plusfn.call(host, …), marked[#25467 cut 4].RecordReadHosttypes every member as the class member itself (ObjectStackProtocolImplementation['m']). Each moved function types itsthisas thePickof the host over exactly the members its body reads, measured by AST. Each stub annotates its literal asThisParameterType<typeof fn>. There is no cast, and no member becomes public or protected.engineandgetServicesRegistry.(...a) => this.m(...a)), so a spy or double set on the instance is still the one the body reaches:assertVersionOf,getMetaItems,probeRecordandreadAsCaller.this: Pick<RecordReadHost, …>omitInternalWriteFields'engine'assertRecordExists'probeRecord'assertVersionOf'readAsCaller'assertVersionMatch'assertVersionOf' | 'probeRecord'readAsCaller'engine'searchAll'engine' | 'getMetaItems' | 'getServicesRegistry'The caller-read path (seat condition 3).
readAsCallerandsearchAllmove token-identical: 59 and 1,976 tokens, 0 differ.Pickcarries an elevation member. The host has no system context, noisSystemreader and no privileged engine handle, only the protocol's ownenginefield (the one the method read before).searchAllstill asks the security service'scanReadObjectwithrequest.contextand the spec'scanServeApiOperation, in the same order, in the moved body.Module-level declarations, by reader
Each declaration the moved bodies read was checked by an AST walk over the whole of base
protocol.ts. The walk finds every identifier reference to the declaration outside its own declaration, attributes it to the enclosing class member or top-level statement, and takes the transitive closure over the moved set.Move with the bodies, byte-identical and unexported. Each has 0 readers outside the moved bodies and the other moved declarations. Each carries exactly its own docblock (leading-trivia blocks checked: one each).
assertVersionTokenNotMalformed:3176assertVersionOf,assertVersionMatchNormalisedVersion:3208normaliseVersionToken,versionTokensAgreenormaliseVersionToken:3300assertVersionOf,versionTokensAgreeversionTokensAgree:3327assertVersionOfNone is read while the module is first evaluated.
Stay in
protocol.tsand gainexportin place, the only edit to it, imported back by the module:canonicalVersionInstant(:3246)auditInstantMs(:3350)The exported classes
ConcurrentUpdateErrorandMalformedVersionTokenErrorare imported as values.src/index.tsexports from./protocol.jsby name, so nothing new is published (measured below).Imports.
protocol.tsdrops the names that lose every reader (noUnusedLocals):canServeApiOperationfrom@objectstack/spec/data,ISecurityServicefrom@objectstack/spec/contracts(theRLS_MEMBERSHIP_RESOLVER_SERVICEimport #25550 added on the next line is untouched), andresolveI18nLabelfrom@objectstack/spec/ui. The module imports them from the same specifiers.protocol.tsgains one statement, the import of the new module.The published
.d.tsUnder the series bar (ruling
6348891160), measured last, on the final layout, at basece18e73483and at head. Both were built withpnpm --filter @objectstack/metadata-protocol buildagainst the same dependency tree (turbo run build --filter='@objectstack/metadata-protocol^...'at base first).dist/index.d.tsanddist/index.d.cts:25da084d43455d125bfd41b85b1e70477ef7caf77f2cb7da23143b325e51a14eat base and at head, both files. That is raw identity, 770,690 bytes each, so there is no union swap this cut. (The value differs from cut 3's because feat(lint)!: membership key declarations, and the row policies that read them, are judged when authored #25550 and fix(metadata-protocol): a widget probe refused by read admission names the caller's read access, not a dataset edit #25560 changed the published surface on main, not this PR.)ff7d6892b9f1161da356208d51301816c48dc021051544b9c28a3aa14329d57e. The normaliser, run frompackages/metadata-protocol:dist/index.d.ts: making line 1361,private engine;, public (engine;) gives raw4bea7ae0…and normalisedbe016197…. Both differ from the real hashes.import()andrequireof the built bundles). The sorted list hashese47ac7c753bcb8bee1e79c1a5f79f7394cf030bd526de81ea8bcbfcb71fa024aon all four, as at cuts 1–3.canonicalVersionInstantdoes not appear.No changeset:
skip-changeset, per ruling6348891160.Proof of move-only
Bodies and declarations, by AST token (recursive
getChildrenleaves). For each moved function, the parameters (less the addedthis), return type and body have the same token sequence as the base method. Each moved declaration has the same tokens, and its own docblock is byte-identical. 2,574 tokens compared against basece18e73483, 0 differ. The module re-generated on that base is byte-identical to the one first cut on798d480269. Negative control: one changed token makes the check report a difference.Class side. The class has 299 members at base and at head. The 293 that did not move are byte-identical to base, and each of the 6 stubs keeps its docblock and signature byte-identical, less
async.Top level of
protocol.ts. 239 of the 247 base statements other than the class are byte-identical with their leading comments. The other 8 are:canonicalVersionInstant(exportadded);The one new statement is the import of the new module.
Size. +796 / −603 over 3 files, 1,399 changed lines.
protocol.tsgoes from 31,264 to 30,718 lines, andprotocol-record-reads.tshas 740.Readers of the moved lines
packages/core/src/security/row-serving-door-exposure.pin.test.ts, seat condition 1). The pin classifies every caller of the read admissioncanReadObjectand checks each door's decision site.searchAll'scanReadObjectcall and itscanServeApiOperationdecision both move. Without a migration the pin fails 3 of its tests: an unclassified caller, a stale key, and a decision site that no longer asks.CALLERSkey anddecisionSitemove toprotocol-record-reads.ts. Its door, operation and behaviour pin (protocol.search-api-exposure.test.ts) are unchanged.protocol.ts: the call matcher no longer fires there, so keepingprotocol.tsas a key would make the pin's stale-entry check fail.second-object-read-exposure.pin(7/7; no path of that one is touched this cut).check-query-options-erasure-ratchetgives identical output at both ends: 50 unswept non-test sites in 15 files, none new, and no baseline file key added. The engine reads that move (readAsCaller'sfindOne,searchAll'sfind) are not counted sites. The counted one stays withprobeRecord.isSystemread. No file-count move.check-tenant-audit-census: identical output at both ends, no--write.docs/qa/platform-checklist/areas/*.jsonanchorprotocol.ts#searchAlland other moved methods. The method shells stay, so the anchors still resolve; they are not edited.check-adr-symbol-anchorsandcheck-platform-checklist: identical output at both ends. The blanking check before scope C found no anchor that resolves only inside the moved bodies (seat condition 4).object.jsoncitesprotocol.ts#searchAllforenable.searchable. Its key-mention check still passes, on the docblock that stays.check-livenessgives identical output at both ends. The file is not edited.check:adr-anchors: 84 → 84 anchored files. Decision-letter citations on the anchored surface go 1,947 → 1,942 atce18e73483→ head, measured exactly at both ends (1,954 → 1,949 at798d480269before the rebuild: the same 5).searchAll's moved body:ADR-0110 D3×2,ADR-0079 D3,ADR-0131 D6andADR-0090 D2.protocol.ts's anchor entry governs ADR-0005/0029/0087/0119/0126. None of its paragraphs describes these bodies (read paragraph by paragraph), so the new module gets no entry and those citations leave the anchored surface unedited.PRODUCERS). Nonew Error(site moves:protocol.tsis 87 → 87 and the new module has 0, so no row is added../protocol.tsall pass at head, includingread-verb-canonical-fold(the reasongetMetaItemCachedstays, above).Verification
tsc --noEmit(package) exit 0.eslintonprotocol.tsandprotocol-record-reads.tsexit 0.packages/metadata-protocolsuite, base and head: 21,489 tests each (21,470 passed, 0 failed, 19 skipped), 266 files. Compared per test name and status: 0 differences.protocol.tsby path, run at head, all pass:corerow-serving-door-exposure.pinandsecond-object-read-exposure.pin(7);objectqlcore-boundary.ratchet(2);specmetadata-type-registry-allow-org-override-absenceandscripts/liveness/evidence(44);clientandrestdiscovery-double-retired-key(3 + 2).check-adr-anchors,check-adr-symbol-anchors,check-platform-checklist,check-system-context-census,check-tenant-audit-census,check-query-options-erasure-ratchet, and speccheck-liveness.dispatch-gates --commands --changedderives 61 gates on the final tree (3 changed paths). All 61 exit 0 at head, and--ranreconciles 61 of 61 with 0 NOT-MEASURED.check-adr-symbol-anchorsandcheck-livenessare not derived and were run by hand (above). The full list and the--ranreconcile are in the report on this PR.Carry-forward (unchanged)
A public member whose return type is inferred stays in
protocol.tsin this series; annotating its type would be a signature edit and needs a ruling. Later cuts skipgetDiscovery,getMetaTypes,getMetaItems,getMetaItemsForExecution,getMetaItem,getMetaItemForExecution,getUiView,findData,getData,validateData,createData,cloneData,updateData,deleteDataandsaveMetaItem. Also staying, from this cut, areprobeRecord(per-file erasure-baseline site) andgetMetaItemCached(read by the #9157 pin by path). The inferred-return.d.tsstill depends onprotocol.tsbyte offsets (cut 3's series note), so each cut measures it last.🤖 Generated with Claude Code
https://claude.ai/code/session_01J8pUyVs5eas1AjMdReJM7C